mirror of
https://github.com/lubosz/overte.git
synced 2025-04-12 18:52:33 +02:00
Merge branch 'master' into plugins
Conflicts: interface/src/Application.cpp interface/src/Camera.cpp interface/src/Camera.h interface/src/ui/ApplicationOverlay.cpp interface/src/ui/ApplicationOverlay.h libraries/render-utils/src/GlowEffect.cpp libraries/render-utils/src/GlowEffect.h libraries/render-utils/src/TextureCache.cpp
This commit is contained in:
commit
031b294ae5
285 changed files with 12694 additions and 3956 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -4,6 +4,7 @@ CMakeFiles/
|
|||
CMakeScripts/
|
||||
cmake_install.cmake
|
||||
build*/
|
||||
ext/
|
||||
Makefile
|
||||
*.user
|
||||
|
||||
|
@ -32,10 +33,6 @@ DerivedData
|
|||
interface/external/*/*
|
||||
!interface/external/*/readme.txt
|
||||
|
||||
# ignore interface optional resources
|
||||
interface/resources/visage/*
|
||||
!interface/resources/visage/tracker.cfg
|
||||
|
||||
# Ignore interfaceCache for Linux users
|
||||
interface/interfaceCache/
|
||||
|
||||
|
@ -46,4 +43,6 @@ libraries/audio-client/external/*/*
|
|||
gvr-interface/assets/oculussig*
|
||||
gvr-interface/libs/*
|
||||
|
||||
TAGS
|
||||
# ignore files for various dev environments
|
||||
TAGS
|
||||
*.swp
|
||||
|
|
18
BUILD.md
18
BUILD.md
|
@ -1,9 +1,9 @@
|
|||
###Dependencies
|
||||
|
||||
* [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.12.2
|
||||
* [Qt](http://qt-project.org/downloads) ~> 5.3.2
|
||||
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1g
|
||||
* IMPORTANT: OpenSSL 1.0.1g is critical to avoid a security vulnerability.
|
||||
* [Qt](http://www.qt.io/download-open-source) ~> 5.4.1
|
||||
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1m
|
||||
* IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities.
|
||||
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
|
||||
|
||||
####CMake External Project Dependencies
|
||||
|
@ -19,9 +19,9 @@ The following external projects are optional dependencies. You can indicate to C
|
|||
* [SDL2](https://www.libsdl.org/download-2.0.php) ~> 2.0.3
|
||||
* Enables game controller support in Interface
|
||||
|
||||
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` directory in each of the subfolders for each external project.
|
||||
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.
|
||||
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 -DGET_$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.
|
||||
|
||||
|
@ -37,12 +37,12 @@ Hifi uses CMake to generate build files and project files for your platform.
|
|||
####Qt
|
||||
In order for CMake to find the Qt5 find modules, you will need to set an ENV variable pointing to your Qt installation.
|
||||
|
||||
For example, a Qt5 5.3.2 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).
|
||||
For example, a Qt5 5.4.1 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).
|
||||
|
||||
The path it needs to be set to will depend on where and how Qt5 was installed. e.g.
|
||||
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.3.2/clang_64/lib/cmake/
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.3.2/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/clang_64/lib/cmake/
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.4.1/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
|
||||
|
||||
####Generating build files
|
||||
|
@ -57,7 +57,7 @@ Any variables that need to be set for CMake to find dependencies can be set as E
|
|||
|
||||
For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation:
|
||||
|
||||
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.3.2/lib/cmake
|
||||
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/lib/cmake
|
||||
|
||||
####Finding Dependencies
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies
|
|||
|
||||
We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. In the code block above qt5 is installed from a formula in this repository.
|
||||
|
||||
*Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.3.x stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.*
|
||||
*Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.4.x stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.*
|
||||
|
||||
###Xcode
|
||||
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.
|
||||
|
|
|
@ -29,12 +29,12 @@ NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit
|
|||
|
||||
* [Download the online installer](http://qt-project.org/downloads)
|
||||
* When it asks you to select components, ONLY select the following:
|
||||
* Qt > Qt 5.3.2 > **msvc2013 32-bit OpenGL**
|
||||
* Qt > Qt 5.4.1 > **msvc2013 32-bit OpenGL**
|
||||
|
||||
* [Download the offline installer](http://download.qt-project.org/official_releases/qt/5.3/5.3.2/qt-opensource-windows-x86-msvc2013_opengl-5.3.2.exe)
|
||||
* [Download the offline installer](http://download.qt.io/official_releases/qt/5.4/5.4.1/qt-opensource-windows-x86-msvc2013_opengl-5.4.1.exe)
|
||||
|
||||
Once Qt is installed, you need to manually configure the following:
|
||||
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.3.2\msvc2013_opengl\lib\cmake` directory.
|
||||
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.4.1\msvc2013_opengl\lib\cmake` directory.
|
||||
* You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New
|
||||
|
||||
###External Libraries
|
||||
|
@ -71,7 +71,7 @@ Your system may already have several versions of the OpenSSL DLL's (ssleay32.dll
|
|||
|
||||
To prevent these problems, install OpenSSL yourself. Download the following binary packages [from this website](http://slproweb.com/products/Win32OpenSSL.html):
|
||||
* Visual C++ 2008 Redistributables
|
||||
* Win32 OpenSSL v1.0.1L
|
||||
* Win32 OpenSSL v1.0.1m
|
||||
|
||||
Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version.
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ if (WIN32)
|
|||
elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
|
||||
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fno-strict-aliasing -ggdb")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter -ggdb")
|
||||
endif(WIN32)
|
||||
|
||||
if (NOT MSVC12)
|
||||
|
@ -92,8 +92,17 @@ else ()
|
|||
if (NOT QT_CMAKE_PREFIX_PATH)
|
||||
set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH})
|
||||
endif ()
|
||||
if (NOT QT_CMAKE_PREFIX_PATH)
|
||||
get_filename_component(QT_CMAKE_PREFIX_PATH "${Qt5_DIR}/.." REALPATH)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
if (NOT EXISTS ${QT_CMAKE_PREFIX_PATH})
|
||||
message(FATAL_ERROR "Could not determine QT_CMAKE_PREFIX_PATH.")
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
# figure out where the qt dir is
|
||||
get_filename_component(QT_DIR "${QT_CMAKE_PREFIX_PATH}/../../" ABSOLUTE)
|
||||
|
||||
|
@ -168,6 +177,7 @@ option(GET_SOXR "Get Soxr library automatically as external project" 1)
|
|||
option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1)
|
||||
option(GET_LIBOVR "Get LibOVR library automatically as external project" 1)
|
||||
option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
|
||||
option(GET_VHACD "Get V-HACD library automatically as external project" 1)
|
||||
|
||||
if (WIN32)
|
||||
option(GET_GLEW "Get GLEW library automatically as external project" 1)
|
||||
|
|
|
@ -549,14 +549,18 @@ void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const Hif
|
|||
|
||||
nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
|
||||
} else if (mixerPacketType == PacketTypeMuteEnvironment) {
|
||||
QByteArray packet = receivedPacket;
|
||||
populatePacketHeader(packet, PacketTypeMuteEnvironment);
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node){
|
||||
if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != nodeList->sendingNodeForPacket(receivedPacket)) {
|
||||
nodeList->writeDatagram(packet, packet.size(), node);
|
||||
}
|
||||
});
|
||||
SharedNodePointer sendingNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
if (sendingNode->getCanAdjustLocks()) {
|
||||
QByteArray packet = receivedPacket;
|
||||
populatePacketHeader(packet, PacketTypeMuteEnvironment);
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node){
|
||||
if (node->getType() == NodeType::Agent && node->getActiveSocket() &&
|
||||
node->getLinkedData() && node != sendingNode) {
|
||||
nodeList->writeDatagram(packet, packet.size(), node);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// let processNodeData handle it.
|
||||
nodeList->processNodeData(senderSockAddr, receivedPacket);
|
||||
|
|
31
cmake/externals/vhacd/CMakeLists.txt
vendored
Normal file
31
cmake/externals/vhacd/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
set(EXTERNAL_NAME vhacd)
|
||||
|
||||
if (ANDROID)
|
||||
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
|
||||
endif ()
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/v-hacd-master.zip
|
||||
URL_MD5 3bfc94f8dd3dfbfe8f4dc088f4820b3e
|
||||
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/VHACD_LIB.lib CACHE FILEPATH "Path to V-HACD debug library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/VHACD_LIB.lib CACHE FILEPATH "Path to V-HACD release library")
|
||||
else ()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to V-HACD debug library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libVHACD.a CACHE FILEPATH "Path to V-HACD release library")
|
||||
endif ()
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to V-HACD include directory")
|
40
cmake/macros/AddResourcesToOSXBundle.cmake
Normal file
40
cmake/macros/AddResourcesToOSXBundle.cmake
Normal file
|
@ -0,0 +1,40 @@
|
|||
#
|
||||
# AddResourcesToOSXBundle.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Created by Stephen Birarda on 04/27/15.
|
||||
# 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
|
||||
#
|
||||
|
||||
macro(_RECURSIVELY_SET_PACKAGE_LOCATION _PATH)
|
||||
# enumerate the items
|
||||
foreach(_ITEM ${ARGN})
|
||||
|
||||
if (IS_DIRECTORY "${_ITEM}")
|
||||
# recurse into the directory and check for more resources
|
||||
file(GLOB _DIR_CONTENTS "${_ITEM}/*")
|
||||
|
||||
# figure out the path for this directory and then call ourselves to recurse into it
|
||||
get_filename_component(_ITEM_PATH ${_ITEM} NAME)
|
||||
_recursively_set_package_location("${_PATH}/${_ITEM_PATH}" ${_DIR_CONTENTS})
|
||||
else ()
|
||||
# add any files in the root to the resources folder directly
|
||||
SET_SOURCE_FILES_PROPERTIES(${_ITEM} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources${_PATH}")
|
||||
list(APPEND DISCOVERED_RESOURCES ${_ITEM})
|
||||
endif ()
|
||||
|
||||
endforeach()
|
||||
endmacro(_RECURSIVELY_SET_PACKAGE_LOCATION _PATH)
|
||||
|
||||
macro(ADD_RESOURCES_TO_OS_X_BUNDLE _RSRC_FOLDER)
|
||||
|
||||
# GLOB the resource directory
|
||||
file(GLOB _ROOT_ITEMS "${_RSRC_FOLDER}/*")
|
||||
|
||||
# recursively enumerate from the root items
|
||||
_recursively_set_package_location("" ${_ROOT_ITEMS})
|
||||
|
||||
endmacro(ADD_RESOURCES_TO_OS_X_BUNDLE _RSRC_FOLDER)
|
|
@ -22,11 +22,11 @@ macro(SETUP_EXTERNALS_BINARY_DIR)
|
|||
endif ()
|
||||
endif ()
|
||||
|
||||
set(EXTERNALS_BINARY_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/build-ext")
|
||||
set(EXTERNALS_BINARY_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/ext")
|
||||
if (ANDROID)
|
||||
set(EXTERNALS_BINARY_DIR "${EXTERNALS_BINARY_ROOT_DIR}/android/${CMAKE_GENERATOR_FOLDER_NAME}")
|
||||
else ()
|
||||
set(EXTERNALS_BINARY_DIR "${EXTERNALS_BINARY_ROOT_DIR}/${CMAKE_GENERATOR_FOLDER_NAME}")
|
||||
endif ()
|
||||
|
||||
endmacro()
|
||||
endmacro()
|
||||
|
|
|
@ -19,40 +19,16 @@
|
|||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("vhacd")
|
||||
|
||||
macro(_FIND_VHACD_LIBRARY _var)
|
||||
set(_${_var}_NAMES ${ARGN})
|
||||
find_library(${_var}_LIBRARY_RELEASE
|
||||
NAMES ${_${_var}_NAMES}
|
||||
HINTS
|
||||
${VHACD_SEARCH_DIRS}
|
||||
$ENV{VHACD_ROOT_DIR}
|
||||
PATH_SUFFIXES lib lib/Release
|
||||
)
|
||||
|
||||
find_library(${_var}_LIBRARY_DEBUG
|
||||
NAMES ${_${_var}_NAMES}
|
||||
HINTS
|
||||
${VHACD_SEARCH_DIRS}
|
||||
$ENV{VHACD_ROOT_DIR}
|
||||
PATH_SUFFIXES lib lib/Debug
|
||||
)
|
||||
|
||||
select_library_configurations(${_var})
|
||||
|
||||
mark_as_advanced(${_var}_LIBRARY)
|
||||
mark_as_advanced(${_var}_LIBRARY)
|
||||
endmacro()
|
||||
|
||||
find_path(VHACD_INCLUDE_DIRS VHACD.h PATH_SUFFIXES include HINTS ${VHACD_SEARCH_DIRS})
|
||||
|
||||
find_path(VHACD_INCLUDE_DIRS VHACD.h PATH_SUFFIXES include HINTS ${VHACD_SEARCH_DIRS} $ENV{VHACD_ROOT_DIR})
|
||||
if(NOT WIN32)
|
||||
_FIND_VHACD_LIBRARY(VHACD libVHACD.a)
|
||||
else()
|
||||
_FIND_VHACD_LIBRARY(VHACD VHACD_LIB)
|
||||
endif()
|
||||
find_library(VHACD_LIBRARY_DEBUG NAMES VHACD VHACD_LIB PATH_SUFFIXES lib/Debug HINTS ${VHACD_SEARCH_DIRS})
|
||||
find_library(VHACD_LIBRARY_RELEASE NAMES VHACD VHACD_LIB PATH_SUFFIXES lib/Release lib HINTS ${VHACD_SEARCH_DIRS})
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(VHACD)
|
||||
|
||||
set(VHACD_LIBRARIES ${VHACD_LIBRARY})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(VHACD "Could NOT find VHACD, try to set the path to VHACD root folder in the system variable VHACD_ROOT_DIR or create a directory vhacd in HIFI_LIB_DIR and paste the necessary files there"
|
||||
VHACD_INCLUDE_DIRS VHACD_LIBRARIES)
|
||||
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
#
|
||||
# FindVisage.cmake
|
||||
#
|
||||
# Try to find the Visage controller library
|
||||
#
|
||||
# You must provide a VISAGE_ROOT_DIR which contains lib and include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# VISAGE_FOUND - system found Visage
|
||||
# VISAGE_INCLUDE_DIRS - the Visage include directory
|
||||
# VISAGE_LIBRARIES - Link this to use Visage
|
||||
#
|
||||
# Created on 2/11/2014 by Andrzej Kapolka
|
||||
# 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
|
||||
#
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("visage")
|
||||
|
||||
find_path(VISAGE_BASE_INCLUDE_DIR VisageTracker2.h PATH_SUFFIXES include HINTS ${VISAGE_SEARCH_DIRS})
|
||||
|
||||
if (APPLE)
|
||||
find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h HINTS /usr/include/libxml2 ${VISAGE_SEARCH_DIRS})
|
||||
find_path(VISAGE_OPENCV_INCLUDE_DIR cv.h PATH_SUFFIXES dependencies/OpenCV_MacOSX/include HINTS ${VISAGE_SEARCH_DIRS})
|
||||
find_path(VISAGE_OPENCV2_INCLUDE_DIR opencv.hpp PATH_SUFFIXES dependencies/OpenCV_MacOSX/include/opencv2 HINTS ${VISAGE_SEARCH_DIRS})
|
||||
|
||||
find_library(VISAGE_CORE_LIBRARY NAME vscore PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS})
|
||||
find_library(VISAGE_VISION_LIBRARY NAME vsvision PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS})
|
||||
find_library(VISAGE_OPENCV_LIBRARY NAME OpenCV PATH_SUFFIXES dependencies/OpenCV_MacOSX/lib HINTS ${VISAGE_SEARCH_DIRS})
|
||||
|
||||
find_library(CoreVideo CoreVideo)
|
||||
find_library(QTKit QTKit)
|
||||
find_library(IOKit IOKit)
|
||||
|
||||
elseif (WIN32)
|
||||
find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h PATH_SUFFIXES dependencies/libxml2/include HINTS ${VISAGE_SEARCH_DIRS})
|
||||
find_path(VISAGE_OPENCV_INCLUDE_DIR opencv/cv.h PATH_SUFFIXES dependencies/OpenCV/include HINTS ${VISAGE_SEARCH_DIRS})
|
||||
find_path(VISAGE_OPENCV2_INCLUDE_DIR cv.h PATH_SUFFIXES dependencies/OpenCV/include/opencv HINTS ${VISAGE_SEARCH_DIRS})
|
||||
|
||||
find_library(VISAGE_CORE_LIBRARY NAME vscore PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS})
|
||||
find_library(VISAGE_VISION_LIBRARY NAME vsvision PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS})
|
||||
find_library(VISAGE_OPENCV_LIBRARY NAME opencv_core243 PATH_SUFFIXES dependencies/OpenCV/lib HINTS ${VISAGE_SEARCH_DIRS})
|
||||
endif ()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
list(APPEND VISAGE_ARGS_LIST VISAGE_BASE_INCLUDE_DIR VISAGE_XML_INCLUDE_DIR
|
||||
VISAGE_OPENCV_INCLUDE_DIR VISAGE_OPENCV2_INCLUDE_DIR
|
||||
VISAGE_CORE_LIBRARY VISAGE_VISION_LIBRARY)
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND VISAGE_ARGS_LIST CoreVideo QTKit IOKit)
|
||||
endif ()
|
||||
|
||||
find_package_handle_standard_args(Visage DEFAULT_MSG ${VISAGE_ARGS_LIST})
|
||||
|
||||
set(VISAGE_INCLUDE_DIRS "${VISAGE_XML_INCLUDE_DIR}" "${VISAGE_OPENCV_INCLUDE_DIR}" "${VISAGE_OPENCV2_INCLUDE_DIR}" "${VISAGE_BASE_INCLUDE_DIR}")
|
||||
set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY}" "${VISAGE_VISION_LIBRARY}")
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND VISAGE_LIBRARIES "${CoreVideo}" "${QTKit}" "${IOKit}")
|
||||
endif ()
|
||||
|
||||
mark_as_advanced(VISAGE_INCLUDE_DIRS VISAGE_LIBRARIES)
|
|
@ -302,6 +302,9 @@ bool DomainServer::didSetupAccountManagerWithAccessToken() {
|
|||
<< "at keypath metaverse.access_token or in your ENV at key DOMAIN_SERVER_ACCESS_TOKEN";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Using access token from DOMAIN_SERVER_ACCESS_TOKEN in env. This overrides any access token present"
|
||||
<< " in the user or master config.";
|
||||
}
|
||||
|
||||
// give this access token to the AccountManager
|
||||
|
@ -501,7 +504,7 @@ void DomainServer::populateStaticScriptedAssignmentsFromSettings() {
|
|||
Assignment::AgentType,
|
||||
scriptPool);
|
||||
scriptAssignment->setPayload(scriptURL.toUtf8());
|
||||
|
||||
|
||||
// add it to static hash so we know we have to keep giving it back out
|
||||
addStaticAssignmentToAssignmentHash(scriptAssignment);
|
||||
}
|
||||
|
@ -672,6 +675,10 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID());
|
||||
nodeData->setWalletUUID(pendingAssigneeData->getWalletUUID());
|
||||
|
||||
// always allow assignment clients to create and destroy entities
|
||||
newNode->setCanAdjustLocks(true);
|
||||
newNode->setCanRez(true);
|
||||
|
||||
// now that we've pulled the wallet UUID and added the node to our list, delete the pending assignee data
|
||||
delete pendingAssigneeData;
|
||||
}
|
||||
|
@ -2116,10 +2123,10 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig
|
|||
Assignment* assignment = sharedAssignment->data();
|
||||
bool requestIsAllTypes = requestAssignment.getType() == Assignment::AllTypes;
|
||||
bool assignmentTypesMatch = assignment->getType() == requestAssignment.getType();
|
||||
bool nietherHasPool = assignment->getPool().isEmpty() && requestAssignment.getPool().isEmpty();
|
||||
bool neitherHasPool = assignment->getPool().isEmpty() && requestAssignment.getPool().isEmpty();
|
||||
bool assignmentPoolsMatch = assignment->getPool() == requestAssignment.getPool();
|
||||
|
||||
if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) {
|
||||
if ((requestIsAllTypes || assignmentTypesMatch) && (neitherHasPool || assignmentPoolsMatch)) {
|
||||
|
||||
// remove the assignment from the queue
|
||||
SharedAssignmentPointer deployableAssignment = _unfulfilledAssignments.takeAt(sharedAssignment
|
||||
|
|
|
@ -33,7 +33,7 @@ DomainServerWebSessionData::DomainServerWebSessionData(const QJsonObject& userOb
|
|||
}
|
||||
}
|
||||
|
||||
DomainServerWebSessionData::DomainServerWebSessionData(const DomainServerWebSessionData& otherSessionData) {
|
||||
DomainServerWebSessionData::DomainServerWebSessionData(const DomainServerWebSessionData& otherSessionData) : QObject() {
|
||||
_username = otherSessionData._username;
|
||||
_roles = otherSessionData._roles;
|
||||
}
|
||||
|
|
163
examples/acScripts/rain.js
Normal file
163
examples/acScripts/rain.js
Normal file
|
@ -0,0 +1,163 @@
|
|||
//
|
||||
// rain.js
|
||||
// examples
|
||||
//
|
||||
// Created by David Rowe on 9 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
|
||||
//
|
||||
|
||||
var RainSquall = function (properties) {
|
||||
var // Properties
|
||||
squallOrigin,
|
||||
squallRadius,
|
||||
dropsPerMinute = 60,
|
||||
dropSize = { x: 0.1, y: 0.1, z: 0.1 },
|
||||
dropFallSpeed = 1, // m/s
|
||||
dropLifetime = 60, // Seconds
|
||||
dropSpinMax = 0, // Maximum angular velocity per axis; deg/s
|
||||
debug = false, // Display origin circle; don't use running on Stack Manager
|
||||
// Other
|
||||
squallCircle,
|
||||
SQUALL_CIRCLE_COLOR = { red: 255, green: 0, blue: 0 },
|
||||
SQUALL_CIRCLE_ALPHA = 0.5,
|
||||
SQUALL_CIRCLE_ROTATION = Quat.fromPitchYawRollDegrees(90, 0, 0),
|
||||
raindropProperties,
|
||||
RAINDROP_MODEL_URL = "https://s3.amazonaws.com/hifi-public/ozan/Polyworld/Sets/sky/tetrahedron_v1-Faceted2.fbx",
|
||||
raindropTimer,
|
||||
raindrops = [], // HACK: Work around raindrops not always getting velocities
|
||||
raindropVelocities = [], // HACK: Work around raindrops not always getting velocities
|
||||
DEGREES_TO_RADIANS = Math.PI / 180;
|
||||
|
||||
function processProperties() {
|
||||
if (!properties.hasOwnProperty("origin")) {
|
||||
print("ERROR: Rain squall origin must be specified");
|
||||
return;
|
||||
}
|
||||
squallOrigin = properties.origin;
|
||||
|
||||
if (!properties.hasOwnProperty("radius")) {
|
||||
print("ERROR: Rain squall radius must be specified");
|
||||
return;
|
||||
}
|
||||
squallRadius = properties.radius;
|
||||
|
||||
if (properties.hasOwnProperty("dropsPerMinute")) {
|
||||
dropsPerMinute = properties.dropsPerMinute;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("dropSize")) {
|
||||
dropSize = properties.dropSize;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("dropFallSpeed")) {
|
||||
dropFallSpeed = properties.dropFallSpeed;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("dropLifetime")) {
|
||||
dropLifetime = properties.dropLifetime;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("dropSpinMax")) {
|
||||
dropSpinMax = properties.dropSpinMax;
|
||||
}
|
||||
|
||||
if (properties.hasOwnProperty("debug")) {
|
||||
debug = properties.debug;
|
||||
}
|
||||
|
||||
raindropProperties = {
|
||||
type: "Model",
|
||||
modelURL: RAINDROP_MODEL_URL,
|
||||
lifetime: dropLifetime,
|
||||
dimensions: dropSize,
|
||||
velocity: { x: 0, y: -dropFallSpeed, z: 0 },
|
||||
damping: 0,
|
||||
angularDamping: 0,
|
||||
ignoreForCollisions: true
|
||||
};
|
||||
}
|
||||
|
||||
function createRaindrop() {
|
||||
var angle,
|
||||
radius,
|
||||
offset,
|
||||
drop,
|
||||
spin = { x: 0, y: 0, z: 0 },
|
||||
maxSpinRadians = properties.dropSpinMax * DEGREES_TO_RADIANS,
|
||||
i;
|
||||
|
||||
// HACK: Work around rain drops not always getting velocities set at creation
|
||||
for (i = 0; i < raindrops.length; i += 1) {
|
||||
Entities.editEntity(raindrops[i], raindropVelocities[i]);
|
||||
}
|
||||
|
||||
angle = Math.random() * 360;
|
||||
radius = Math.random() * squallRadius;
|
||||
offset = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, angle, 0), { x: 0, y: -0.1, z: radius });
|
||||
raindropProperties.position = Vec3.sum(squallOrigin, offset);
|
||||
if (properties.dropSpinMax > 0) {
|
||||
spin = {
|
||||
x: Math.random() * maxSpinRadians,
|
||||
y: Math.random() * maxSpinRadians,
|
||||
z: Math.random() * maxSpinRadians
|
||||
};
|
||||
raindropProperties.angularVelocity = spin;
|
||||
}
|
||||
drop = Entities.addEntity(raindropProperties);
|
||||
|
||||
// HACK: Work around rain drops not always getting velocities set at creation
|
||||
raindrops.push(drop);
|
||||
raindropVelocities.push({
|
||||
velocity: raindropProperties.velocity,
|
||||
angularVelocity: spin
|
||||
});
|
||||
if (raindrops.length > 5) {
|
||||
raindrops.shift();
|
||||
raindropVelocities.shift();
|
||||
}
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
if (debug) {
|
||||
squallCircle = Overlays.addOverlay("circle3d", {
|
||||
size: { x: 2 * squallRadius, y: 2 * squallRadius },
|
||||
color: SQUALL_CIRCLE_COLOR,
|
||||
alpha: SQUALL_CIRCLE_ALPHA,
|
||||
solid: true,
|
||||
visible: debug,
|
||||
position: squallOrigin,
|
||||
rotation: SQUALL_CIRCLE_ROTATION
|
||||
});
|
||||
}
|
||||
|
||||
raindropTimer = Script.setInterval(createRaindrop, 60000 / dropsPerMinute);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
Script.clearInterval(raindropTimer);
|
||||
if (debug) {
|
||||
Overlays.deleteOverlay(squallCircle);
|
||||
}
|
||||
}
|
||||
|
||||
processProperties();
|
||||
setUp();
|
||||
Script.scriptEnding.connect(tearDown);
|
||||
|
||||
return {
|
||||
};
|
||||
};
|
||||
|
||||
var rainSquall1 = new RainSquall({
|
||||
origin: { x: 1195, y: 1223, z: 1020 },
|
||||
radius: 25,
|
||||
dropsPerMinute: 120,
|
||||
dropSize: { x: 0.1, y: 0.1, z: 0.1 },
|
||||
dropFallSpeed: 3,
|
||||
dropLifetime: 30,
|
||||
dropSpinMax: 180,
|
||||
debug: false
|
||||
});
|
|
@ -18,3 +18,4 @@ Script.load("lobby.js");
|
|||
Script.load("notifications.js");
|
||||
Script.load("look.js");
|
||||
Script.load("users.js");
|
||||
Script.load("grab.js");
|
||||
|
|
|
@ -28,6 +28,7 @@ Script.include([
|
|||
"libraries/gridTool.js",
|
||||
"libraries/entityList.js",
|
||||
"libraries/lightOverlayManager.js",
|
||||
"libraries/zoneOverlayManager.js",
|
||||
]);
|
||||
|
||||
var selectionDisplay = SelectionDisplay;
|
||||
|
@ -35,6 +36,7 @@ var selectionManager = SelectionManager;
|
|||
var entityPropertyDialogBox = EntityPropertyDialogBox;
|
||||
|
||||
var lightOverlayManager = new LightOverlayManager();
|
||||
var zoneOverlayManager = new ZoneOverlayManager();
|
||||
|
||||
var cameraManager = new CameraManager();
|
||||
|
||||
|
@ -47,6 +49,7 @@ var entityListTool = EntityListTool();
|
|||
selectionManager.addEventListener(function() {
|
||||
selectionDisplay.updateHandles();
|
||||
lightOverlayManager.updatePositions();
|
||||
zoneOverlayManager.updatePositions();
|
||||
});
|
||||
|
||||
var windowDimensions = Controller.getViewportDimensions();
|
||||
|
@ -77,11 +80,13 @@ var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS);
|
|||
var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select";
|
||||
var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus";
|
||||
var MENU_SHOW_LIGHTS_IN_EDIT_MODE = "Show Lights in Edit Mode";
|
||||
var MENU_SHOW_ZONES_IN_EDIT_MODE = "Show Zones in Edit Mode";
|
||||
|
||||
var SETTING_INSPECT_TOOL_ENABLED = "inspectToolEnabled";
|
||||
var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect";
|
||||
var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus";
|
||||
var SETTING_SHOW_LIGHTS_IN_EDIT_MODE = "showLightsInEditMode";
|
||||
var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode";
|
||||
|
||||
var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain."
|
||||
var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain."
|
||||
|
@ -135,6 +140,7 @@ var toolBar = (function () {
|
|||
newSphereButton,
|
||||
newLightButton,
|
||||
newTextButton,
|
||||
newZoneButton,
|
||||
browseMarketplaceButton;
|
||||
|
||||
function initialize() {
|
||||
|
@ -201,6 +207,14 @@ var toolBar = (function () {
|
|||
alpha: 0.9,
|
||||
visible: false
|
||||
});
|
||||
newZoneButton = toolBar.addTool({
|
||||
imageURL: toolIconUrl + "zonecube3.svg",
|
||||
subImage: { x: 0, y: Tool.IMAGE_WIDTH + 208, width: 256, height: 256 },
|
||||
width: toolWidth,
|
||||
height: toolHeight,
|
||||
alpha: 0.9,
|
||||
visible: false
|
||||
});
|
||||
|
||||
that.setActive(false);
|
||||
}
|
||||
|
@ -232,6 +246,7 @@ var toolBar = (function () {
|
|||
}
|
||||
toolBar.selectTool(activeButton, isActive);
|
||||
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
|
||||
zoneOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
|
||||
};
|
||||
|
||||
// Sets visibility of tool buttons, excluding the power button
|
||||
|
@ -241,6 +256,7 @@ var toolBar = (function () {
|
|||
toolBar.showTool(newSphereButton, doShow);
|
||||
toolBar.showTool(newLightButton, doShow);
|
||||
toolBar.showTool(newTextButton, doShow);
|
||||
toolBar.showTool(newZoneButton, doShow);
|
||||
};
|
||||
|
||||
var RESIZE_INTERVAL = 50;
|
||||
|
@ -412,6 +428,21 @@ var toolBar = (function () {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (newZoneButton === toolBar.clicked(clickedOverlay)) {
|
||||
var position = getPositionToCreateEntity();
|
||||
|
||||
if (position.x > 0 && position.y > 0 && position.z > 0) {
|
||||
placingEntityID = Entities.addEntity({
|
||||
type: "Zone",
|
||||
position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_DIMENSIONS), DEFAULT_DIMENSIONS),
|
||||
dimensions: { x: 10, y: 10, z: 10 },
|
||||
});
|
||||
} else {
|
||||
print("Can't create box: Text would be out of bounds.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
@ -486,12 +517,22 @@ function rayPlaneIntersection(pickRay, point, normal) {
|
|||
}
|
||||
|
||||
function findClickedEntity(event) {
|
||||
var pickZones = event.isControl;
|
||||
|
||||
if (pickZones) {
|
||||
Entities.setZonesArePickable(true);
|
||||
}
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
||||
var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking
|
||||
var lightResult = lightOverlayManager.findRayIntersection(pickRay);
|
||||
lightResult.accurate = true;
|
||||
|
||||
if (pickZones) {
|
||||
Entities.setZonesArePickable(false);
|
||||
}
|
||||
|
||||
var result;
|
||||
|
||||
if (!entityResult.intersects && !lightResult.intersects) {
|
||||
|
@ -776,6 +817,8 @@ function setupModelMenus() {
|
|||
isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
|
||||
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, afterItem: MENU_EASE_ON_FOCUS,
|
||||
isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true" });
|
||||
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE,
|
||||
isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true" });
|
||||
|
||||
Entities.setLightsArePickable(false);
|
||||
}
|
||||
|
@ -804,12 +847,14 @@ function cleanupModelMenus() {
|
|||
Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT);
|
||||
Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS);
|
||||
Menu.removeMenuItem("View", MENU_SHOW_LIGHTS_IN_EDIT_MODE);
|
||||
Menu.removeMenuItem("View", MENU_SHOW_ZONES_IN_EDIT_MODE);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
Settings.setValue(SETTING_AUTO_FOCUS_ON_SELECT, Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT));
|
||||
Settings.setValue(SETTING_EASE_ON_FOCUS, Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||
Settings.setValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
|
||||
Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
|
||||
|
||||
progressDialog.cleanup();
|
||||
toolBar.cleanup();
|
||||
|
@ -942,6 +987,8 @@ function handeMenuEvent(menuItem) {
|
|||
selectAllEtitiesInCurrentSelectionBox(true);
|
||||
} else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) {
|
||||
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
|
||||
} else if (menuItem == MENU_SHOW_ZONES_IN_EDIT_MODE) {
|
||||
zoneOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
|
||||
}
|
||||
tooltip.show(false);
|
||||
}
|
||||
|
|
23
examples/example/entities/fullDomainZoneEntityExample.js
Normal file
23
examples/example/entities/fullDomainZoneEntityExample.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// fullDomainZoneEntityExample.js
|
||||
// examples
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 4/16/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example script that demonstrates creating a Zone which is as large as the entire domain
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Entities.addEntity({
|
||||
type: "Zone",
|
||||
position: { x: -10000, y: -10000, z: -10000 },
|
||||
dimensions: { x: 30000, y: 30000, z: 30000 },
|
||||
keyLightColor: { red: 255, green: 255, blue: 0 },
|
||||
keyLightDirection: { x: 0, y: -1.0, z: 0 },
|
||||
keyLightIntensity: 1.0,
|
||||
keyLightAmbientIntensity: 0.5
|
||||
});
|
||||
|
72
examples/example/entities/zoneEntityExample.js
Normal file
72
examples/example/entities/zoneEntityExample.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// zoneEntityExample.js
|
||||
// examples
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 4/16/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example script that demonstrates creating and editing a entity
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
|
||||
var count = 0;
|
||||
var stopAfter = 1000;
|
||||
|
||||
var zoneEntityA = Entities.addEntity({
|
||||
type: "Zone",
|
||||
position: { x: 5, y: 5, z: 5 },
|
||||
dimensions: { x: 10, y: 10, z: 10 },
|
||||
keyLightColor: { red: 255, green: 0, blue: 0 },
|
||||
stageSunModelEnabled: false,
|
||||
keyLightDirection: { x: 0, y: -1.0, z: 0 },
|
||||
shapeType: "sphere"
|
||||
});
|
||||
|
||||
print("zoneEntityA:" + zoneEntityA);
|
||||
|
||||
var zoneEntityB = Entities.addEntity({
|
||||
type: "Zone",
|
||||
position: { x: 5, y: 5, z: 5 },
|
||||
dimensions: { x: 2, y: 2, z: 2 },
|
||||
keyLightColor: { red: 0, green: 255, blue: 0 },
|
||||
keyLightIntensity: 0.9,
|
||||
stageLatitude: 37.777,
|
||||
stageLongitude: 122.407,
|
||||
stageAltitude: 0.03,
|
||||
stageDay: 60,
|
||||
stageHour: 12,
|
||||
stageSunModelEnabled: true
|
||||
});
|
||||
|
||||
print("zoneEntityB:" + zoneEntityB);
|
||||
|
||||
|
||||
var zoneEntityC = Entities.addEntity({
|
||||
type: "Zone",
|
||||
position: { x: 5, y: 10, z: 5 },
|
||||
dimensions: { x: 10, y: 10, z: 10 },
|
||||
keyLightColor: { red: 0, green: 0, blue: 255 },
|
||||
keyLightIntensity: 0.75,
|
||||
keyLightDirection: { x: 0, y: 0, z: -1 },
|
||||
stageSunModelEnabled: false,
|
||||
shapeType: "compound",
|
||||
compoundShapeURL: "http://headache.hungry.com/~seth/hifi/cube.fbx"
|
||||
});
|
||||
|
||||
print("zoneEntityC:" + zoneEntityC);
|
||||
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Script.update.connect(function(deltaTime) {
|
||||
// stop it...
|
||||
if (count >= stopAfter) {
|
||||
print("calling Script.stop()");
|
||||
Script.stop();
|
||||
}
|
||||
count++;
|
||||
});
|
||||
|
|
@ -39,126 +39,130 @@ hitSounds.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "Collisions-ballhitsandc
|
|||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
var screenSize = Controller.getViewportDimensions();
|
||||
var reticle = Overlays.addOverlay("image", {
|
||||
x: screenSize.x / 2 - 16,
|
||||
y: screenSize.y / 2 - 16,
|
||||
width: 32,
|
||||
height: 32,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
x: screenSize.x / 2 - 16,
|
||||
y: screenSize.y / 2 - 16,
|
||||
width: 32,
|
||||
height: 32,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
function makeTable(pos) {
|
||||
// Top
|
||||
tableParts.push(Entities.addEntity(
|
||||
// Top
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: pos,
|
||||
dimensions: { x: LENGTH * SCALE, y: HEIGHT, z: WIDTH * SCALE },
|
||||
color: { red: 0, green: 255, blue: 0 } }));
|
||||
// Long Bumpers
|
||||
tableParts.push(Entities.addEntity(
|
||||
position: pos,
|
||||
dimensions: { x: LENGTH * SCALE, y: HEIGHT, z: WIDTH * SCALE },
|
||||
color: { red: 0, green: 255, blue: 0 } }));
|
||||
// Long Bumpers
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x - LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
tableParts.push(Entities.addEntity(
|
||||
position: { x: pos.x - LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x + LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
position: { x: pos.x + LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
|
||||
tableParts.push(Entities.addEntity(
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x - LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
tableParts.push(Entities.addEntity(
|
||||
position: { x: pos.x - LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x + LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
// End bumpers
|
||||
tableParts.push(Entities.addEntity(
|
||||
position: { x: pos.x + LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
// End bumpers
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x + (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z },
|
||||
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
position: { x: pos.x + (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z },
|
||||
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
|
||||
tableParts.push(Entities.addEntity(
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x - (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z },
|
||||
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
position: { x: pos.x - (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z },
|
||||
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
|
||||
}
|
||||
|
||||
function makeBalls(pos) {
|
||||
// Object balls
|
||||
// Object balls
|
||||
var whichBall = [ 1, 14, 15, 4, 8, 7, 12, 9, 3, 13, 10, 5, 6, 11, 2 ];
|
||||
var ballNumber = 0;
|
||||
var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z };
|
||||
for (var row = 1; row <= 5; row++) {
|
||||
ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE);
|
||||
for (var spot = 0; spot < row; spot++) {
|
||||
balls.push(Entities.addEntity(
|
||||
var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z };
|
||||
for (var row = 1; row <= 5; row++) {
|
||||
ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE);
|
||||
for (var spot = 0; spot < row; spot++) {
|
||||
balls.push(Entities.addEntity(
|
||||
{ type: "Model",
|
||||
modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/ball_" + whichBall[ballNumber].toString() + ".fbx",
|
||||
position: ballPosition,
|
||||
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||
rotation: Quat.fromPitchYawRollDegrees((Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20),
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||
ignoreCollisions: false,
|
||||
damping: 0.50,
|
||||
shapeType: "sphere",
|
||||
collisionsWillMove: true }));
|
||||
ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE;
|
||||
modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/ball_" +
|
||||
whichBall[ballNumber].toString() + ".fbx",
|
||||
position: ballPosition,
|
||||
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||
rotation: Quat.fromPitchYawRollDegrees((Math.random() - 0.5) * 20,
|
||||
(Math.random() - 0.5) * 20,
|
||||
(Math.random() - 0.5) * 20),
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||
velocity: {x: 0, y: -0.2, z: 0 },
|
||||
ignoreCollisions: false,
|
||||
damping: 0.50,
|
||||
shapeType: "sphere",
|
||||
collisionsWillMove: true }));
|
||||
ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE;
|
||||
ballNumber++;
|
||||
}
|
||||
ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE;
|
||||
}
|
||||
ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE;
|
||||
}
|
||||
|
||||
// Cue Ball
|
||||
cuePosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z };
|
||||
cueBall = Entities.addEntity(
|
||||
{ type: "Model",
|
||||
modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/cue_ball.fbx",
|
||||
position: cuePosition,
|
||||
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||
angularVelocity: { x: 0, y: 0, z: 0 },
|
||||
velocity: {x: 0, y: 0, z: 0 },
|
||||
ignoreCollisions: false,
|
||||
damping: 0.50,
|
||||
shapeType: "sphere",
|
||||
collisionsWillMove: true });
|
||||
|
||||
{ type: "Model",
|
||||
modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/cue_ball.fbx",
|
||||
position: cuePosition,
|
||||
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||
angularVelocity: { x: 0, y: 0, z: 0 },
|
||||
velocity: {x: 0, y: -0.2, z: 0 },
|
||||
ignoreCollisions: false,
|
||||
damping: 0.50,
|
||||
shapeType: "sphere",
|
||||
collisionsWillMove: true });
|
||||
|
||||
}
|
||||
|
||||
function isObjectBall(id) {
|
||||
for (var i; i < balls.length; i++) {
|
||||
if (balls[i].id == id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
for (var i; i < balls.length; i++) {
|
||||
if (balls[i].id == id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function shootCue(velocity) {
|
||||
var DISTANCE_FROM_CAMERA = BALL_SIZE * 5.0 * SCALE;
|
||||
var DISTANCE_FROM_CAMERA = BALL_SIZE * 5.0 * SCALE;
|
||||
var camera = Camera.getPosition();
|
||||
var forwardVector = Quat.getFront(Camera.getOrientation());
|
||||
var cuePosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));
|
||||
|
@ -180,14 +184,14 @@ function shootCue(velocity) {
|
|||
density: 8000,
|
||||
ignoreCollisions: false,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
});
|
||||
print("Shot, velocity = " + velocity);
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
if ((startStroke > 0) && event.text == "SPACE") {
|
||||
var endTime = new Date().getTime();
|
||||
var delta = endTime - startStroke;
|
||||
if ((startStroke > 0) && event.text == "SPACE") {
|
||||
var endTime = new Date().getTime();
|
||||
var delta = endTime - startStroke;
|
||||
shootCue(delta / 100.0);
|
||||
startStroke = 0;
|
||||
}
|
||||
|
@ -201,49 +205,49 @@ function keyPressEvent(event) {
|
|||
}
|
||||
|
||||
function cleanup() {
|
||||
for (var i = 0; i < tableParts.length; i++) {
|
||||
if (!tableParts[i].isKnownID) {
|
||||
tableParts[i] = Entities.identifyEntity(tableParts[i]);
|
||||
}
|
||||
Entities.deleteEntity(tableParts[i]);
|
||||
for (var i = 0; i < tableParts.length; i++) {
|
||||
if (!tableParts[i].isKnownID) {
|
||||
tableParts[i] = Entities.identifyEntity(tableParts[i]);
|
||||
}
|
||||
for (var i = 0; i < balls.length; i++) {
|
||||
if (!balls[i].isKnownID) {
|
||||
balls[i] = Entities.identifyEntity(balls[i]);
|
||||
}
|
||||
Entities.deleteEntity(balls[i]);
|
||||
Entities.deleteEntity(tableParts[i]);
|
||||
}
|
||||
for (var i = 0; i < balls.length; i++) {
|
||||
if (!balls[i].isKnownID) {
|
||||
balls[i] = Entities.identifyEntity(balls[i]);
|
||||
}
|
||||
Overlays.deleteOverlay(reticle);
|
||||
Entities.deleteEntity(cueBall);
|
||||
Entities.deleteEntity(balls[i]);
|
||||
}
|
||||
Overlays.deleteOverlay(reticle);
|
||||
Entities.deleteEntity(cueBall);
|
||||
}
|
||||
|
||||
function update(deltaTime) {
|
||||
if (!cueBall.isKnownID) {
|
||||
cueBall = Entities.identifyEntity(cueBall);
|
||||
} else {
|
||||
// Check if cue ball has fallen off table, re-drop if so
|
||||
var cueProperties = Entities.getEntityProperties(cueBall);
|
||||
if (cueProperties.position.y < tableCenter.y) {
|
||||
// Replace the cueball
|
||||
Entities.editEntity(cueBall, { position: cuePosition } );
|
||||
if (!cueBall.isKnownID) {
|
||||
cueBall = Entities.identifyEntity(cueBall);
|
||||
} else {
|
||||
// Check if cue ball has fallen off table, re-drop if so
|
||||
var cueProperties = Entities.getEntityProperties(cueBall);
|
||||
if (cueProperties.position.y < tableCenter.y) {
|
||||
// Replace the cueball
|
||||
Entities.editEntity(cueBall, { position: cuePosition } );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function entityCollisionWithEntity(entity1, entity2, collision) {
|
||||
/*
|
||||
NOT WORKING YET
|
||||
if ((entity1.id == cueBall.id) || (entity2.id == cueBall.id)) {
|
||||
print("Cue ball collision!");
|
||||
//audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
|
||||
//Audio.playSound(hitSounds[0], { position: Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())) });
|
||||
}
|
||||
/*
|
||||
NOT WORKING YET
|
||||
if ((entity1.id == cueBall.id) || (entity2.id == cueBall.id)) {
|
||||
print("Cue ball collision!");
|
||||
//audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
|
||||
//Audio.playSound(hitSounds[0], { position: Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())) });
|
||||
}
|
||||
|
||||
else if (isObjectBall(entity1.id) || isObjectBall(entity2.id)) {
|
||||
print("Object ball collision");
|
||||
} */
|
||||
else if (isObjectBall(entity1.id) || isObjectBall(entity2.id)) {
|
||||
print("Object ball collision");
|
||||
} */
|
||||
}
|
||||
|
||||
tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation())));
|
||||
|
|
|
@ -41,7 +41,8 @@ panel.newSlider("Year Time", 0, 364,
|
|||
);
|
||||
|
||||
panel.newSlider("Day Time", 0, 24,
|
||||
function(value) { Scene.setStageDayTime(value); },
|
||||
|
||||
function(value) { Scene.setStageDayTime(value); panel.update("Light Direction"); },
|
||||
function() { return Scene.getStageDayTime(); },
|
||||
function(value) {
|
||||
var hour = Math.floor(value);
|
||||
|
@ -72,12 +73,36 @@ function runStageTime() {
|
|||
}
|
||||
Script.setInterval(runStageTime, tickTackPeriod);
|
||||
|
||||
panel.newCheckbox("Enable Sun Model",
|
||||
function(value) { Scene.setStageSunModelEnable((value != 0)); },
|
||||
function() { return Scene.isStageSunModelEnabled(); },
|
||||
function(value) { return (value); }
|
||||
);
|
||||
|
||||
panel.newDirectionBox("Light Direction",
|
||||
function(value) { Scene.setKeyLightDirection(value); },
|
||||
function() { return Scene.getKeyLightDirection(); },
|
||||
function(value) { return value.x.toFixed(2) + "," + value.y.toFixed(2) + "," + value.z.toFixed(2); }
|
||||
);
|
||||
|
||||
panel.newSlider("Light Intensity", 0.0, 5,
|
||||
function(value) { Scene.setSunIntensity(value); },
|
||||
function() { return Scene.getSunIntensity(); },
|
||||
function(value) { Scene.setKeyLightIntensity(value); },
|
||||
function() { return Scene.getKeyLightIntensity(); },
|
||||
function(value) { return (value).toFixed(2); }
|
||||
);
|
||||
|
||||
panel.newSlider("Ambient Light Intensity", 0.0, 1.0,
|
||||
function(value) { Scene.setKeyLightAmbientIntensity(value); },
|
||||
function() { return Scene.getKeyLightAmbientIntensity(); },
|
||||
function(value) { return (value).toFixed(2); }
|
||||
);
|
||||
|
||||
panel.newColorBox("Light Color",
|
||||
function(value) { Scene.setKeyLightColor(value); },
|
||||
function() { return Scene.getKeyLightColor(); },
|
||||
function(value) { return (value); } // "(" + value.x + "," = value.y + "," + value.z + ")"; }
|
||||
);
|
||||
|
||||
Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); });
|
||||
Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); });
|
||||
Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); });
|
||||
|
|
223
examples/grab.js
Normal file
223
examples/grab.js
Normal file
|
@ -0,0 +1,223 @@
|
|||
var isGrabbing = false;
|
||||
var grabbedEntity = null;
|
||||
var prevMouse = {};
|
||||
var deltaMouse = {
|
||||
z: 0
|
||||
}
|
||||
var entityProps;
|
||||
var box, box2, ground;
|
||||
var baseMoveFactor = .001;
|
||||
var finalMoveMultiplier;
|
||||
var avatarEntityDistance;
|
||||
var camYaw, dv;
|
||||
var prevPosition;
|
||||
var newPosition;
|
||||
var flingVelocity;
|
||||
var flingMultiplier = 10;
|
||||
var moveUpDown = false;
|
||||
var savedGravity;
|
||||
|
||||
var DROP_DISTANCE = 5.0;
|
||||
var DROP_COLOR = {
|
||||
red: 200,
|
||||
green: 200,
|
||||
blue: 200
|
||||
};
|
||||
var DROP_WIDTH = 4;
|
||||
|
||||
|
||||
var autoBox = false;
|
||||
if (autoBox) {
|
||||
setUpTestObjects();
|
||||
}
|
||||
|
||||
var dropLine = Overlays.addOverlay("line3d", {
|
||||
start: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
end: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
color: DROP_COLOR,
|
||||
alpha: 1,
|
||||
visible: false,
|
||||
lineWidth: DROP_WIDTH
|
||||
});
|
||||
|
||||
|
||||
function mousePressEvent(event) {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var intersection = Entities.findRayIntersection(pickRay);
|
||||
if (intersection.intersects && intersection.properties.collisionsWillMove) {
|
||||
grabbedEntity = intersection.entityID;
|
||||
var props = Entities.getEntityProperties(grabbedEntity)
|
||||
prevPosition = props.position;
|
||||
isGrabbing = true;
|
||||
savedGravity = props.gravity;
|
||||
Overlays.editOverlay(dropLine, {
|
||||
visible: true
|
||||
});
|
||||
Entities.editEntity(grabbedEntity, {
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}
|
||||
});
|
||||
//We need to store entity's current gravity, and then disable it while we move
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function mouseReleaseEvent() {
|
||||
if (isGrabbing) {
|
||||
flingObject();
|
||||
Entities.editEntity(grabbedEntity, {
|
||||
gravity: savedGravity
|
||||
});
|
||||
}
|
||||
isGrabbing = false;
|
||||
Overlays.editOverlay(dropLine, {
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
|
||||
function flingObject() {
|
||||
//calculate velocity to give object base on current and previous position
|
||||
entityProps = Entities.getEntityProperties(grabbedEntity);
|
||||
|
||||
flingVelocity = Vec3.subtract(entityProps.position, prevPosition);
|
||||
flingVelocity = Vec3.multiply(flingMultiplier, flingVelocity);
|
||||
flingVelocity.y = 0;
|
||||
Entities.editEntity(grabbedEntity, {
|
||||
velocity: flingVelocity
|
||||
});
|
||||
}
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
if (isGrabbing) {
|
||||
entityProps = Entities.getEntityProperties(grabbedEntity);
|
||||
prevPosition = entityProps.position;
|
||||
avatarEntityDistance = Vec3.distance(MyAvatar.position, entityProps.position);
|
||||
finalMoveMultiplier = baseMoveFactor * Math.pow(avatarEntityDistance, 1.5);
|
||||
deltaMouse.x = event.x - prevMouse.x;
|
||||
if (!moveUpDown) {
|
||||
deltaMouse.z = event.y - prevMouse.y;
|
||||
} else {
|
||||
deltaMouse.y = (event.y - prevMouse.y) * -1;
|
||||
}
|
||||
finalMoveMultiplier = baseMoveFactor * Math.pow(avatarEntityDistance, 1.5);
|
||||
deltaMouse = Vec3.multiply(deltaMouse, finalMoveMultiplier);
|
||||
camYaw = Quat.safeEulerAngles(Camera.getOrientation()).y;
|
||||
dv = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, camYaw, 0), deltaMouse);
|
||||
newPosition = Vec3.sum(entityProps.position, dv);
|
||||
Entities.editEntity(grabbedEntity, {
|
||||
position: newPosition
|
||||
});
|
||||
Overlays.editOverlay(dropLine, {
|
||||
start: newPosition,
|
||||
end: Vec3.sum(newPosition, {
|
||||
x: 0,
|
||||
y: -DROP_DISTANCE,
|
||||
z: 0
|
||||
})
|
||||
});
|
||||
}
|
||||
prevMouse.x = event.x;
|
||||
prevMouse.y = event.y;
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
if (event.text === "SHIFT") {
|
||||
moveUpDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
if (event.text === "SHIFT") {
|
||||
moveUpDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(box);
|
||||
Entities.deleteEntity(box2);
|
||||
Entities.deleteEntity(ground);
|
||||
}
|
||||
|
||||
function setUpTestObjects() {
|
||||
var distance = 4;
|
||||
box = Entities.addEntity({
|
||||
type: 'Box',
|
||||
position: Vec3.sum(MyAvatar.position, Vec3.multiply(distance, Quat.getFront(Camera.getOrientation()))),
|
||||
dimensions: {
|
||||
x: .5,
|
||||
y: .5,
|
||||
z: .5
|
||||
},
|
||||
color: {
|
||||
red: 200,
|
||||
green: 50,
|
||||
blue: 192
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -1,
|
||||
z: 0
|
||||
}
|
||||
});
|
||||
|
||||
box2 = Entities.addEntity({
|
||||
type: 'Box',
|
||||
position: Vec3.sum(MyAvatar.position, Vec3.multiply(distance + 1, Quat.getFront(Camera.getOrientation()))),
|
||||
dimensions: {
|
||||
x: .5,
|
||||
y: .5,
|
||||
z: .5
|
||||
},
|
||||
color: {
|
||||
red: 200,
|
||||
green: 50,
|
||||
blue: 192
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -1,
|
||||
z: 0
|
||||
}
|
||||
});
|
||||
|
||||
ground = Entities.addEntity({
|
||||
type: 'Box',
|
||||
position: {
|
||||
x: MyAvatar.position.x,
|
||||
y: MyAvatar.position.y - 5,
|
||||
z: MyAvatar.position.z
|
||||
},
|
||||
dimensions: {
|
||||
x: 100,
|
||||
y: 2,
|
||||
z: 100
|
||||
},
|
||||
color: {
|
||||
red: 20,
|
||||
green: 200,
|
||||
blue: 50
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -95,6 +95,7 @@
|
|||
};
|
||||
|
||||
function loaded() {
|
||||
var allSections = [];
|
||||
var elID = document.getElementById("property-id");
|
||||
var elType = document.getElementById("property-type");
|
||||
var elLocked = document.getElementById("property-locked");
|
||||
|
@ -146,11 +147,13 @@
|
|||
var elUserData = document.getElementById("property-user-data");
|
||||
|
||||
var elBoxSections = document.querySelectorAll(".box-section");
|
||||
allSections.push(elBoxSections);
|
||||
var elBoxColorRed = document.getElementById("property-box-red");
|
||||
var elBoxColorGreen = document.getElementById("property-box-green");
|
||||
var elBoxColorBlue = document.getElementById("property-box-blue");
|
||||
|
||||
var elLightSections = document.querySelectorAll(".light-section");
|
||||
allSections.push(elLightSections);
|
||||
var elLightSpotLight = document.getElementById("property-light-spot-light");
|
||||
var elLightColorRed = document.getElementById("property-light-color-red");
|
||||
var elLightColorGreen = document.getElementById("property-light-color-green");
|
||||
|
@ -161,8 +164,10 @@
|
|||
var elLightCutoff = document.getElementById("property-light-cutoff");
|
||||
|
||||
var elModelSections = document.querySelectorAll(".model-section");
|
||||
allSections.push(elModelSections);
|
||||
var elModelURL = document.getElementById("property-model-url");
|
||||
var elCollisionModelURL = document.getElementById("property-collision-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");
|
||||
|
@ -170,9 +175,9 @@
|
|||
var elModelAnimationSettings = document.getElementById("property-model-animation-settings");
|
||||
var elModelTextures = document.getElementById("property-model-textures");
|
||||
var elModelOriginalTextures = document.getElementById("property-model-original-textures");
|
||||
var elModelShapeType = document.getElementById("property-model-shape");
|
||||
|
||||
var elTextSections = document.querySelectorAll(".text-section");
|
||||
allSections.push(elTextSections);
|
||||
var elTextText = document.getElementById("property-text-text");
|
||||
var elTextLineHeight = document.getElementById("property-text-line-height");
|
||||
var elTextTextColorRed = document.getElementById("property-text-text-color-red");
|
||||
|
@ -182,6 +187,24 @@
|
|||
var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green");
|
||||
var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue");
|
||||
|
||||
var elZoneSections = document.querySelectorAll(".zone-section");
|
||||
allSections.push(elZoneSections);
|
||||
var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled");
|
||||
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 elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity");
|
||||
var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x");
|
||||
var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y");
|
||||
var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z");
|
||||
|
||||
var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude");
|
||||
var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude");
|
||||
var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude");
|
||||
var elZoneStageDay = document.getElementById("property-zone-stage-day");
|
||||
var elZoneStageHour = document.getElementById("property-zone-stage-hour");
|
||||
|
||||
if (window.EventBridge !== undefined) {
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
|
@ -282,11 +305,13 @@
|
|||
elScriptURL.value = properties.script;
|
||||
elUserData.value = properties.userData;
|
||||
|
||||
if (properties.type != "Box") {
|
||||
for (var i = 0; i < elBoxSections.length; i++) {
|
||||
elBoxSections[i].style.display = 'none';
|
||||
for (var i = 0; i < allSections.length; i++) {
|
||||
for (var j = 0; j < allSections[i].length; j++) {
|
||||
allSections[i][j].style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if (properties.type == "Box") {
|
||||
for (var i = 0; i < elBoxSections.length; i++) {
|
||||
elBoxSections[i].style.display = 'block';
|
||||
}
|
||||
|
@ -294,19 +319,14 @@
|
|||
elBoxColorRed.value = properties.color.red;
|
||||
elBoxColorGreen.value = properties.color.green;
|
||||
elBoxColorBlue.value = properties.color.blue;
|
||||
}
|
||||
|
||||
if (properties.type != "Model") {
|
||||
for (var i = 0; i < elModelSections.length; i++) {
|
||||
elModelSections[i].style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
} else if (properties.type == "Model") {
|
||||
for (var i = 0; i < elModelSections.length; i++) {
|
||||
elModelSections[i].style.display = 'block';
|
||||
}
|
||||
|
||||
elModelURL.value = properties.modelURL;
|
||||
elCollisionModelURL.value = properties.collisionModelURL;
|
||||
elShapeType.value = properties.shapeType;
|
||||
elCompoundShapeURL.value = properties.compoundShapeURL;
|
||||
elModelAnimationURL.value = properties.animationURL;
|
||||
elModelAnimationPlaying.checked = properties.animationIsPlaying;
|
||||
elModelAnimationFPS.value = properties.animationFPS;
|
||||
|
@ -314,14 +334,7 @@
|
|||
elModelAnimationSettings.value = properties.animationSettings;
|
||||
elModelTextures.value = properties.textures;
|
||||
elModelOriginalTextures.value = properties.originalTextures;
|
||||
elModelShapeType.value = properties.shapeType;
|
||||
}
|
||||
|
||||
if (properties.type != "Text") {
|
||||
for (var i = 0; i < elTextSections.length; i++) {
|
||||
elTextSections[i].style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
} else if (properties.type == "Text") {
|
||||
for (var i = 0; i < elTextSections.length; i++) {
|
||||
elTextSections[i].style.display = 'block';
|
||||
}
|
||||
|
@ -334,13 +347,7 @@
|
|||
elTextBackgroundColorRed.value = properties.backgroundColor.red;
|
||||
elTextBackgroundColorGreen.value = properties.backgroundColor.green;
|
||||
elTextBackgroundColorBlue.value = properties.backgroundColor.blue;
|
||||
}
|
||||
|
||||
if (properties.type != "Light") {
|
||||
for (var i = 0; i < elLightSections.length; i++) {
|
||||
elLightSections[i].style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
} else if (properties.type == "Light") {
|
||||
for (var i = 0; i < elLightSections.length; i++) {
|
||||
elLightSections[i].style.display = 'block';
|
||||
}
|
||||
|
@ -354,6 +361,28 @@
|
|||
elLightIntensity.value = properties.intensity;
|
||||
elLightExponent.value = properties.exponent;
|
||||
elLightCutoff.value = properties.cutoff;
|
||||
} else if (properties.type == "Zone") {
|
||||
for (var i = 0; i < elZoneSections.length; i++) {
|
||||
elZoneSections[i].style.display = 'block';
|
||||
}
|
||||
|
||||
elZoneStageSunModelEnabled.checked = properties.stageSunModelEnabled;
|
||||
elZoneKeyLightColorRed.value = properties.keyLightColor.red;
|
||||
elZoneKeyLightColorGreen.value = properties.keyLightColor.green;
|
||||
elZoneKeyLightColorBlue.value = properties.keyLightColor.blue;
|
||||
elZoneKeyLightIntensity.value = properties.keyLightIntensity.toFixed(2);
|
||||
elZoneKeyLightAmbientIntensity.value = properties.keyLightAmbientIntensity.toFixed(2);
|
||||
elZoneKeyLightDirectionX.value = properties.keyLightDirection.x.toFixed(2);
|
||||
elZoneKeyLightDirectionY.value = properties.keyLightDirection.y.toFixed(2);
|
||||
elZoneKeyLightDirectionZ.value = properties.keyLightDirection.z.toFixed(2);
|
||||
|
||||
elZoneStageLatitude.value = properties.stageLatitude.toFixed(2);
|
||||
elZoneStageLongitude.value = properties.stageLongitude.toFixed(2);
|
||||
elZoneStageAltitude.value = properties.stageAltitude.toFixed(2);
|
||||
elZoneStageDay.value = properties.stageDay;
|
||||
elZoneStageHour.value = properties.stageHour;
|
||||
elShapeType.value = properties.shapeType;
|
||||
elCompoundShapeURL.value = properties.compoundShapeURL;
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
|
@ -444,14 +473,14 @@
|
|||
elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff'));
|
||||
|
||||
elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
|
||||
elCollisionModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionModelURL'));
|
||||
elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType'));
|
||||
elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL'));
|
||||
elModelAnimationURL.addEventListener('change', createEmitTextPropertyUpdateFunction('animationURL'));
|
||||
elModelAnimationPlaying.addEventListener('change', createEmitCheckedPropertyUpdateFunction('animationIsPlaying'));
|
||||
elModelAnimationFPS.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFPS'));
|
||||
elModelAnimationFrame.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFrameIndex'));
|
||||
elModelAnimationSettings.addEventListener('change', createEmitTextPropertyUpdateFunction('animationSettings'));
|
||||
elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
|
||||
elModelShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType'));
|
||||
|
||||
elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text'));
|
||||
elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight'));
|
||||
|
@ -468,6 +497,26 @@
|
|||
elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction);
|
||||
elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
|
||||
|
||||
elZoneStageSunModelEnabled.addEventListener('change', createEmitCheckedPropertyUpdateFunction('stageSunModelEnabled'));
|
||||
var zoneKeyLightColorChangeFunction = createEmitColorPropertyUpdateFunction(
|
||||
'keyLightColor', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue);
|
||||
elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction);
|
||||
elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction);
|
||||
elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction);
|
||||
elZoneKeyLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('keyLightIntensity'));
|
||||
elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('keyLightAmbientIntensity'));
|
||||
var zoneKeyLightDirectionChangeFunction = createEmitVec3PropertyUpdateFunction(
|
||||
'keyLightDirection', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY, elZoneKeyLightDirectionZ);
|
||||
elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction);
|
||||
elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction);
|
||||
elZoneKeyLightDirectionZ.addEventListener('change', zoneKeyLightDirectionChangeFunction);
|
||||
|
||||
elZoneStageLatitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLatitude'));
|
||||
elZoneStageLongitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLongitude'));
|
||||
elZoneStageAltitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageAltitude'));
|
||||
elZoneStageDay.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageDay'));
|
||||
elZoneStageHour.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageHour'));
|
||||
|
||||
elMoveSelectionToGrid.addEventListener("click", function() {
|
||||
EventBridge.emitWebEvent(JSON.stringify({
|
||||
type: "action",
|
||||
|
@ -508,18 +557,22 @@
|
|||
// To make this work we block the first mouseup event after the elements
|
||||
// received focus. If we block all mouseup events the user will not
|
||||
// be able to click within the selected text.
|
||||
// We also check to see if the value has changed to make sure we aren't
|
||||
// blocking a mouse-up event when clicking on an input spinner.
|
||||
var els = document.querySelectorAll("input, textarea");
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
var clicked = false;
|
||||
var originalText;
|
||||
els[i].onfocus = function() {
|
||||
originalText = this.value;
|
||||
this.select();
|
||||
clicked = false;
|
||||
};
|
||||
els[i].onmouseup = function(e) {
|
||||
if (!clicked) {
|
||||
if (!clicked && originalText == this.value) {
|
||||
e.preventDefault();
|
||||
clicked = true;
|
||||
}
|
||||
clicked = true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -707,10 +760,21 @@
|
|||
<input type="text" id="property-model-url" class="url"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="model-section property">
|
||||
<div class="label">Collision Model URL</div>
|
||||
<div class="model-section zone-section property">
|
||||
<div class="label">Shape Type</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-collision-model-url" class="url"></input>
|
||||
<select name="SelectShapeType" id="property-shape-type" name="SelectShapeType">
|
||||
<option value='none'>none</option>
|
||||
<option value='box'>box</option>
|
||||
<option value='sphere'>sphere</option>
|
||||
<option value='compound'>compound</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="model-section zone-section property">
|
||||
<div class="label">Compound Shape URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-compound-shape-url" class="url"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="model-section property">
|
||||
|
@ -756,18 +820,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="model-section property">
|
||||
<div class="label">Shape Type</div>
|
||||
<div class="value">
|
||||
<select name="SelectShapeType" id="property-model-shape" name="SelectShapeType">
|
||||
<option value='none'>none</option>
|
||||
<option value='box'>box</option>
|
||||
<option value='sphere'>sphere</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="text-section property">
|
||||
<div class="label">Text</div>
|
||||
<div class="value">
|
||||
|
@ -829,6 +881,73 @@
|
|||
<input class="coord" type='number' id="property-light-cutoff"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section property">
|
||||
<span class="label">Stage Sun Model Enabled</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-zone-stage-sun-model-enabled">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="zone-section property">
|
||||
<div class="label">Key Light Color</div>
|
||||
<div class="value">
|
||||
<div class="input-area">R <input class="coord" type='number' id="property-zone-key-light-color-red" min="0" max="255" step="1"></input></div>
|
||||
<div class="input-area">G <input class="coord" type='number' id="property-zone-key-light-color-green" min="0" max="255" step="1"></input></div>
|
||||
<div class="input-area">B <input class="coord" type='number' id="property-zone-key-light-color-blue" min="0" max="255" step="1"></input></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="label">Key Light Intensity</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-key-intensity" min="0" max="10" step="0.1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="label">Key Light Ambient Intensity</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-key-ambient-intensity" min="0" max="10" step="0.1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="label">Key Light Direction</div>
|
||||
<div class="value">
|
||||
<div class="input-area">Pitch <input class="coord" type='number' id="property-zone-key-light-direction-x"></input></div>
|
||||
<div class="input-area">Yaw <input class="coord" type='number' id="property-zone-key-light-direction-y"></input></div>
|
||||
<div class="input-area">Roll <input class="coord" type='number' id="property-zone-key-light-direction-z"></input></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section property">
|
||||
<div class="label">Stage Latitude</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-latitude" min="-90" max="90" step="1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="label">Stage Longitude</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-longitude" min="-180" max="180" step="1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="label">Stage Altitude</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-altitude" step="1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="label">Stage Day</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-day" min="0" max="365" step="1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="label">Stage Hour</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-stage-hour" min="0" max="24" step="0.5"></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -53,7 +53,8 @@ function Tooltip() {
|
|||
text += "ID: " + properties.id + "\n"
|
||||
if (properties.type == "Model") {
|
||||
text += "Model URL: " + properties.modelURL + "\n"
|
||||
text += "Collision Model URL: " + properties.collisionModelURL + "\n"
|
||||
text += "Shape Type: " + properties.shapeType + "\n"
|
||||
text += "Compound Shape URL: " + properties.compoundShapeURL + "\n"
|
||||
text += "Animation URL: " + properties.animationURL + "\n"
|
||||
text += "Animation is playing: " + properties.animationIsPlaying + "\n"
|
||||
if (properties.sittingPoints && properties.sittingPoints.length > 0) {
|
||||
|
|
|
@ -52,7 +52,9 @@ EntityPropertyDialogBox = (function () {
|
|||
if (properties.type == "Model") {
|
||||
array.push({ label: "Model URL:", value: properties.modelURL });
|
||||
index++;
|
||||
array.push({ label: "Collision Model URL:", value: properties.collisionModelURL });
|
||||
array.push({ label: "Shape Type:", value: properties.shapeType });
|
||||
index++;
|
||||
array.push({ label: "Compound Shape URL:", value: properties.compoundShapeURL });
|
||||
index++;
|
||||
array.push({ label: "Animation URL:", value: properties.animationURL });
|
||||
index++;
|
||||
|
@ -284,7 +286,8 @@ EntityPropertyDialogBox = (function () {
|
|||
properties.locked = array[index++].value;
|
||||
if (properties.type == "Model") {
|
||||
properties.modelURL = array[index++].value;
|
||||
properties.collisionModelURL = array[index++].value;
|
||||
properties.shapeType = array[index++].value;
|
||||
properties.compoundShapeURL = array[index++].value;
|
||||
properties.animationURL = array[index++].value;
|
||||
|
||||
var newAnimationIsPlaying = array[index++].value;
|
||||
|
|
|
@ -122,7 +122,7 @@ LightOverlayManager = function() {
|
|||
Entities.clearingEntities.connect(clearEntities);
|
||||
|
||||
// Add existing entities
|
||||
var ids = Entities.findEntities(MyAvatar.position, 100);
|
||||
var ids = Entities.findEntities(MyAvatar.position, 64000);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
addEntity(ids[i]);
|
||||
}
|
||||
|
|
146
examples/libraries/zoneOverlayManager.js
Normal file
146
examples/libraries/zoneOverlayManager.js
Normal file
|
@ -0,0 +1,146 @@
|
|||
ZoneOverlayManager = function(isEntityFunc, entityAddedFunc, entityRemovedFunc, entityMovedFunc) {
|
||||
var self = this;
|
||||
|
||||
var visible = false;
|
||||
|
||||
// List of all created overlays
|
||||
var allOverlays = [];
|
||||
|
||||
// List of overlays not currently being used
|
||||
var unusedOverlays = [];
|
||||
|
||||
// Map from EntityItemID.id to overlay id
|
||||
var entityOverlays = {};
|
||||
|
||||
// Map from EntityItemID.id to EntityItemID object
|
||||
var entityIDs = {};
|
||||
|
||||
this.updatePositions = function(ids) {
|
||||
for (var id in entityIDs) {
|
||||
var entityID = entityIDs[id];
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
Overlays.editOverlay(entityOverlays[entityID.id].solid, {
|
||||
position: properties.position,
|
||||
rotation: properties.rotation,
|
||||
dimensions: properties.dimensions,
|
||||
});
|
||||
Overlays.editOverlay(entityOverlays[entityID.id].outline, {
|
||||
position: properties.position,
|
||||
rotation: properties.rotation,
|
||||
dimensions: properties.dimensions,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.setVisible = function(isVisible) {
|
||||
if (visible != isVisible) {
|
||||
visible = isVisible;
|
||||
for (var id in entityOverlays) {
|
||||
Overlays.editOverlay(entityOverlays[id].solid, { visible: visible });
|
||||
Overlays.editOverlay(entityOverlays[id].outline, { visible: visible });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Allocate or get an unused overlay
|
||||
function getOverlay() {
|
||||
if (unusedOverlays.length == 0) {
|
||||
var overlay = Overlays.addOverlay("cube", {
|
||||
});
|
||||
allOverlays.push(overlay);
|
||||
} else {
|
||||
var overlay = unusedOverlays.pop();
|
||||
};
|
||||
return overlay;
|
||||
}
|
||||
|
||||
function releaseOverlay(overlay) {
|
||||
unusedOverlays.push(overlay);
|
||||
Overlays.editOverlay(overlay, { visible: false });
|
||||
}
|
||||
|
||||
function addEntity(entityID) {
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
if (properties.type == "Zone" && !(entityID.id in entityOverlays)) {
|
||||
var overlaySolid = getOverlay();
|
||||
var overlayOutline = getOverlay();
|
||||
|
||||
entityOverlays[entityID.id] = {
|
||||
solid: overlaySolid,
|
||||
outline: overlayOutline,
|
||||
}
|
||||
entityIDs[entityID.id] = entityID;
|
||||
|
||||
var color = {
|
||||
red: Math.round(Math.random() * 255),
|
||||
green: Math.round(Math.random() * 255),
|
||||
blue: Math.round(Math.random() * 255)
|
||||
};
|
||||
Overlays.editOverlay(overlaySolid, {
|
||||
position: properties.position,
|
||||
rotation: properties.rotation,
|
||||
dimensions: properties.dimensions,
|
||||
visible: visible,
|
||||
solid: true,
|
||||
alpha: 0.1,
|
||||
color: color,
|
||||
ignoreRayIntersection: true,
|
||||
});
|
||||
Overlays.editOverlay(overlayOutline, {
|
||||
position: properties.position,
|
||||
rotation: properties.rotation,
|
||||
dimensions: properties.dimensions,
|
||||
visible: visible,
|
||||
solid: false,
|
||||
dashed: false,
|
||||
lineWidth: 2.0,
|
||||
alpha: 1.0,
|
||||
color: color,
|
||||
ignoreRayIntersection: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function deleteEntity(entityID) {
|
||||
if (entityID.id in entityOverlays) {
|
||||
releaseOverlay(entityOverlays[entityID.id].outline);
|
||||
releaseOverlay(entityOverlays[entityID.id].solid);
|
||||
delete entityIDs[entityID.id];
|
||||
delete entityOverlays[entityID.id];
|
||||
}
|
||||
}
|
||||
|
||||
function changeEntityID(oldEntityID, newEntityID) {
|
||||
entityOverlays[newEntityID.id] = entityOverlays[oldEntityID.id];
|
||||
entityIDs[newEntityID.id] = newEntityID;
|
||||
|
||||
delete entityOverlays[oldEntityID.id];
|
||||
delete entityIDs[oldEntityID.id];
|
||||
}
|
||||
|
||||
function clearEntities() {
|
||||
for (var id in entityOverlays) {
|
||||
releaseOverlay(entityOverlays[id].outline);
|
||||
releaseOverlay(entityOverlays[id].solid);
|
||||
}
|
||||
entityOverlays = {};
|
||||
entityIDs = {};
|
||||
}
|
||||
|
||||
Entities.addingEntity.connect(addEntity);
|
||||
Entities.changingEntityID.connect(changeEntityID);
|
||||
Entities.deletingEntity.connect(deleteEntity);
|
||||
Entities.clearingEntities.connect(clearEntities);
|
||||
|
||||
// Add existing entities
|
||||
var ids = Entities.findEntities(MyAvatar.position, 64000);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
addEntity(ids[i]);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
for (var i = 0; i < allOverlays.length; i++) {
|
||||
Overlays.deleteOverlay(allOverlays[i]);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -31,7 +31,9 @@ var yawFromTouch = 0;
|
|||
var pitchFromTouch = 0;
|
||||
|
||||
// Touch Data
|
||||
var TIME_BEFORE_GENERATED_END_TOUCH_EVENT = 50; // ms
|
||||
var startedTouching = false;
|
||||
var lastTouchEvent = 0;
|
||||
var lastMouseX = 0;
|
||||
var lastMouseY = 0;
|
||||
var yawFromMouse = 0;
|
||||
|
@ -80,11 +82,17 @@ function touchBeginEvent(event) {
|
|||
yawFromTouch = 0;
|
||||
pitchFromTouch = 0;
|
||||
startedTouching = true;
|
||||
var d = new Date();
|
||||
lastTouchEvent = d.getTime();
|
||||
}
|
||||
|
||||
function touchEndEvent(event) {
|
||||
if (wantDebugging) {
|
||||
print("touchEndEvent event.x,y=" + event.x + ", " + event.y);
|
||||
if (event) {
|
||||
print("touchEndEvent event.x,y=" + event.x + ", " + event.y);
|
||||
} else {
|
||||
print("touchEndEvent generated");
|
||||
}
|
||||
}
|
||||
startedTouching = false;
|
||||
}
|
||||
|
@ -96,16 +104,17 @@ function touchUpdateEvent(event) {
|
|||
}
|
||||
|
||||
if (!startedTouching) {
|
||||
// handle Qt 5.4.x bug where we get touch update without a touch begin event
|
||||
startedTouching = true;
|
||||
lastTouchX = event.x;
|
||||
lastTouchY = event.y;
|
||||
// handle Qt 5.4.x bug where we get touch update without a touch begin event
|
||||
touchBeginEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
yawFromTouch += ((event.x - lastTouchX) * TOUCH_YAW_SCALE * FIXED_TOUCH_TIMESTEP);
|
||||
pitchFromTouch += ((event.y - lastTouchY) * TOUCH_PITCH_SCALE * FIXED_TOUCH_TIMESTEP);
|
||||
lastTouchX = event.x;
|
||||
lastTouchY = event.y;
|
||||
var d = new Date();
|
||||
lastTouchEvent = d.getTime();
|
||||
}
|
||||
|
||||
|
||||
|
@ -113,6 +122,14 @@ function update(deltaTime) {
|
|||
if (wantDebugging) {
|
||||
print("update()...");
|
||||
}
|
||||
|
||||
if (startedTouching) {
|
||||
var d = new Date();
|
||||
var sinceLastTouch = d.getTime() - lastTouchEvent;
|
||||
if (sinceLastTouch > TIME_BEFORE_GENERATED_END_TOUCH_EVENT) {
|
||||
touchEndEvent();
|
||||
}
|
||||
}
|
||||
|
||||
if (yawFromTouch != 0 || yawFromMouse != 0) {
|
||||
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollRadians(0, yawFromTouch + yawFromMouse, 0));
|
||||
|
|
|
@ -12,6 +12,129 @@
|
|||
|
||||
// The Slider class
|
||||
Slider = function(x,y,width,thumbSize) {
|
||||
this.background = Overlays.addOverlay("text", {
|
||||
backgroundColor: { red: 125, green: 125, blue: 255 },
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: thumbSize,
|
||||
alpha: 1.0,
|
||||
backgroundAlpha: 0.5,
|
||||
visible: true
|
||||
});
|
||||
this.thumb = Overlays.addOverlay("text", {
|
||||
backgroundColor: { red: 255, green: 255, blue: 255 },
|
||||
x: x,
|
||||
y: y,
|
||||
width: thumbSize,
|
||||
height: thumbSize,
|
||||
alpha: 1.0,
|
||||
backgroundAlpha: 1.0,
|
||||
visible: true
|
||||
});
|
||||
|
||||
|
||||
this.thumbSize = thumbSize;
|
||||
this.thumbHalfSize = 0.5 * thumbSize;
|
||||
|
||||
this.minThumbX = x + this.thumbHalfSize;
|
||||
this.maxThumbX = x + width - this.thumbHalfSize;
|
||||
this.thumbX = this.minThumbX;
|
||||
|
||||
this.minValue = 0.0;
|
||||
this.maxValue = 1.0;
|
||||
|
||||
this.clickOffsetX = 0;
|
||||
this.isMoving = false;
|
||||
|
||||
this.updateThumb = function() {
|
||||
thumbTruePos = this.thumbX - 0.5 * this.thumbSize;
|
||||
Overlays.editOverlay(this.thumb, { x: thumbTruePos } );
|
||||
};
|
||||
|
||||
this.isClickableOverlayItem = function(item) {
|
||||
return (item == this.thumb) || (item == this.background);
|
||||
};
|
||||
|
||||
this.onMouseMoveEvent = function(event) {
|
||||
if (this.isMoving) {
|
||||
newThumbX = event.x - this.clickOffsetX;
|
||||
if (newThumbX < this.minThumbX) {
|
||||
newThumbX = this.minThumbX;
|
||||
}
|
||||
if (newThumbX > this.maxThumbX) {
|
||||
newThumbX = this.maxThumbX;
|
||||
}
|
||||
this.thumbX = newThumbX;
|
||||
this.updateThumb();
|
||||
this.onValueChanged(this.getValue());
|
||||
}
|
||||
};
|
||||
|
||||
this.onMousePressEvent = function(event, clickedOverlay) {
|
||||
if (!this.isClickableOverlayItem(clickedOverlay)) {
|
||||
this.isMoving = false;
|
||||
return;
|
||||
}
|
||||
this.isMoving = true;
|
||||
var clickOffset = event.x - this.thumbX;
|
||||
if ((clickOffset > -this.thumbHalfSize) && (clickOffset < this.thumbHalfSize)) {
|
||||
this.clickOffsetX = clickOffset;
|
||||
} else {
|
||||
this.clickOffsetX = 0;
|
||||
this.thumbX = event.x;
|
||||
this.updateThumb();
|
||||
this.onValueChanged(this.getValue());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.onMouseReleaseEvent = function(event) {
|
||||
this.isMoving = false;
|
||||
};
|
||||
|
||||
// Public members:
|
||||
|
||||
this.setNormalizedValue = function(value) {
|
||||
if (value < 0.0) {
|
||||
this.thumbX = this.minThumbX;
|
||||
} else if (value > 1.0) {
|
||||
this.thumbX = this.maxThumbX;
|
||||
} else {
|
||||
this.thumbX = value * (this.maxThumbX - this.minThumbX) + this.minThumbX;
|
||||
}
|
||||
this.updateThumb();
|
||||
};
|
||||
this.getNormalizedValue = function() {
|
||||
return (this.thumbX - this.minThumbX) / (this.maxThumbX - this.minThumbX);
|
||||
};
|
||||
|
||||
this.setValue = function(value) {
|
||||
var normValue = (value - this.minValue) / (this.maxValue - this.minValue);
|
||||
this.setNormalizedValue(normValue);
|
||||
};
|
||||
|
||||
this.getValue = function() {
|
||||
return this.getNormalizedValue() * (this.maxValue - this.minValue) + this.minValue;
|
||||
};
|
||||
|
||||
this.onValueChanged = function(value) {};
|
||||
|
||||
this.destroy = function() {
|
||||
Overlays.deleteOverlay(this.background);
|
||||
Overlays.deleteOverlay(this.thumb);
|
||||
};
|
||||
|
||||
this.setThumbColor = function(color) {
|
||||
Overlays.editOverlay(this.thumb, {backgroundColor: { red: color.x*255, green: color.y*255, blue: color.z*255 }});
|
||||
};
|
||||
this.setBackgroundColor = function(color) {
|
||||
Overlays.editOverlay(this.background, {backgroundColor: { red: color.x*255, green: color.y*255, blue: color.z*255 }});
|
||||
};
|
||||
}
|
||||
|
||||
// The Checkbox class
|
||||
Checkbox = function(x,y,width,thumbSize) {
|
||||
|
||||
this.thumb = Overlays.addOverlay("text", {
|
||||
backgroundColor: { red: 255, green: 255, blue: 255 },
|
||||
|
@ -52,6 +175,10 @@ Slider = function(x,y,width,thumbSize) {
|
|||
Overlays.editOverlay(this.thumb, { x: thumbTruePos } );
|
||||
};
|
||||
|
||||
this.isClickableOverlayItem = function(item) {
|
||||
return item == this.background;
|
||||
};
|
||||
|
||||
this.onMouseMoveEvent = function(event) {
|
||||
if (this.isMoving) {
|
||||
newThumbX = event.x - this.clickOffsetX;
|
||||
|
@ -67,7 +194,11 @@ Slider = function(x,y,width,thumbSize) {
|
|||
}
|
||||
};
|
||||
|
||||
this.onMousePressEvent = function(event) {
|
||||
this.onMousePressEvent = function(event, clickedOverlay) {
|
||||
if (this.background != clickedOverlay) {
|
||||
this.isMoving = false;
|
||||
return;
|
||||
}
|
||||
this.isMoving = true;
|
||||
var clickOffset = event.x - this.thumbX;
|
||||
if ((clickOffset > -this.thumbHalfSize) && (clickOffset < this.thumbHalfSize)) {
|
||||
|
@ -118,6 +249,167 @@ Slider = function(x,y,width,thumbSize) {
|
|||
};
|
||||
}
|
||||
|
||||
// The ColorBox class
|
||||
ColorBox = function(x,y,width,thumbSize) {
|
||||
var self = this;
|
||||
|
||||
var slideHeight = thumbSize / 3;
|
||||
var sliderWidth = width;
|
||||
this.red = new Slider(x, y, width, slideHeight);
|
||||
this.green = new Slider(x, y + slideHeight, width, slideHeight);
|
||||
this.blue = new Slider(x, y + 2 * slideHeight, width, slideHeight);
|
||||
this.red.setBackgroundColor({x: 1, y: 0, z: 0});
|
||||
this.green.setBackgroundColor({x: 0, y: 1, z: 0});
|
||||
this.blue.setBackgroundColor({x: 0, y: 0, z: 1});
|
||||
|
||||
this.isClickableOverlayItem = function(item) {
|
||||
return this.red.isClickableOverlayItem(item)
|
||||
|| this.green.isClickableOverlayItem(item)
|
||||
|| this.blue.isClickableOverlayItem(item);
|
||||
};
|
||||
|
||||
this.onMouseMoveEvent = function(event) {
|
||||
this.red.onMouseMoveEvent(event);
|
||||
this.green.onMouseMoveEvent(event);
|
||||
this.blue.onMouseMoveEvent(event);
|
||||
};
|
||||
|
||||
this.onMousePressEvent = function(event, clickedOverlay) {
|
||||
this.red.onMousePressEvent(event, clickedOverlay);
|
||||
if (this.red.isMoving) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.green.onMousePressEvent(event, clickedOverlay);
|
||||
if (this.green.isMoving) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.blue.onMousePressEvent(event, clickedOverlay);
|
||||
};
|
||||
|
||||
this.onMouseReleaseEvent = function(event) {
|
||||
this.red.onMouseReleaseEvent(event);
|
||||
this.green.onMouseReleaseEvent(event);
|
||||
this.blue.onMouseReleaseEvent(event);
|
||||
};
|
||||
|
||||
this.setterFromWidget = function(value) {
|
||||
var color = self.getValue();
|
||||
self.onValueChanged(color);
|
||||
self.updateRGBSliders(color);
|
||||
};
|
||||
|
||||
this.red.onValueChanged = this.setterFromWidget;
|
||||
this.green.onValueChanged = this.setterFromWidget;
|
||||
this.blue.onValueChanged = this.setterFromWidget;
|
||||
|
||||
this.updateRGBSliders = function(color) {
|
||||
this.red.setThumbColor({x: color.x, y: 0, z: 0});
|
||||
this.green.setThumbColor({x: 0, y: color.y, z: 0});
|
||||
this.blue.setThumbColor({x: 0, y: 0, z: color.z});
|
||||
};
|
||||
|
||||
// Public members:
|
||||
this.setValue = function(value) {
|
||||
this.red.setValue(value.x);
|
||||
this.green.setValue(value.y);
|
||||
this.blue.setValue(value.z);
|
||||
this.updateRGBSliders(value);
|
||||
};
|
||||
|
||||
this.getValue = function() {
|
||||
var value = {x:this.red.getValue(), y:this.green.getValue(),z:this.blue.getValue()};
|
||||
return value;
|
||||
};
|
||||
|
||||
this.destroy = function() {
|
||||
this.red.destroy();
|
||||
this.green.destroy();
|
||||
this.blue.destroy();
|
||||
};
|
||||
|
||||
this.onValueChanged = function(value) {};
|
||||
}
|
||||
|
||||
// The DirectionBox class
|
||||
DirectionBox = function(x,y,width,thumbSize) {
|
||||
var self = this;
|
||||
|
||||
var slideHeight = thumbSize / 2;
|
||||
var sliderWidth = width;
|
||||
this.yaw = new Slider(x, y, width, slideHeight);
|
||||
this.pitch = new Slider(x, y + slideHeight, width, slideHeight);
|
||||
|
||||
this.yaw.setThumbColor({x: 1, y: 0, z: 0});
|
||||
this.yaw.minValue = -180;
|
||||
this.yaw.maxValue = +180;
|
||||
|
||||
this.pitch.setThumbColor({x: 0, y: 0, z: 1});
|
||||
this.pitch.minValue = -1;
|
||||
this.pitch.maxValue = +1;
|
||||
|
||||
this.isClickableOverlayItem = function(item) {
|
||||
return this.yaw.isClickableOverlayItem(item)
|
||||
|| this.pitch.isClickableOverlayItem(item);
|
||||
};
|
||||
|
||||
this.onMouseMoveEvent = function(event) {
|
||||
this.yaw.onMouseMoveEvent(event);
|
||||
this.pitch.onMouseMoveEvent(event);
|
||||
};
|
||||
|
||||
this.onMousePressEvent = function(event, clickedOverlay) {
|
||||
this.yaw.onMousePressEvent(event, clickedOverlay);
|
||||
if (this.yaw.isMoving) {
|
||||
return;
|
||||
}
|
||||
this.pitch.onMousePressEvent(event, clickedOverlay);
|
||||
};
|
||||
|
||||
this.onMouseReleaseEvent = function(event) {
|
||||
this.yaw.onMouseReleaseEvent(event);
|
||||
this.pitch.onMouseReleaseEvent(event);
|
||||
};
|
||||
|
||||
this.setterFromWidget = function(value) {
|
||||
var yawPitch = self.getValue();
|
||||
self.onValueChanged(yawPitch);
|
||||
};
|
||||
|
||||
this.yaw.onValueChanged = this.setterFromWidget;
|
||||
this.pitch.onValueChanged = this.setterFromWidget;
|
||||
|
||||
// Public members:
|
||||
this.setValue = function(direction) {
|
||||
var flatXZ = Math.sqrt(direction.x * direction.x + direction.z * direction.z);
|
||||
if (flatXZ > 0.0) {
|
||||
var flatX = direction.x / flatXZ;
|
||||
var flatZ = direction.z / flatXZ;
|
||||
var yaw = Math.acos(flatX) * 180 / Math.PI;
|
||||
if (flatZ < 0) {
|
||||
yaw = -yaw;
|
||||
}
|
||||
this.yaw.setValue(yaw);
|
||||
}
|
||||
this.pitch.setValue(direction.y);
|
||||
};
|
||||
|
||||
this.getValue = function() {
|
||||
var dirZ = this.pitch.getValue();
|
||||
var yaw = this.yaw.getValue() * Math.PI / 180;
|
||||
var cosY = Math.sqrt(1 - dirZ*dirZ);
|
||||
var value = {x:cosY * Math.cos(yaw), y:dirZ, z: cosY * Math.sin(yaw)};
|
||||
return value;
|
||||
};
|
||||
|
||||
this.destroy = function() {
|
||||
this.yaw.destroy();
|
||||
this.pitch.destroy();
|
||||
};
|
||||
|
||||
this.onValueChanged = function(value) {};
|
||||
}
|
||||
|
||||
var textFontSize = 16;
|
||||
|
||||
|
@ -167,7 +459,12 @@ function PanelItem(name, setter, getter, displayer, x, y, textWidth, valueWidth,
|
|||
};
|
||||
this.setterFromWidget = function(value) {
|
||||
setter(value);
|
||||
Overlays.editOverlay(this.value, {text: this.displayer(getter())});
|
||||
// ANd loop back the value after the final setter has been called
|
||||
var value = getter();
|
||||
if (this.widget) {
|
||||
this.widget.setValue(value);
|
||||
}
|
||||
Overlays.editOverlay(this.value, {text: this.displayer(value)});
|
||||
};
|
||||
|
||||
|
||||
|
@ -219,9 +516,9 @@ Panel = function(x, y) {
|
|||
for (var i in this.items) {
|
||||
var widget = this.items[i].widget;
|
||||
|
||||
if (clickedOverlay == widget.background) {
|
||||
if (widget.isClickableOverlayItem(clickedOverlay)) {
|
||||
this.activeWidget = widget;
|
||||
this.activeWidget.onMousePressEvent(event);
|
||||
this.activeWidget.onMousePressEvent(event, clickedOverlay);
|
||||
// print("clicked... widget=" + i);
|
||||
break;
|
||||
}
|
||||
|
@ -237,21 +534,63 @@ Panel = function(x, y) {
|
|||
|
||||
this.newSlider = function(name, minValue, maxValue, setValue, getValue, displayValue) {
|
||||
|
||||
var sliderItem = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight);
|
||||
var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight);
|
||||
|
||||
var slider = new Slider(this.widgetX, this.nextY, widgetWidth, rawHeight);
|
||||
slider.minValue = minValue;
|
||||
slider.maxValue = maxValue;
|
||||
slider.onValueChanged = function(value) { sliderItem.setterFromWidget(value); };
|
||||
|
||||
|
||||
sliderItem.widget = slider;
|
||||
sliderItem.setter(getValue());
|
||||
this.items[name] = sliderItem;
|
||||
item.widget = slider;
|
||||
item.widget.onValueChanged = function(value) { item.setterFromWidget(value); };
|
||||
item.setter(getValue());
|
||||
this.items[name] = item;
|
||||
this.nextY += rawYDelta;
|
||||
// print("created Item... slider=" + name);
|
||||
};
|
||||
|
||||
this.newCheckbox = function(name, setValue, getValue, displayValue) {
|
||||
|
||||
var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight);
|
||||
|
||||
var checkbox = new Checkbox(this.widgetX, this.nextY, widgetWidth, rawHeight);
|
||||
|
||||
item.widget = checkbox;
|
||||
item.widget.onValueChanged = function(value) { item.setterFromWidget(value); };
|
||||
item.setter(getValue());
|
||||
this.items[name] = item;
|
||||
this.nextY += rawYDelta;
|
||||
// print("created Item... slider=" + name);
|
||||
};
|
||||
|
||||
this.newColorBox = function(name, setValue, getValue, displayValue) {
|
||||
|
||||
var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight);
|
||||
|
||||
var colorBox = new ColorBox(this.widgetX, this.nextY, widgetWidth, rawHeight);
|
||||
|
||||
item.widget = colorBox;
|
||||
item.widget.onValueChanged = function(value) { item.setterFromWidget(value); };
|
||||
item.setter(getValue());
|
||||
this.items[name] = item;
|
||||
this.nextY += rawYDelta;
|
||||
// print("created Item... colorBox=" + name);
|
||||
};
|
||||
|
||||
this.newDirectionBox= function(name, setValue, getValue, displayValue) {
|
||||
|
||||
var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight);
|
||||
|
||||
var directionBox = new DirectionBox(this.widgetX, this.nextY, widgetWidth, rawHeight);
|
||||
|
||||
item.widget = directionBox;
|
||||
item.widget.onValueChanged = function(value) { item.setterFromWidget(value); };
|
||||
item.setter(getValue());
|
||||
this.items[name] = item;
|
||||
this.nextY += rawYDelta;
|
||||
// print("created Item... directionBox=" + name);
|
||||
};
|
||||
|
||||
this.destroy = function() {
|
||||
for (var i in this.items) {
|
||||
this.items[i].destroy();
|
||||
|
@ -273,6 +612,15 @@ Panel = function(x, y) {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
this.update = function(name) {
|
||||
var item = this.items[name];
|
||||
if (item != null) {
|
||||
return item.setter(item.getter());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -12,17 +12,29 @@
|
|||
//
|
||||
|
||||
var createdRenderMenu = false;
|
||||
var createdGeneratedAudioMenu = false;
|
||||
var createdStereoInputMenuItem = false;
|
||||
|
||||
var ENTITIES_MENU = "Developer > Entities";
|
||||
var DEVELOPER_MENU = "Developer";
|
||||
|
||||
var ENTITIES_MENU = DEVELOPER_MENU + " > Entities";
|
||||
var COLLISION_UPDATES_TO_SERVER = "Don't send collision updates to server";
|
||||
|
||||
var RENDER_MENU = "Developer > Render";
|
||||
var RENDER_MENU = DEVELOPER_MENU + " > Render";
|
||||
var ENTITIES_ITEM = "Entities";
|
||||
var AVATARS_ITEM = "Avatars";
|
||||
|
||||
var AUDIO_MENU = DEVELOPER_MENU + " > Audio";
|
||||
var AUDIO_SOURCE_INJECT = "Generated Audio";
|
||||
var AUDIO_SOURCE_MENU = AUDIO_MENU + " > Generated Audio Source";
|
||||
var AUDIO_SOURCE_PINK_NOISE = "Pink Noise";
|
||||
var AUDIO_SOURCE_SINE_440 = "Sine 440hz";
|
||||
var AUDIO_STEREO_INPUT = "Stereo Input";
|
||||
|
||||
|
||||
function setupMenus() {
|
||||
if (!Menu.menuExists("Developer")) {
|
||||
Menu.addMenu("Developer");
|
||||
if (!Menu.menuExists(DEVELOPER_MENU)) {
|
||||
Menu.addMenu(DEVELOPER_MENU);
|
||||
}
|
||||
if (!Menu.menuExists(ENTITIES_MENU)) {
|
||||
Menu.addMenu(ENTITIES_MENU);
|
||||
|
@ -54,6 +66,24 @@ function setupMenus() {
|
|||
if (!Menu.menuItemExists(RENDER_MENU, AVATARS_ITEM)) {
|
||||
Menu.addMenuItem({ menuName: RENDER_MENU, menuItemName: AVATARS_ITEM, isCheckable: true, isChecked: Scene.shouldRenderAvatars })
|
||||
}
|
||||
|
||||
|
||||
if (!Menu.menuExists(AUDIO_MENU)) {
|
||||
Menu.addMenu(AUDIO_MENU);
|
||||
}
|
||||
if (!Menu.menuItemExists(AUDIO_MENU, AUDIO_SOURCE_INJECT)) {
|
||||
Menu.addMenuItem({ menuName: AUDIO_MENU, menuItemName: AUDIO_SOURCE_INJECT, isCheckable: true, isChecked: false });
|
||||
Menu.addMenu(AUDIO_SOURCE_MENU);
|
||||
Menu.addMenuItem({ menuName: AUDIO_SOURCE_MENU, menuItemName: AUDIO_SOURCE_PINK_NOISE, isCheckable: true, isChecked: false });
|
||||
Menu.addMenuItem({ menuName: AUDIO_SOURCE_MENU, menuItemName: AUDIO_SOURCE_SINE_440, isCheckable: true, isChecked: false });
|
||||
Menu.setIsOptionChecked(AUDIO_SOURCE_PINK_NOISE, true);
|
||||
Audio.selectPinkNoise();
|
||||
createdGeneratedAudioMenu = true;
|
||||
}
|
||||
if (!Menu.menuItemExists(AUDIO_MENU, AUDIO_STEREO_INPUT)) {
|
||||
Menu.addMenuItem({ menuName: AUDIO_MENU, menuItemName: AUDIO_STEREO_INPUT, isCheckable: true, isChecked: false });
|
||||
createdStereoInputMenuItem = true;
|
||||
}
|
||||
}
|
||||
|
||||
Menu.menuItemEvent.connect(function (menuItem) {
|
||||
|
@ -67,7 +97,17 @@ Menu.menuItemEvent.connect(function (menuItem) {
|
|||
Scene.shouldRenderEntities = Menu.isOptionChecked(ENTITIES_ITEM);
|
||||
} else if (menuItem == AVATARS_ITEM) {
|
||||
Scene.shouldRenderAvatars = Menu.isOptionChecked(AVATARS_ITEM);
|
||||
}
|
||||
} else if (menuItem == AUDIO_SOURCE_INJECT && !createdGeneratedAudioMenu) {
|
||||
Audio.injectGeneratedNoise(Menu.isOptionChecked(AUDIO_SOURCE_INJECT));
|
||||
} else if (menuItem == AUDIO_SOURCE_PINK_NOISE && !createdGeneratedAudioMenu) {
|
||||
Audio.selectPinkNoise();
|
||||
Menu.setIsOptionChecked(AUDIO_SOURCE_SINE_440, false);
|
||||
} else if (menuItem == AUDIO_SOURCE_SINE_440 && !createdGeneratedAudioMenu) {
|
||||
Audio.selectSine440();
|
||||
Menu.setIsOptionChecked(AUDIO_SOURCE_PINK_NOISE, false);
|
||||
} else if (menuItem == AUDIO_STEREO_INPUT) {
|
||||
Audio.setStereoInput(Menu.isOptionChecked(AUDIO_STEREO_INPUT))
|
||||
}
|
||||
});
|
||||
|
||||
Scene.shouldRenderAvatarsChanged.connect(function(shouldRenderAvatars) {
|
||||
|
@ -87,6 +127,16 @@ function scriptEnding() {
|
|||
Menu.removeMenuItem(RENDER_MENU, ENTITIES_ITEM);
|
||||
Menu.removeMenuItem(RENDER_MENU, AVATARS_ITEM);
|
||||
}
|
||||
|
||||
if (createdGeneratedAudioMenu) {
|
||||
Audio.injectGeneratedNoise(false);
|
||||
Menu.removeMenuItem(AUDIO_MENU, AUDIO_SOURCE_INJECT);
|
||||
Menu.removeMenu(AUDIO_SOURCE_MENU);
|
||||
}
|
||||
|
||||
if (createdStereoInputMenuItem) {
|
||||
Menu.removeMenuItem(AUDIO_MENU, AUDIO_STEREO_INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
setupMenus();
|
||||
|
|
|
@ -106,18 +106,15 @@ if (APPLE)
|
|||
# set where in the bundle to put the resources file
|
||||
SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/icon/${ICON_FILENAME} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
|
||||
# grab the directories in resources and put them in the right spot in Resources
|
||||
file(GLOB RESOURCE_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/resources" "${CMAKE_CURRENT_SOURCE_DIR}/resources/*")
|
||||
foreach(DIR ${RESOURCE_SUBDIRS})
|
||||
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/resources/${DIR}")
|
||||
FILE(GLOB DIR_CONTENTS "resources/${DIR}/*")
|
||||
SET_SOURCE_FILES_PROPERTIES(${DIR_CONTENTS} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/${DIR}")
|
||||
set(DISCOVERED_RESOURCES "")
|
||||
|
||||
SET(INTERFACE_SRCS ${INTERFACE_SRCS} "${DIR_CONTENTS}")
|
||||
endif()
|
||||
endforeach()
|
||||
# use the add_resources_to_os_x_bundle macro to recurse into resources
|
||||
add_resources_to_os_x_bundle("${CMAKE_CURRENT_SOURCE_DIR}/resources")
|
||||
|
||||
SET(INTERFACE_SRCS ${INTERFACE_SRCS} "${CMAKE_CURRENT_SOURCE_DIR}/icon/${ICON_FILENAME}")
|
||||
# append the discovered resources to our list of interface sources
|
||||
list(APPEND INTERFACE_SRCS ${DISCOVERED_RESOURCES})
|
||||
|
||||
set(INTERFACE_SRCS ${INTERFACE_SRCS} "${CMAKE_CURRENT_SOURCE_DIR}/icon/${ICON_FILENAME}")
|
||||
endif()
|
||||
|
||||
# create the executable, make it a bundle on OS X
|
||||
|
@ -146,7 +143,7 @@ target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
|
|||
# link required hifi libraries
|
||||
link_hifi_libraries(shared octree environment gpu model fbx networking entities avatars
|
||||
audio audio-client animation script-engine physics
|
||||
render-utils entities-renderer)
|
||||
render-utils entities-renderer ui)
|
||||
|
||||
add_dependency_external_projects(sdl2)
|
||||
|
||||
|
@ -188,15 +185,6 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
|||
endif ()
|
||||
endforeach()
|
||||
|
||||
# special APPLE modifications for Visage library
|
||||
if (VISAGE_FOUND AND NOT DISABLE_VISAGE AND APPLE)
|
||||
add_definitions(-DMAC_OS_X)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-comment")
|
||||
find_library(AVFoundation AVFoundation)
|
||||
find_library(CoreMedia CoreMedia)
|
||||
target_link_libraries(${TARGET_NAME} ${AVFoundation} ${CoreMedia} ${NEW_STD_LIBRARY})
|
||||
endif ()
|
||||
|
||||
# special OS X modifications for RtMidi library
|
||||
if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI AND APPLE)
|
||||
find_library(CoreMIDI CoreMIDI)
|
||||
|
|
14
interface/external/visage/readme.txt
vendored
14
interface/external/visage/readme.txt
vendored
|
@ -1,14 +0,0 @@
|
|||
|
||||
Instructions for adding the Visage driver to Interface
|
||||
Andrzej Kapolka, February 11, 2014
|
||||
|
||||
1. Copy the Visage sdk folders (lib, include, dependencies) into the interface/external/visage folder.
|
||||
This readme.txt should be there as well.
|
||||
|
||||
2. Copy the contents of the Visage configuration data folder (visageSDK-MacOS/Samples/MacOSX/data/) to
|
||||
interface/resources/visage (i.e., so that interface/resources/visage/candide3.wfm is accessible)
|
||||
|
||||
3. Copy the Visage license file to interface/resources/visage/license.vlc.
|
||||
|
||||
4. Delete your build directory, run cmake and build, and you should be all set.
|
||||
|
BIN
interface/resources/fonts/fontawesome-webfont.ttf
Normal file
BIN
interface/resources/fonts/fontawesome-webfont.ttf
Normal file
Binary file not shown.
102
interface/resources/qml/AddressBarDialog.qml
Normal file
102
interface/resources/qml/AddressBarDialog.qml
Normal file
|
@ -0,0 +1,102 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
title: "Go to..."
|
||||
objectName: "AddressBarDialog"
|
||||
contentImplicitWidth: addressBarDialog.implicitWidth
|
||||
contentImplicitHeight: addressBarDialog.implicitHeight
|
||||
destroyOnCloseButton: false
|
||||
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
onEnabledChanged: {
|
||||
if (enabled) {
|
||||
addressLine.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
onParentChanged: {
|
||||
if (enabled && visible) {
|
||||
addressLine.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
function reset() {
|
||||
addressLine.text = ""
|
||||
goButton.source = "../images/address-bar-submit.svg"
|
||||
}
|
||||
|
||||
AddressBarDialog {
|
||||
id: addressBarDialog
|
||||
// The client area
|
||||
x: root.clientX
|
||||
y: root.clientY
|
||||
implicitWidth: 512
|
||||
implicitHeight: border.height + hifi.layout.spacing * 4
|
||||
|
||||
|
||||
Border {
|
||||
id: border
|
||||
height: 64
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: hifi.layout.spacing * 2
|
||||
anchors.right: goButton.left
|
||||
anchors.rightMargin: hifi.layout.spacing
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
TextInput {
|
||||
id: addressLine
|
||||
anchors.fill: parent
|
||||
helperText: "domain, location, @user, /x,y,z"
|
||||
anchors.margins: hifi.layout.spacing
|
||||
onAccepted: {
|
||||
event.accepted
|
||||
addressBarDialog.loadAddress(addressLine.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: goButton
|
||||
width: 32
|
||||
height: 32
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: hifi.layout.spacing * 2
|
||||
source: "../images/address-bar-submit.svg"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
parent.source = "../images/address-bar-submit-active.svg"
|
||||
addressBarDialog.loadAddress(addressLine.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
function toggleOrGo() {
|
||||
if (addressLine.text == "") {
|
||||
enabled = false
|
||||
} else {
|
||||
addressBarDialog.loadAddress(addressLine.text)
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: toggleOrGo()
|
||||
Keys.onEnterPressed: toggleOrGo()
|
||||
}
|
||||
|
30
interface/resources/qml/Browser.qml
Normal file
30
interface/resources/qml/Browser.qml
Normal file
|
@ -0,0 +1,30 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtWebKit 3.0
|
||||
import "controls"
|
||||
|
||||
Dialog {
|
||||
title: "Browser Window"
|
||||
id: testDialog
|
||||
objectName: "Browser"
|
||||
width: 1280
|
||||
height: 720
|
||||
|
||||
Item {
|
||||
id: clientArea
|
||||
// The client area
|
||||
anchors.fill: parent
|
||||
anchors.margins: parent.margins
|
||||
anchors.topMargin: parent.topMargin
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
WebView {
|
||||
id: webview
|
||||
url: "http://slashdot.org"
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
198
interface/resources/qml/LoginDialog.qml
Normal file
198
interface/resources/qml/LoginDialog.qml
Normal file
|
@ -0,0 +1,198 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
Dialog {
|
||||
HifiConstants { id: hifi }
|
||||
title: "Login"
|
||||
objectName: "LoginDialog"
|
||||
height: 512
|
||||
width: 384
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
onEnabledChanged: {
|
||||
if (enabled) {
|
||||
username.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
function reset() {
|
||||
username.text = ""
|
||||
password.text = ""
|
||||
loginDialog.statusText = ""
|
||||
}
|
||||
|
||||
LoginDialog {
|
||||
id: loginDialog
|
||||
anchors.fill: parent
|
||||
anchors.margins: parent.margins
|
||||
anchors.topMargin: parent.topMargin
|
||||
Column {
|
||||
anchors.topMargin: 8
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
spacing: 8
|
||||
|
||||
Image {
|
||||
height: 64
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: 64
|
||||
source: "../images/hifi-logo.svg"
|
||||
}
|
||||
|
||||
Border {
|
||||
width: 304
|
||||
height: 64
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
TextInput {
|
||||
id: username
|
||||
anchors.fill: parent
|
||||
helperText: "Username or Email"
|
||||
anchors.margins: 8
|
||||
KeyNavigation.tab: password
|
||||
KeyNavigation.backtab: password
|
||||
}
|
||||
}
|
||||
|
||||
Border {
|
||||
width: 304
|
||||
height: 64
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
TextInput {
|
||||
id: password
|
||||
anchors.fill: parent
|
||||
echoMode: TextInput.Password
|
||||
helperText: "Password"
|
||||
anchors.margins: 8
|
||||
KeyNavigation.tab: username
|
||||
KeyNavigation.backtab: username
|
||||
onFocusChanged: {
|
||||
if (password.focus) {
|
||||
password.selectAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
textFormat: Text.StyledText
|
||||
width: parent.width
|
||||
height: 96
|
||||
wrapMode: Text.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: loginDialog.statusText
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.bottomMargin: 5
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Rectangle {
|
||||
width: 192
|
||||
height: 64
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: hifi.colors.hifiBlue
|
||||
border.width: 0
|
||||
radius: 10
|
||||
|
||||
MouseArea {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
onClicked: {
|
||||
loginDialog.login(username.text, password.text)
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 8
|
||||
Image {
|
||||
id: loginIcon
|
||||
height: 32
|
||||
width: 32
|
||||
source: "../images/login.svg"
|
||||
}
|
||||
Text {
|
||||
text: "Login"
|
||||
color: "white"
|
||||
width: 64
|
||||
height: parent.height
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
height: 24
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text:"Create Account"
|
||||
font.pointSize: 12
|
||||
font.bold: true
|
||||
color: hifi.colors.hifiBlue
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
loginDialog.openUrl(loginDialog.rootUrl + "/signup")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
height: 24
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.pointSize: 12
|
||||
text: "Recover Password"
|
||||
color: hifi.colors.hifiBlue
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Keys.onPressed: {
|
||||
switch(event.key) {
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
if (username.activeFocus) {
|
||||
event.accepted = true
|
||||
password.forceActiveFocus()
|
||||
} else if (password.activeFocus) {
|
||||
event.accepted = true
|
||||
if (username.text == "") {
|
||||
username.forceActiveFocus()
|
||||
} else {
|
||||
loginDialog.login(username.text, password.text)
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
50
interface/resources/qml/MarketplaceDialog.qml
Normal file
50
interface/resources/qml/MarketplaceDialog.qml
Normal file
|
@ -0,0 +1,50 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import QtWebKit 3.0
|
||||
import "controls"
|
||||
|
||||
Dialog {
|
||||
title: "Test Dlg"
|
||||
id: testDialog
|
||||
objectName: "Browser"
|
||||
width: 720
|
||||
height: 720
|
||||
resizable: true
|
||||
|
||||
MarketplaceDialog {
|
||||
id: marketplaceDialog
|
||||
}
|
||||
|
||||
Item {
|
||||
id: clientArea
|
||||
// The client area
|
||||
anchors.fill: parent
|
||||
anchors.margins: parent.margins
|
||||
anchors.topMargin: parent.topMargin
|
||||
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
WebView {
|
||||
objectName: "WebView"
|
||||
id: webview
|
||||
url: "https://metaverse.highfidelity.com/marketplace"
|
||||
anchors.fill: parent
|
||||
onNavigationRequested: {
|
||||
console.log(request.url)
|
||||
if (!marketplaceDialog.navigationRequested(request.url)) {
|
||||
console.log("Application absorbed the request")
|
||||
request.action = WebView.IgnoreRequest;
|
||||
return;
|
||||
}
|
||||
console.log("Application passed on the request")
|
||||
request.action = WebView.AcceptRequest;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
333
interface/resources/qml/MessageDialog.qml
Normal file
333
interface/resources/qml/MessageDialog.qml
Normal file
|
@ -0,0 +1,333 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Dialogs 1.2
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
property real spacing: hifi.layout.spacing
|
||||
property real outerSpacing: hifi.layout.spacing * 2
|
||||
|
||||
destroyOnCloseButton: true
|
||||
destroyOnInvisible: true
|
||||
contentImplicitWidth: content.implicitWidth
|
||||
contentImplicitHeight: content.implicitHeight
|
||||
|
||||
Component.onCompleted: {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
onParentChanged: {
|
||||
if (visible && enabled) {
|
||||
forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Hifi.MessageDialog {
|
||||
id: content
|
||||
clip: true
|
||||
|
||||
x: root.clientX
|
||||
y: root.clientY
|
||||
implicitHeight: contentColumn.implicitHeight + outerSpacing * 2
|
||||
implicitWidth: mainText.implicitWidth + outerSpacing * 2
|
||||
|
||||
Component.onCompleted: {
|
||||
root.title = title
|
||||
}
|
||||
|
||||
onTitleChanged: {
|
||||
root.title = title
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
id: contentColumn
|
||||
spacing: root.outerSpacing
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Math.max(icon.height, mainText.height + informativeText.height + root.spacing)
|
||||
|
||||
FontAwesome {
|
||||
id: icon
|
||||
width: content.icon ? 48 : 0
|
||||
height: content.icon ? 48 : 0
|
||||
visible: content.icon ? true : false
|
||||
font.pixelSize: 48
|
||||
verticalAlignment: Text.AlignTop
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
color: iconColor()
|
||||
text: iconSymbol()
|
||||
|
||||
function iconSymbol() {
|
||||
switch (content.icon) {
|
||||
case Hifi.MessageDialog.Information:
|
||||
return "\uF05A"
|
||||
case Hifi.MessageDialog.Question:
|
||||
return "\uF059"
|
||||
case Hifi.MessageDialog.Warning:
|
||||
return "\uF071"
|
||||
case Hifi.MessageDialog.Critical:
|
||||
return "\uF057"
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return content.icon;
|
||||
}
|
||||
function iconColor() {
|
||||
switch (content.icon) {
|
||||
case Hifi.MessageDialog.Information:
|
||||
case Hifi.MessageDialog.Question:
|
||||
return "blue"
|
||||
case Hifi.MessageDialog.Warning:
|
||||
case Hifi.MessageDialog.Critical:
|
||||
return "red"
|
||||
default:
|
||||
break
|
||||
}
|
||||
return "black"
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors {
|
||||
left: icon.right
|
||||
leftMargin: root.spacing
|
||||
right: parent.right
|
||||
}
|
||||
text: content.text
|
||||
font.pointSize: 14
|
||||
font.weight: Font.Bold
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Text {
|
||||
id: informativeText
|
||||
anchors {
|
||||
left: icon.right
|
||||
right: parent.right
|
||||
top: mainText.bottom
|
||||
leftMargin: root.spacing
|
||||
topMargin: root.spacing
|
||||
}
|
||||
text: content.informativeText
|
||||
font.pointSize: 14
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Flow {
|
||||
id: buttons
|
||||
spacing: root.spacing
|
||||
layoutDirection: Qt.RightToLeft
|
||||
width: parent.width
|
||||
Button {
|
||||
id: okButton
|
||||
text: qsTr("OK")
|
||||
onClicked: content.click(StandardButton.Ok)
|
||||
visible: content.standardButtons & StandardButton.Ok
|
||||
}
|
||||
Button {
|
||||
id: openButton
|
||||
text: qsTr("Open")
|
||||
onClicked: content.click(StandardButton.Open)
|
||||
visible: content.standardButtons & StandardButton.Open
|
||||
}
|
||||
Button {
|
||||
id: saveButton
|
||||
text: qsTr("Save")
|
||||
onClicked: content.click(StandardButton.Save)
|
||||
visible: content.standardButtons & StandardButton.Save
|
||||
}
|
||||
Button {
|
||||
id: saveAllButton
|
||||
text: qsTr("Save All")
|
||||
onClicked: content.click(StandardButton.SaveAll)
|
||||
visible: content.standardButtons & StandardButton.SaveAll
|
||||
}
|
||||
Button {
|
||||
id: retryButton
|
||||
text: qsTr("Retry")
|
||||
onClicked: content.click(StandardButton.Retry)
|
||||
visible: content.standardButtons & StandardButton.Retry
|
||||
}
|
||||
Button {
|
||||
id: ignoreButton
|
||||
text: qsTr("Ignore")
|
||||
onClicked: content.click(StandardButton.Ignore)
|
||||
visible: content.standardButtons & StandardButton.Ignore
|
||||
}
|
||||
Button {
|
||||
id: applyButton
|
||||
text: qsTr("Apply")
|
||||
onClicked: content.click(StandardButton.Apply)
|
||||
visible: content.standardButtons & StandardButton.Apply
|
||||
}
|
||||
Button {
|
||||
id: yesButton
|
||||
text: qsTr("Yes")
|
||||
onClicked: content.click(StandardButton.Yes)
|
||||
visible: content.standardButtons & StandardButton.Yes
|
||||
}
|
||||
Button {
|
||||
id: yesAllButton
|
||||
text: qsTr("Yes to All")
|
||||
onClicked: content.click(StandardButton.YesToAll)
|
||||
visible: content.standardButtons & StandardButton.YesToAll
|
||||
}
|
||||
Button {
|
||||
id: noButton
|
||||
text: qsTr("No")
|
||||
onClicked: content.click(StandardButton.No)
|
||||
visible: content.standardButtons & StandardButton.No
|
||||
}
|
||||
Button {
|
||||
id: noAllButton
|
||||
text: qsTr("No to All")
|
||||
onClicked: content.click(StandardButton.NoToAll)
|
||||
visible: content.standardButtons & StandardButton.NoToAll
|
||||
}
|
||||
Button {
|
||||
id: discardButton
|
||||
text: qsTr("Discard")
|
||||
onClicked: content.click(StandardButton.Discard)
|
||||
visible: content.standardButtons & StandardButton.Discard
|
||||
}
|
||||
Button {
|
||||
id: resetButton
|
||||
text: qsTr("Reset")
|
||||
onClicked: content.click(StandardButton.Reset)
|
||||
visible: content.standardButtons & StandardButton.Reset
|
||||
}
|
||||
Button {
|
||||
id: restoreDefaultsButton
|
||||
text: qsTr("Restore Defaults")
|
||||
onClicked: content.click(StandardButton.RestoreDefaults)
|
||||
visible: content.standardButtons & StandardButton.RestoreDefaults
|
||||
}
|
||||
Button {
|
||||
id: cancelButton
|
||||
text: qsTr("Cancel")
|
||||
onClicked: content.click(StandardButton.Cancel)
|
||||
visible: content.standardButtons & StandardButton.Cancel
|
||||
}
|
||||
Button {
|
||||
id: abortButton
|
||||
text: qsTr("Abort")
|
||||
onClicked: content.click(StandardButton.Abort)
|
||||
visible: content.standardButtons & StandardButton.Abort
|
||||
}
|
||||
Button {
|
||||
id: closeButton
|
||||
text: qsTr("Close")
|
||||
onClicked: content.click(StandardButton.Close)
|
||||
visible: content.standardButtons & StandardButton.Close
|
||||
}
|
||||
Button {
|
||||
id: moreButton
|
||||
text: qsTr("Show Details...")
|
||||
onClicked: content.state = (content.state === "" ? "expanded" : "")
|
||||
visible: content.detailedText.length > 0
|
||||
}
|
||||
Button {
|
||||
id: helpButton
|
||||
text: qsTr("Help")
|
||||
onClicked: content.click(StandardButton.Help)
|
||||
visible: content.standardButtons & StandardButton.Help
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: details
|
||||
width: parent.width
|
||||
implicitHeight: detailedText.implicitHeight + root.spacing
|
||||
height: 0
|
||||
clip: true
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: contentColumn.bottom
|
||||
topMargin: root.spacing
|
||||
leftMargin: root.outerSpacing
|
||||
rightMargin: root.outerSpacing
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: flickable
|
||||
contentHeight: detailedText.height
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: root.spacing
|
||||
anchors.bottomMargin: root.outerSpacing
|
||||
TextEdit {
|
||||
id: detailedText
|
||||
text: content.detailedText
|
||||
width: details.width
|
||||
wrapMode: Text.WordWrap
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "expanded"
|
||||
PropertyChanges {
|
||||
target: details
|
||||
height: root.height - contentColumn.height - root.spacing - root.outerSpacing
|
||||
}
|
||||
PropertyChanges {
|
||||
target: content
|
||||
implicitHeight: contentColumn.implicitHeight + root.spacing * 2 +
|
||||
detailedText.implicitHeight + root.outerSpacing * 2
|
||||
}
|
||||
PropertyChanges {
|
||||
target: moreButton
|
||||
text: qsTr("Hide Details")
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.modifiers === Qt.ControlModifier)
|
||||
switch (event.key) {
|
||||
case Qt.Key_A:
|
||||
event.accepted = true
|
||||
detailedText.selectAll()
|
||||
break
|
||||
case Qt.Key_C:
|
||||
event.accepted = true
|
||||
detailedText.copy()
|
||||
break
|
||||
case Qt.Key_Period:
|
||||
if (Qt.platform.os === "osx") {
|
||||
event.accepted = true
|
||||
content.reject()
|
||||
}
|
||||
break
|
||||
} else switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
event.accepted = true
|
||||
content.reject()
|
||||
break
|
||||
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
event.accepted = true
|
||||
content.accept()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
150
interface/resources/qml/Palettes.qml
Normal file
150
interface/resources/qml/Palettes.qml
Normal file
|
@ -0,0 +1,150 @@
|
|||
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}
|
||||
}
|
||||
}
|
||||
}
|
14
interface/resources/qml/Root.qml
Normal file
14
interface/resources/qml/Root.qml
Normal file
|
@ -0,0 +1,14 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
|
||||
// This is our primary 'window' object to which all dialogs and controls will
|
||||
// be childed.
|
||||
Root {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
|
||||
onParentChanged: {
|
||||
forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
9
interface/resources/qml/RootMenu.qml
Normal file
9
interface/resources/qml/RootMenu.qml
Normal file
|
@ -0,0 +1,9 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls 1.3
|
||||
|
||||
Item {
|
||||
Menu {
|
||||
id: root
|
||||
objectName: "rootMenu"
|
||||
}
|
||||
}
|
94
interface/resources/qml/TestDialog.qml
Normal file
94
interface/resources/qml/TestDialog.qml
Normal file
|
@ -0,0 +1,94 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "controls"
|
||||
|
||||
Dialog {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
115
interface/resources/qml/TestMenu.qml
Normal file
115
interface/resources/qml/TestMenu.qml
Normal file
|
@ -0,0 +1,115 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls 1.3
|
||||
import Hifi 1.0
|
||||
|
||||
// Currently for testing a pure QML replacement menu
|
||||
Item {
|
||||
Item {
|
||||
objectName: "AllActions"
|
||||
Action {
|
||||
id: aboutApp
|
||||
objectName: "HifiAction_" + MenuConstants.AboutApp
|
||||
text: qsTr("About Interface")
|
||||
}
|
||||
|
||||
//
|
||||
// File Menu
|
||||
//
|
||||
Action {
|
||||
id: login
|
||||
objectName: "HifiAction_" + MenuConstants.Login
|
||||
text: qsTr("Login")
|
||||
}
|
||||
Action {
|
||||
id: quit
|
||||
objectName: "HifiAction_" + MenuConstants.Quit
|
||||
text: qsTr("Quit")
|
||||
//shortcut: StandardKey.Quit
|
||||
shortcut: "Ctrl+Q"
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Edit menu
|
||||
//
|
||||
Action {
|
||||
id: undo
|
||||
text: "Undo"
|
||||
shortcut: StandardKey.Undo
|
||||
}
|
||||
|
||||
Action {
|
||||
id: redo
|
||||
text: "Redo"
|
||||
shortcut: StandardKey.Redo
|
||||
}
|
||||
|
||||
Action {
|
||||
id: animations
|
||||
objectName: "HifiAction_" + MenuConstants.Animations
|
||||
text: qsTr("Animations...")
|
||||
}
|
||||
Action {
|
||||
id: attachments
|
||||
text: qsTr("Attachments...")
|
||||
}
|
||||
Action {
|
||||
id: explode
|
||||
text: qsTr("Explode on quit")
|
||||
checkable: true
|
||||
checked: true
|
||||
}
|
||||
Action {
|
||||
id: freeze
|
||||
text: qsTr("Freeze on quit")
|
||||
checkable: true
|
||||
checked: false
|
||||
}
|
||||
ExclusiveGroup {
|
||||
Action {
|
||||
id: visibleToEveryone
|
||||
objectName: "HifiAction_" + MenuConstants.VisibleToEveryone
|
||||
text: qsTr("Everyone")
|
||||
checkable: true
|
||||
checked: true
|
||||
}
|
||||
Action {
|
||||
id: visibleToFriends
|
||||
objectName: "HifiAction_" + MenuConstants.VisibleToFriends
|
||||
text: qsTr("Friends")
|
||||
checkable: true
|
||||
}
|
||||
Action {
|
||||
id: visibleToNoOne
|
||||
objectName: "HifiAction_" + MenuConstants.VisibleToNoOne
|
||||
text: qsTr("No one")
|
||||
checkable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
objectName: "rootMenu";
|
||||
Menu {
|
||||
title: "File"
|
||||
MenuItem { action: login }
|
||||
MenuItem { action: explode }
|
||||
MenuItem { action: freeze }
|
||||
MenuItem { action: quit }
|
||||
}
|
||||
Menu {
|
||||
title: "Tools"
|
||||
Menu {
|
||||
title: "I Am Visible To"
|
||||
MenuItem { action: visibleToEveryone }
|
||||
MenuItem { action: visibleToFriends }
|
||||
MenuItem { action: visibleToNoOne }
|
||||
}
|
||||
MenuItem { action: animations }
|
||||
}
|
||||
Menu {
|
||||
title: "Help"
|
||||
MenuItem { action: aboutApp }
|
||||
}
|
||||
}
|
||||
}
|
43
interface/resources/qml/TestRoot.qml
Normal file
43
interface/resources/qml/TestRoot.qml
Normal file
|
@ -0,0 +1,43 @@
|
|||
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")
|
||||
}
|
||||
}
|
||||
|
326
interface/resources/qml/VrMenu.qml
Normal file
326
interface/resources/qml/VrMenu.qml
Normal file
|
@ -0,0 +1,326 @@
|
|||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.4
|
||||
import QtQuick.Controls 1.3
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
Hifi.VrMenu {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
objectName: "VrMenu"
|
||||
|
||||
enabled: false
|
||||
opacity: 0.0
|
||||
|
||||
property int animationDuration: 200
|
||||
property var models: []
|
||||
property var columns: []
|
||||
|
||||
z: 10000
|
||||
|
||||
onEnabledChanged: {
|
||||
if (enabled && columns.length == 0) {
|
||||
pushColumn(rootMenu.items);
|
||||
}
|
||||
opacity = enabled ? 1.0 : 0.0
|
||||
if (enabled) {
|
||||
forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
// The actual animator
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: Easing.InOutBounce
|
||||
}
|
||||
}
|
||||
|
||||
onOpacityChanged: {
|
||||
visible = (opacity != 0.0);
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) reset();
|
||||
}
|
||||
|
||||
property var menuBuilder: Component {
|
||||
Border {
|
||||
HifiConstants { id: hifi }
|
||||
Component.onCompleted: {
|
||||
menuDepth = root.models.length - 1
|
||||
if (menuDepth == 0) {
|
||||
x = lastMousePosition.x - 20
|
||||
y = lastMousePosition.y - 20
|
||||
} else {
|
||||
var lastColumn = root.columns[menuDepth - 1]
|
||||
x = lastColumn.x + 64;
|
||||
y = lastMousePosition.y - height / 2;
|
||||
}
|
||||
}
|
||||
border.color: hifi.colors.hifiBlue
|
||||
color: hifi.colors.window
|
||||
property int menuDepth
|
||||
implicitHeight: listView.implicitHeight + 16
|
||||
implicitWidth: listView.implicitWidth + 16
|
||||
|
||||
Column {
|
||||
id: listView
|
||||
property real minWidth: 0
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 8
|
||||
left: parent.left
|
||||
leftMargin: 8
|
||||
right: parent.right
|
||||
rightMargin: 8
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.models[menuDepth]
|
||||
delegate: Loader {
|
||||
id: loader
|
||||
sourceComponent: root.itemBuilder
|
||||
Binding {
|
||||
target: loader.item
|
||||
property: "root"
|
||||
value: root
|
||||
when: loader.status == Loader.Ready
|
||||
}
|
||||
Binding {
|
||||
target: loader.item
|
||||
property: "source"
|
||||
value: modelData
|
||||
when: loader.status == Loader.Ready
|
||||
}
|
||||
Binding {
|
||||
target: loader.item
|
||||
property: "border"
|
||||
value: listView.parent
|
||||
when: loader.status == Loader.Ready
|
||||
}
|
||||
Binding {
|
||||
target: loader.item
|
||||
property: "listView"
|
||||
value: listView
|
||||
when: loader.status == Loader.Ready
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property var itemBuilder: Component {
|
||||
Item {
|
||||
property var source
|
||||
property var root
|
||||
property var listView
|
||||
property var border
|
||||
implicitHeight: row.implicitHeight + 4
|
||||
implicitWidth: row.implicitWidth + label.height
|
||||
// FIXME uncommenting this line results in menus that have blank spots
|
||||
// rather than having the correct size
|
||||
// visible: source.visible
|
||||
Row {
|
||||
id: row
|
||||
spacing: 2
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 2
|
||||
}
|
||||
Spacer { size: 4 }
|
||||
FontAwesome {
|
||||
id: check
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
y: 2
|
||||
size: label.height
|
||||
text: checkText()
|
||||
color: label.color
|
||||
function checkText() {
|
||||
if (!source || source.type != 1 || !source.checkable) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// FIXME this works for native QML menus but I don't think it will
|
||||
// for proxied QML menus
|
||||
if (source.exclusiveGroup) {
|
||||
return source.checked ? "\uF05D" : "\uF10C"
|
||||
}
|
||||
return source.checked ? "\uF046" : "\uF096"
|
||||
}
|
||||
}
|
||||
Text {
|
||||
id: label
|
||||
text: typedText()
|
||||
color: source.enabled ? hifi.colors.text : hifi.colors.disabledText
|
||||
enabled: source.enabled && source.visible
|
||||
function typedText() {
|
||||
if (source) {
|
||||
switch(source.type) {
|
||||
case 2:
|
||||
return source.title;
|
||||
case 1:
|
||||
return source.text;
|
||||
case 0:
|
||||
return "-----"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
} // row
|
||||
|
||||
FontAwesome {
|
||||
anchors {
|
||||
top: row.top
|
||||
}
|
||||
id: tag
|
||||
size: label.height
|
||||
width: implicitWidth
|
||||
visible: source.type == 2
|
||||
x: listView.width - width - 4
|
||||
text: "\uF0DA"
|
||||
color: label.color
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: tag.right
|
||||
rightMargin: -4
|
||||
}
|
||||
acceptedButtons: Qt.LeftButton
|
||||
hoverEnabled: true
|
||||
Rectangle {
|
||||
id: highlight
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
color: "#7f0e7077"
|
||||
}
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 1000
|
||||
onTriggered: parent.select();
|
||||
}
|
||||
onEntered: {
|
||||
/*
|
||||
* Uncomment below to have menus auto-popup
|
||||
*
|
||||
* FIXME if we enabled timer based menu popup, either the timer has
|
||||
* to be very very short or after auto popup there has to be a small
|
||||
* amount of time, or a test if the mouse has moved before a click
|
||||
* will be accepted, otherwise it's too easy to accidently click on
|
||||
* something immediately after the auto-popup appears underneath your
|
||||
* cursor
|
||||
*
|
||||
*/
|
||||
//if (source.type == 2 && enabled) {
|
||||
// timer.start()
|
||||
//}
|
||||
highlight.visible = source.enabled
|
||||
}
|
||||
onExited: {
|
||||
timer.stop()
|
||||
highlight.visible = false
|
||||
}
|
||||
onClicked: {
|
||||
select();
|
||||
}
|
||||
function select() {
|
||||
//timer.stop();
|
||||
var popped = false;
|
||||
while (columns.length - 1 > listView.parent.menuDepth) {
|
||||
popColumn();
|
||||
popped = true;
|
||||
}
|
||||
|
||||
if (!popped || source.type != 1) {
|
||||
parent.root.selectItem(parent.source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lastColumn() {
|
||||
return columns[root.columns.length - 1];
|
||||
}
|
||||
|
||||
function pushColumn(items) {
|
||||
models.push(items)
|
||||
if (columns.length) {
|
||||
var oldColumn = lastColumn();
|
||||
//oldColumn.enabled = false
|
||||
}
|
||||
var newColumn = menuBuilder.createObject(root);
|
||||
columns.push(newColumn);
|
||||
newColumn.forceActiveFocus();
|
||||
}
|
||||
|
||||
function popColumn() {
|
||||
if (columns.length > 0) {
|
||||
var curColumn = columns.pop();
|
||||
curColumn.visible = false;
|
||||
curColumn.destroy();
|
||||
models.pop();
|
||||
}
|
||||
|
||||
if (columns.length == 0) {
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
curColumn = lastColumn();
|
||||
curColumn.enabled = true;
|
||||
curColumn.opacity = 1.0;
|
||||
curColumn.forceActiveFocus();
|
||||
}
|
||||
|
||||
function selectItem(source) {
|
||||
switch (source.type) {
|
||||
case 2:
|
||||
pushColumn(source.items)
|
||||
break;
|
||||
case 1:
|
||||
source.trigger()
|
||||
enabled = false
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function reset() {
|
||||
while (columns.length > 0) {
|
||||
popColumn();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
root.popColumn()
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
id: mouseArea
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: {
|
||||
if (mouse.button == Qt.RightButton) {
|
||||
root.popColumn();
|
||||
} else {
|
||||
root.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
interface/resources/qml/controls/Button.qml
Normal file
9
interface/resources/qml/controls/Button.qml
Normal file
|
@ -0,0 +1,9 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.3 as Original
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "."
|
||||
import "../styles"
|
||||
|
||||
Original.Button {
|
||||
style: ButtonStyle { }
|
||||
}
|
16
interface/resources/qml/controls/CheckBox.qml
Normal file
16
interface/resources/qml/controls/CheckBox.qml
Normal file
|
@ -0,0 +1,16 @@
|
|||
import QtQuick.Controls 1.3 as Original
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import "../styles"
|
||||
import "."
|
||||
Original.CheckBox {
|
||||
text: "Check Box"
|
||||
style: CheckBoxStyle {
|
||||
indicator: FontAwesome {
|
||||
text: control.checked ? "\uf046" : "\uf096"
|
||||
}
|
||||
label: Text {
|
||||
text: control.text
|
||||
}
|
||||
}
|
||||
|
||||
}
|
158
interface/resources/qml/controls/Dialog.qml
Normal file
158
interface/resources/qml/controls/Dialog.qml
Normal file
|
@ -0,0 +1,158 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import "."
|
||||
import "../styles"
|
||||
|
||||
/*
|
||||
* FIXME Need to create a client property here so that objects can be
|
||||
* placed in it without having to think about positioning within the outer
|
||||
* window.
|
||||
*
|
||||
* Examine the QML ApplicationWindow.qml source for how it does this
|
||||
*
|
||||
*/
|
||||
DialogBase {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
// FIXME better placement via a window manager
|
||||
x: parent ? parent.width / 2 - width / 2 : 0
|
||||
y: parent ? parent.height / 2 - height / 2 : 0
|
||||
|
||||
property bool destroyOnInvisible: false
|
||||
property bool destroyOnCloseButton: true
|
||||
property bool resizable: false
|
||||
|
||||
property int animationDuration: hifi.effects.fadeInDuration
|
||||
property int minX: 256
|
||||
property int minY: 256
|
||||
readonly property int topMargin: root.height - clientBorder.height + hifi.layout.spacing
|
||||
|
||||
/*
|
||||
* Support for animating the dialog in and out.
|
||||
*/
|
||||
enabled: false
|
||||
scale: 0.0
|
||||
|
||||
// The offscreen UI will enable an object, rather than manipulating it's
|
||||
// visibility, so that we can do animations in both directions. Because
|
||||
// visibility and enabled are boolean flags, they cannot be animated. So when
|
||||
// enabled is change, we modify a property that can be animated, like scale or
|
||||
// opacity, and then when the target animation value is reached, we can
|
||||
// modify the visibility
|
||||
onEnabledChanged: {
|
||||
scale = enabled ? 1.0 : 0.0
|
||||
}
|
||||
|
||||
// The actual animator
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: Easing.InOutBounce
|
||||
}
|
||||
}
|
||||
|
||||
// Once we're scaled to 0, disable the dialog's visibility
|
||||
onScaleChanged: {
|
||||
visible = (scale != 0.0);
|
||||
}
|
||||
|
||||
// Some dialogs should be destroyed when they become invisible,
|
||||
// so handle that
|
||||
onVisibleChanged: {
|
||||
if (!visible && destroyOnInvisible) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// our close function performs the same way as the OffscreenUI class:
|
||||
// don't do anything but manipulate the enabled flag and let the other
|
||||
// mechanisms decide if the window should be destoryed after the close
|
||||
// animation completes
|
||||
function close() {
|
||||
if (destroyOnCloseButton) {
|
||||
destroyOnInvisible = true
|
||||
}
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resize support
|
||||
*/
|
||||
function deltaSize(dx, dy) {
|
||||
width = Math.max(width + dx, minX)
|
||||
height = Math.max(height + dy, minY)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: sizeDrag
|
||||
enabled: root.resizable
|
||||
property int startX
|
||||
property int startY
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
width: 16
|
||||
height: 16
|
||||
z: 1000
|
||||
hoverEnabled: true
|
||||
onPressed: {
|
||||
startX = mouseX
|
||||
startY = mouseY
|
||||
}
|
||||
onPositionChanged: {
|
||||
if (pressed) {
|
||||
root.deltaSize((mouseX - startX), (mouseY - startY))
|
||||
startX = mouseX
|
||||
startY = mouseY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: titleDrag
|
||||
x: root.titleX
|
||||
y: root.titleY
|
||||
width: root.titleWidth
|
||||
height: root.titleHeight
|
||||
|
||||
drag {
|
||||
target: root
|
||||
minimumX: 0
|
||||
minimumY: 0
|
||||
maximumX: root.parent ? root.parent.width - root.width : 0
|
||||
maximumY: root.parent ? root.parent.height - root.height : 0
|
||||
}
|
||||
|
||||
Row {
|
||||
id: windowControls
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: hifi.layout.spacing
|
||||
FontAwesome {
|
||||
id: icon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: root.titleHeight - hifi.layout.spacing * 2
|
||||
color: "red"
|
||||
text: "\uf00d"
|
||||
MouseArea {
|
||||
anchors.margins: hifi.layout.spacing / 2
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch(event.key) {
|
||||
case Qt.Key_W:
|
||||
if (event.modifiers == Qt.ControlModifier) {
|
||||
event.accepted = true
|
||||
enabled = false
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
98
interface/resources/qml/controls/DialogBase.qml
Normal file
98
interface/resources/qml/controls/DialogBase.qml
Normal file
|
@ -0,0 +1,98 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import "."
|
||||
import "../styles"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
implicitHeight: contentImplicitHeight + titleBorder.height + hifi.styles.borderWidth
|
||||
implicitWidth: contentImplicitWidth + hifi.styles.borderWidth * 2
|
||||
property string title
|
||||
property int titleSize: titleBorder.height + 12
|
||||
property string frameColor: hifi.colors.hifiBlue
|
||||
property string backgroundColor: hifi.colors.dialogBackground
|
||||
property bool active: false
|
||||
property real contentImplicitWidth: 800
|
||||
property real contentImplicitHeight: 800
|
||||
|
||||
property alias titleBorder: titleBorder
|
||||
readonly property alias titleX: titleBorder.x
|
||||
readonly property alias titleY: titleBorder.y
|
||||
readonly property alias titleWidth: titleBorder.width
|
||||
readonly property alias titleHeight: titleBorder.height
|
||||
|
||||
// readonly property real borderWidth: hifi.styles.borderWidth
|
||||
readonly property real borderWidth: 0
|
||||
property alias clientBorder: clientBorder
|
||||
readonly property real clientX: clientBorder.x + borderWidth
|
||||
readonly property real clientY: clientBorder.y + borderWidth
|
||||
readonly property real clientWidth: clientBorder.width - borderWidth * 2
|
||||
readonly property real clientHeight: clientBorder.height - borderWidth * 2
|
||||
|
||||
/*
|
||||
* Window decorations, with a title bar and frames
|
||||
*/
|
||||
Border {
|
||||
id: windowBorder
|
||||
anchors.fill: parent
|
||||
border.color: root.frameColor
|
||||
border.width: 0
|
||||
color: "#00000000"
|
||||
|
||||
Border {
|
||||
id: titleBorder
|
||||
height: hifi.layout.windowTitleHeight
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
border.color: root.frameColor
|
||||
border.width: 0
|
||||
clip: true
|
||||
color: root.active ?
|
||||
hifi.colors.activeWindow.headerBackground :
|
||||
hifi.colors.inactiveWindow.headerBackground
|
||||
|
||||
|
||||
Text {
|
||||
id: titleText
|
||||
color: root.active ?
|
||||
hifi.colors.activeWindow.headerText :
|
||||
hifi.colors.inactiveWindow.headerText
|
||||
text: root.title
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.fill: parent
|
||||
}
|
||||
} // header border
|
||||
|
||||
// These two rectangles hide the curves between the title area
|
||||
// and the client area
|
||||
Rectangle {
|
||||
y: titleBorder.height - titleBorder.radius
|
||||
height: titleBorder.radius
|
||||
width: titleBorder.width
|
||||
color: titleBorder.color
|
||||
visible: borderWidth == 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
y: titleBorder.height
|
||||
width: clientBorder.width
|
||||
height: clientBorder.radius
|
||||
color: clientBorder.color
|
||||
}
|
||||
|
||||
Border {
|
||||
id: clientBorder
|
||||
border.width: 0
|
||||
border.color: root.frameColor
|
||||
color: root.backgroundColor
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: titleBorder.bottom
|
||||
anchors.topMargin: -titleBorder.border.width
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
} // client border
|
||||
} // window border
|
||||
|
||||
}
|
16
interface/resources/qml/controls/FontAwesome.qml
Normal file
16
interface/resources/qml/controls/FontAwesome.qml
Normal file
|
@ -0,0 +1,16 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.3
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
|
||||
Text {
|
||||
id: root
|
||||
FontLoader { id: iconFont; source: "../../fonts/fontawesome-webfont.ttf"; }
|
||||
property int size: 32
|
||||
width: size
|
||||
height: size
|
||||
font.pixelSize: size
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font.family: iconFont.name
|
||||
}
|
||||
|
2
interface/resources/qml/controls/README.md
Normal file
2
interface/resources/qml/controls/README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
These are our own custom controls with the same names as existing controls, but
|
||||
customized for readability / usability in VR.
|
8
interface/resources/qml/controls/Slider.qml
Normal file
8
interface/resources/qml/controls/Slider.qml
Normal file
|
@ -0,0 +1,8 @@
|
|||
import QtQuick.Controls 1.3 as Original
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
|
||||
import "../styles"
|
||||
import "."
|
||||
|
||||
Original.Slider {
|
||||
}
|
11
interface/resources/qml/controls/Spacer.qml
Normal file
11
interface/resources/qml/controls/Spacer.qml
Normal file
|
@ -0,0 +1,11 @@
|
|||
import QtQuick 2.4
|
||||
import "../styles"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
property real size: hifi.layout.spacing
|
||||
property real multiplier: 1.0
|
||||
height: size * multiplier
|
||||
width: size * multiplier
|
||||
}
|
15
interface/resources/qml/controls/SpinBox.qml
Normal file
15
interface/resources/qml/controls/SpinBox.qml
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
import QtQuick.Controls 1.3 as Original
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
|
||||
import "../styles"
|
||||
import "."
|
||||
|
||||
Original.SpinBox {
|
||||
style: SpinBoxStyle {
|
||||
HifiConstants { id: hifi }
|
||||
font.family: hifi.fonts.fontFamily
|
||||
font.pointSize: hifi.fonts.fontSize
|
||||
}
|
||||
|
||||
}
|
9
interface/resources/qml/controls/Text.qml
Normal file
9
interface/resources/qml/controls/Text.qml
Normal file
|
@ -0,0 +1,9 @@
|
|||
import QtQuick 2.3 as Original
|
||||
import "../styles"
|
||||
|
||||
Original.Text {
|
||||
HifiConstants { id: hifi }
|
||||
font.family: hifi.fonts.fontFamily
|
||||
font.pointSize: hifi.fonts.fontSize
|
||||
}
|
||||
|
24
interface/resources/qml/controls/TextAndSlider.qml
Normal file
24
interface/resources/qml/controls/TextAndSlider.qml
Normal file
|
@ -0,0 +1,24 @@
|
|||
import QtQuick 2.3 as Original
|
||||
import "../styles"
|
||||
import "."
|
||||
|
||||
Original.Item {
|
||||
property alias text: label.text
|
||||
property alias value: slider.value
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
verticalAlignment: Original.Text.AlignVCenter
|
||||
}
|
||||
|
||||
Slider {
|
||||
id: slider
|
||||
width: 120
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
}
|
26
interface/resources/qml/controls/TextAndSpinBox.qml
Normal file
26
interface/resources/qml/controls/TextAndSpinBox.qml
Normal file
|
@ -0,0 +1,26 @@
|
|||
import QtQuick 2.3 as Original
|
||||
import "../styles"
|
||||
import "."
|
||||
|
||||
Original.Item {
|
||||
property alias text: label.text
|
||||
property alias value: spinBox.value
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
verticalAlignment: Original.Text.AlignVCenter
|
||||
text: "Minimum HMD FPS"
|
||||
}
|
||||
SpinBox {
|
||||
id: spinBox
|
||||
width: 120
|
||||
maximumValue: 240
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
|
||||
}
|
9
interface/resources/qml/controls/TextArea.qml
Normal file
9
interface/resources/qml/controls/TextArea.qml
Normal file
|
@ -0,0 +1,9 @@
|
|||
import QtQuick.Controls 2.3 as Original
|
||||
import "../styles"
|
||||
|
||||
Original.TextArea {
|
||||
HifiConstants { id: hifi }
|
||||
font.family: hifi.fonts.fontFamily
|
||||
font.pointSize: hifi.fonts.fontSize
|
||||
}
|
||||
|
9
interface/resources/qml/controls/TextEdit.qml
Normal file
9
interface/resources/qml/controls/TextEdit.qml
Normal file
|
@ -0,0 +1,9 @@
|
|||
import QtQuick 2.3 as Original
|
||||
import "../styles"
|
||||
|
||||
Original.TextEdit {
|
||||
HifiConstants { id: hifi }
|
||||
font.family: hifi.fonts.fontFamily
|
||||
font.pointSize: hifi.fonts.fontSize
|
||||
}
|
||||
|
9
interface/resources/qml/controls/TextHeader.qml
Normal file
9
interface/resources/qml/controls/TextHeader.qml
Normal file
|
@ -0,0 +1,9 @@
|
|||
import "."
|
||||
import "../styles"
|
||||
|
||||
Text {
|
||||
HifiConstants { id: hifi }
|
||||
color: hifi.colors.hifiBlue
|
||||
font.pointSize: hifi.fonts.headerPointSize
|
||||
font.bold: true
|
||||
}
|
36
interface/resources/qml/controls/TextInput.qml
Normal file
36
interface/resources/qml/controls/TextInput.qml
Normal file
|
@ -0,0 +1,36 @@
|
|||
import QtQuick 2.3 as Original
|
||||
import "../styles"
|
||||
import "."
|
||||
|
||||
Original.TextInput {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
property string helperText
|
||||
height: hifi.layout.rowHeight
|
||||
clip: true
|
||||
color: hifi.colors.text
|
||||
verticalAlignment: Original.TextInput.AlignVCenter
|
||||
font.family: hifi.fonts.fontFamily
|
||||
font.pointSize: hifi.fonts.fontSize
|
||||
|
||||
/*
|
||||
Original.Rectangle {
|
||||
// Render the rectangle as background
|
||||
z: -1
|
||||
anchors.fill: parent
|
||||
color: hifi.colors.inputBackground
|
||||
}
|
||||
*/
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
font.pointSize: parent.font.pointSize
|
||||
font.family: parent.font.family
|
||||
verticalAlignment: parent.verticalAlignment
|
||||
horizontalAlignment: parent.horizontalAlignment
|
||||
text: root.helperText
|
||||
color: hifi.colors.hintText
|
||||
visible: !root.text
|
||||
}
|
||||
}
|
||||
|
||||
|
40
interface/resources/qml/controls/TextInputAndButton.qml
Normal file
40
interface/resources/qml/controls/TextInputAndButton.qml
Normal file
|
@ -0,0 +1,40 @@
|
|||
import QtQuick 2.3 as Original
|
||||
import "../styles"
|
||||
import "."
|
||||
|
||||
Original.Item {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
height: hifi.layout.rowHeight
|
||||
property string text
|
||||
property string helperText
|
||||
property string buttonText
|
||||
property int buttonWidth: 0
|
||||
property alias input: input
|
||||
property alias button: button
|
||||
signal clicked()
|
||||
|
||||
TextInput {
|
||||
id: input
|
||||
text: root.text
|
||||
helperText: root.helperText
|
||||
anchors.left: parent.left
|
||||
anchors.right: button.left
|
||||
anchors.rightMargin: 8
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
}
|
||||
|
||||
Button {
|
||||
id: button
|
||||
clip: true
|
||||
width: root.buttonWidth ? root.buttonWidth : implicitWidth
|
||||
text: root.buttonText
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
|
12
interface/resources/qml/styles/Border.qml
Normal file
12
interface/resources/qml/styles/Border.qml
Normal file
|
@ -0,0 +1,12 @@
|
|||
import QtQuick 2.3
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi }
|
||||
implicitHeight: 64
|
||||
implicitWidth: 64
|
||||
color: hifi.colors.window
|
||||
border.color: hifi.colors.hifiBlue
|
||||
border.width: hifi.styles.borderWidth
|
||||
radius: hifi.styles.borderRadius
|
||||
}
|
||||
|
23
interface/resources/qml/styles/ButtonStyle.qml
Normal file
23
interface/resources/qml/styles/ButtonStyle.qml
Normal file
|
@ -0,0 +1,23 @@
|
|||
import QtQuick 2.4 as Original
|
||||
import QtQuick.Controls.Styles 1.3 as OriginalStyles
|
||||
import "."
|
||||
import "../controls"
|
||||
|
||||
OriginalStyles.ButtonStyle {
|
||||
HifiConstants { id: hifi }
|
||||
padding {
|
||||
top: 8
|
||||
left: 12
|
||||
right: 12
|
||||
bottom: 8
|
||||
}
|
||||
background: Border {
|
||||
anchors.fill: parent
|
||||
}
|
||||
label: Text {
|
||||
verticalAlignment: Original.Text.AlignVCenter
|
||||
horizontalAlignment: Original.Text.AlignHCenter
|
||||
text: control.text
|
||||
color: control.enabled ? hifi.colors.text : hifi.colors.disabledText
|
||||
}
|
||||
}
|
61
interface/resources/qml/styles/HifiConstants.qml
Normal file
61
interface/resources/qml/styles/HifiConstants.qml
Normal file
|
@ -0,0 +1,61 @@
|
|||
import QtQuick 2.4
|
||||
|
||||
Item {
|
||||
SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active }
|
||||
readonly property alias colors: colors
|
||||
readonly property alias layout: layout
|
||||
readonly property alias fonts: fonts
|
||||
readonly property alias styles: styles
|
||||
readonly property alias effects: effects
|
||||
|
||||
Item {
|
||||
id: colors
|
||||
readonly property color hifiBlue: "#0e7077"
|
||||
readonly property color window: sysPalette.window
|
||||
readonly property color dialogBackground: sysPalette.window
|
||||
//readonly property color dialogBackground: "#00000000"
|
||||
readonly property color inputBackground: "white"
|
||||
readonly property color background: sysPalette.dark
|
||||
readonly property color text: sysPalette.text
|
||||
readonly property color disabledText: "gray"
|
||||
readonly property color hintText: sysPalette.dark
|
||||
readonly property color light: sysPalette.light
|
||||
readonly property alias activeWindow: activeWindow
|
||||
readonly property alias inactiveWindow: inactiveWindow
|
||||
QtObject {
|
||||
id: activeWindow
|
||||
readonly property color headerBackground: "white"
|
||||
readonly property color headerText: "black"
|
||||
}
|
||||
QtObject {
|
||||
id: inactiveWindow
|
||||
readonly property color headerBackground: "gray"
|
||||
readonly property color headerText: "black"
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: fonts
|
||||
readonly property real headerPointSize: 24
|
||||
readonly property string fontFamily: "Helvetica"
|
||||
readonly property real fontSize: 18
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: layout
|
||||
property int spacing: 8
|
||||
property int rowHeight: 40
|
||||
property int windowTitleHeight: 48
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: styles
|
||||
readonly property int borderWidth: 5
|
||||
readonly property int borderRadius: borderWidth * 2
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: effects
|
||||
readonly property int fadeInDuration: 400
|
||||
}
|
||||
}
|
11
interface/resources/qml/styles/HifiPalette.qml
Normal file
11
interface/resources/qml/styles/HifiPalette.qml
Normal file
|
@ -0,0 +1,11 @@
|
|||
import QtQuick 2.4
|
||||
|
||||
Item {
|
||||
property string hifiBlue: "#0e7077"
|
||||
property alias colors: colorsObj
|
||||
|
||||
Item {
|
||||
id: colorsObj
|
||||
property string hifiRed: "red"
|
||||
}
|
||||
}
|
10
interface/resources/qml/styles/IconButtonStyle.qml
Normal file
10
interface/resources/qml/styles/IconButtonStyle.qml
Normal file
|
@ -0,0 +1,10 @@
|
|||
ButtonStyle {
|
||||
background: Item { anchors.fill: parent }
|
||||
label: FontAwesome {
|
||||
id: icon
|
||||
font.pointSize: 18
|
||||
property alias unicode: text
|
||||
text: control.text
|
||||
color: control.enabled ? hifi.colors.text : hifi.colors.disabledText
|
||||
}
|
||||
}
|
|
@ -35,7 +35,6 @@
|
|||
#include <QMouseEvent>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QObject>
|
||||
#include <QWheelEvent>
|
||||
#include <QScreen>
|
||||
|
@ -64,6 +63,7 @@
|
|||
#include <GlowEffect.h>
|
||||
#include <HFActionEvent.h>
|
||||
#include <HFBackEvent.h>
|
||||
#include <VrMenu.h>
|
||||
#include <LogHandler.h>
|
||||
#include <MainWindow.h>
|
||||
#include <ModelEntityItem.h>
|
||||
|
@ -83,6 +83,7 @@
|
|||
#include <TextRenderer.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <UUID.h>
|
||||
#include <MessageDialog.h>
|
||||
|
||||
#include <SceneScriptingInterface.h>
|
||||
|
||||
|
@ -109,7 +110,6 @@
|
|||
#include "devices/MIDIManager.h"
|
||||
#include "devices/OculusManager.h"
|
||||
#include "devices/TV3DManager.h"
|
||||
#include "devices/Visage.h"
|
||||
|
||||
#include "gpu/Batch.h"
|
||||
#include "gpu/GLBackend.h"
|
||||
|
@ -137,6 +137,7 @@
|
|||
#include "ui/Snapshot.h"
|
||||
#include "ui/StandAloneJSConsole.h"
|
||||
#include "ui/Stats.h"
|
||||
#include "ui/AddressBarDialog.h"
|
||||
|
||||
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
#if defined(Q_OS_WIN)
|
||||
|
@ -145,6 +146,23 @@ extern "C" {
|
|||
}
|
||||
#endif
|
||||
|
||||
enum CustomEventTypes {
|
||||
Lambda = QEvent::User + 1
|
||||
};
|
||||
|
||||
class LambdaEvent : public QEvent {
|
||||
std::function<void()> _fun;
|
||||
public:
|
||||
LambdaEvent(const std::function<void()> & fun) :
|
||||
QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) {
|
||||
}
|
||||
LambdaEvent(std::function<void()> && fun) :
|
||||
QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) {
|
||||
}
|
||||
void call() { _fun(); }
|
||||
};
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Starfield information
|
||||
|
@ -165,8 +183,6 @@ const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::D
|
|||
|
||||
const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js";
|
||||
|
||||
bool renderCollisionHulls = false;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
class MyNativeEventFilter : public QAbstractNativeEventFilter {
|
||||
public:
|
||||
|
@ -209,8 +225,12 @@ public:
|
|||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message);
|
||||
|
||||
|
||||
if (!logMessage.isEmpty()) {
|
||||
#ifdef Q_OS_WIN
|
||||
OutputDebugStringA(logMessage.toLocal8Bit().constData());
|
||||
OutputDebugStringA("\n");
|
||||
#endif
|
||||
Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage + "\n"));
|
||||
}
|
||||
}
|
||||
|
@ -243,7 +263,6 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
auto ambientOcclusionEffect = DependencyManager::set<AmbientOcclusionEffect>();
|
||||
auto textureCache = DependencyManager::set<TextureCache>();
|
||||
auto animationCache = DependencyManager::set<AnimationCache>();
|
||||
auto visage = DependencyManager::set<Visage>();
|
||||
auto ddeFaceTracker = DependencyManager::set<DdeFaceTracker>();
|
||||
auto modelBlender = DependencyManager::set<ModelBlender>();
|
||||
auto audioToolBox = DependencyManager::set<AudioToolBox>();
|
||||
|
@ -260,6 +279,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
#endif
|
||||
auto discoverabilityManager = DependencyManager::set<DiscoverabilityManager>();
|
||||
auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>();
|
||||
auto offscreenUi = DependencyManager::set<OffscreenUi>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -317,8 +337,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
#ifdef Q_OS_WIN
|
||||
installNativeEventFilter(&MyNativeEventFilter::getInstance());
|
||||
#endif
|
||||
|
||||
|
||||
_logger = new FileLogger(this); // After setting organization name in order to get correct directory
|
||||
|
||||
qInstallMessageHandler(messageHandler);
|
||||
|
||||
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf");
|
||||
|
@ -336,9 +358,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
|
||||
_bookmarks = new Bookmarks(); // Before setting up the menu
|
||||
|
||||
// call Menu getInstance static method to set up the menu
|
||||
_window->setMenuBar(Menu::getInstance());
|
||||
|
||||
_runningScriptsWidget = new RunningScriptsWidget(_window);
|
||||
|
||||
// start the nodeThread so its event loop is running
|
||||
|
@ -445,6 +464,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
addressManager->setOrientationGetter(getOrientationForPath);
|
||||
|
||||
connect(addressManager.data(), &AddressManager::rootPlaceNameChanged, this, &Application::updateWindowTitle);
|
||||
connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA WsaData;
|
||||
|
@ -562,8 +582,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
#endif
|
||||
|
||||
this->installEventFilter(this);
|
||||
// The offscreen UI needs to intercept the mouse and keyboard
|
||||
// events coming from the onscreen window
|
||||
_glWidget->installEventFilter(DependencyManager::get<OffscreenUi>().data());
|
||||
}
|
||||
|
||||
|
||||
void Application::aboutToQuit() {
|
||||
emit beforeAboutToQuit();
|
||||
|
||||
|
@ -612,6 +636,10 @@ void Application::cleanupBeforeQuit() {
|
|||
|
||||
// destroy the AudioClient so it and its thread have a chance to go down safely
|
||||
DependencyManager::destroy<AudioClient>();
|
||||
|
||||
#ifdef HAVE_DDE
|
||||
DependencyManager::destroy<DdeFaceTracker>();
|
||||
#endif
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
|
@ -632,6 +660,7 @@ Application::~Application() {
|
|||
|
||||
getActiveRenderPlugin()->deactivate();
|
||||
|
||||
DependencyManager::destroy<OffscreenUi>();
|
||||
DependencyManager::destroy<AvatarManager>();
|
||||
DependencyManager::destroy<AnimationCache>();
|
||||
DependencyManager::destroy<TextureCache>();
|
||||
|
@ -692,6 +721,17 @@ void Application::initializeGL() {
|
|||
initDisplay();
|
||||
qCDebug(interfaceapp, "Initialized Display.");
|
||||
|
||||
// The UI can't be created until the primary OpenGL
|
||||
// context is created, because it needs to share
|
||||
// texture resources
|
||||
initializeUi();
|
||||
qCDebug(interfaceapp, "Initialized Offscreen UI.");
|
||||
_glWidget->makeCurrent();
|
||||
|
||||
// call Menu getInstance static method to set up the menu
|
||||
// Needs to happen AFTER the QML UI initialization
|
||||
_window->setMenuBar(Menu::getInstance());
|
||||
|
||||
init();
|
||||
qCDebug(interfaceapp, "init() complete.");
|
||||
|
||||
|
@ -719,10 +759,45 @@ void Application::initializeGL() {
|
|||
|
||||
// update before the first render
|
||||
update(1.0f / _fps);
|
||||
|
||||
|
||||
InfoView::showFirstTime(INFO_HELP_PATH);
|
||||
}
|
||||
|
||||
void Application::initializeUi() {
|
||||
AddressBarDialog::registerType();
|
||||
LoginDialog::registerType();
|
||||
MessageDialog::registerType();
|
||||
VrMenu::registerType();
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->create(_glWidget->context()->contextHandle());
|
||||
offscreenUi->resize(_glWidget->size());
|
||||
offscreenUi->setProxyWindow(_window->windowHandle());
|
||||
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
||||
offscreenUi->load("Root.qml");
|
||||
offscreenUi->load("RootMenu.qml");
|
||||
VrMenu::load();
|
||||
VrMenu::executeQueuedLambdas();
|
||||
offscreenUi->setMouseTranslator([this](const QPointF& p){
|
||||
if (OculusManager::isConnected()) {
|
||||
glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p));
|
||||
return QPointF(pos.x, pos.y);
|
||||
}
|
||||
return QPointF(p);
|
||||
});
|
||||
offscreenUi->resume();
|
||||
connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect & r){
|
||||
static qreal oldDevicePixelRatio = 0;
|
||||
qreal devicePixelRatio = _glWidget->devicePixelRatio();
|
||||
if (devicePixelRatio != oldDevicePixelRatio) {
|
||||
oldDevicePixelRatio = devicePixelRatio;
|
||||
qDebug() << "Device pixel ratio changed, triggering GL resize";
|
||||
resizeGL(_glWidget->width(),
|
||||
_glWidget->height());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Application::paintGL() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
PerformanceTimer perfTimer("paintGL");
|
||||
|
@ -777,7 +852,7 @@ void Application::paintGL() {
|
|||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Viewport is assigned to the size of the framebuffer
|
||||
QSize size = DependencyManager::get<TextureCache>()->getPrimaryFramebufferObject()->size();
|
||||
QSize size = DependencyManager::get<TextureCache>()->getFrameBufferSize();
|
||||
glViewport(0, 0, size.width(), size.height());
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
@ -789,7 +864,19 @@ void Application::paintGL() {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
_rearMirrorTools->render(true);
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
<<<<<<< HEAD
|
||||
renderRearViewMirror(_mirrorViewRect);
|
||||
=======
|
||||
renderRearViewMirror(_mirrorViewRect);
|
||||
}
|
||||
|
||||
DependencyManager::get<GlowEffect>()->render();
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("renderOverlay");
|
||||
_applicationOverlay.renderOverlay();
|
||||
_applicationOverlay.displayOverlayTexture();
|
||||
>>>>>>> master
|
||||
}
|
||||
}
|
||||
primaryFbo->release();
|
||||
|
@ -864,6 +951,9 @@ void Application::resizeGL(int width, int height) {
|
|||
updateProjectionMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->resize(_glWidget->size());
|
||||
|
||||
// update Stats width
|
||||
// let's set horizontal offset to give stats some margin to mirror
|
||||
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
|
||||
|
@ -913,6 +1003,7 @@ bool Application::importSVOFromURL(const QString& urlString) {
|
|||
|
||||
bool Application::event(QEvent* event) {
|
||||
switch (event->type()) {
|
||||
<<<<<<< HEAD
|
||||
case QEvent::MouseMove:
|
||||
mouseMoveEvent((QMouseEvent*)event);
|
||||
return true;
|
||||
|
@ -948,6 +1039,48 @@ bool Application::event(QEvent* event) {
|
|||
|
||||
default:
|
||||
break;
|
||||
=======
|
||||
case Lambda:
|
||||
((LambdaEvent*)event)->call();
|
||||
return true;
|
||||
|
||||
case QEvent::MouseMove:
|
||||
mouseMoveEvent((QMouseEvent*)event);
|
||||
return true;
|
||||
case QEvent::MouseButtonPress:
|
||||
mousePressEvent((QMouseEvent*)event);
|
||||
return true;
|
||||
case QEvent::MouseButtonRelease:
|
||||
mouseReleaseEvent((QMouseEvent*)event);
|
||||
return true;
|
||||
case QEvent::KeyPress:
|
||||
keyPressEvent((QKeyEvent*)event);
|
||||
return true;
|
||||
case QEvent::KeyRelease:
|
||||
keyReleaseEvent((QKeyEvent*)event);
|
||||
return true;
|
||||
case QEvent::FocusOut:
|
||||
focusOutEvent((QFocusEvent*)event);
|
||||
return true;
|
||||
case QEvent::TouchBegin:
|
||||
touchBeginEvent(static_cast<QTouchEvent*>(event));
|
||||
event->accept();
|
||||
return true;
|
||||
case QEvent::TouchEnd:
|
||||
touchEndEvent(static_cast<QTouchEvent*>(event));
|
||||
return true;
|
||||
case QEvent::TouchUpdate:
|
||||
touchUpdateEvent(static_cast<QTouchEvent*>(event));
|
||||
return true;
|
||||
case QEvent::Wheel:
|
||||
wheelEvent(static_cast<QWheelEvent*>(event));
|
||||
return true;
|
||||
case QEvent::Drop:
|
||||
dropEvent(static_cast<QDropEvent*>(event));
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
>>>>>>> master
|
||||
}
|
||||
|
||||
// handle custom URL
|
||||
|
@ -969,13 +1102,18 @@ bool Application::event(QEvent* event) {
|
|||
if (HFActionEvent::types().contains(event->type())) {
|
||||
_controllerScriptingInterface.handleMetaEvent(static_cast<HFMetaEvent*>(event));
|
||||
}
|
||||
|
||||
|
||||
return QApplication::event(event);
|
||||
}
|
||||
|
||||
bool Application::eventFilter(QObject* object, QEvent* event) {
|
||||
|
||||
if (event->type() == QEvent::ShortcutOverride) {
|
||||
if (DependencyManager::get<OffscreenUi>()->shouldSwallowShortcut(event)) {
|
||||
event->accept();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Filter out captured keys before they're used for shortcut actions.
|
||||
if (_controllerScriptingInterface.isKeyCaptured(static_cast<QKeyEvent*>(event))) {
|
||||
event->accept();
|
||||
|
@ -986,8 +1124,10 @@ bool Application::eventFilter(QObject* object, QEvent* event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Application::keyPressEvent(QKeyEvent* event) {
|
||||
static bool _altPressed{ false };
|
||||
|
||||
void Application::keyPressEvent(QKeyEvent* event) {
|
||||
_altPressed = event->key() == Qt::Key_Alt;
|
||||
_keysPressed.insert(event->key());
|
||||
|
||||
_controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts
|
||||
|
@ -1003,12 +1143,19 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
bool isOption = event->modifiers().testFlag(Qt::AltModifier);
|
||||
switch (event->key()) {
|
||||
break;
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
|
||||
break;
|
||||
|
||||
case Qt::Key_L:
|
||||
if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::LodTools);
|
||||
} else if (isMeta) {
|
||||
if (isShifted && isMeta) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Log);
|
||||
}
|
||||
} else if (isMeta) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
|
||||
} else if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::LodTools);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_E:
|
||||
|
@ -1068,11 +1215,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
|
||||
break;
|
||||
|
||||
case Qt::Key_Backslash:
|
||||
Menu::getInstance()->triggerOption(MenuOption::Chat);
|
||||
break;
|
||||
|
@ -1226,11 +1368,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_Comma: {
|
||||
renderCollisionHulls = !renderCollisionHulls;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
event->ignore();
|
||||
break;
|
||||
|
@ -1238,7 +1375,19 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//#define VR_MENU_ONLY_IN_HMD
|
||||
|
||||
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||
if (event->key() == Qt::Key_Alt && _altPressed && _window->isActiveWindow()) {
|
||||
#ifdef VR_MENU_ONLY_IN_HMD
|
||||
if (OculusManager::isConnected()) {
|
||||
#endif
|
||||
VrMenu::toggle();
|
||||
#ifdef VR_MENU_ONLY_IN_HMD
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
_keysPressed.remove(event->key());
|
||||
|
||||
|
@ -1249,6 +1398,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
switch (event->key()) {
|
||||
case Qt::Key_E:
|
||||
case Qt::Key_PageUp:
|
||||
|
@ -1440,9 +1590,11 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
}
|
||||
|
||||
void Application::touchUpdateEvent(QTouchEvent* event) {
|
||||
TouchEvent thisEvent(*event, _lastTouchEvent);
|
||||
_controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
|
||||
_lastTouchEvent = thisEvent;
|
||||
if (event->type() == QEvent::TouchUpdate) {
|
||||
TouchEvent thisEvent(*event, _lastTouchEvent);
|
||||
_controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
|
||||
_lastTouchEvent = thisEvent;
|
||||
}
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isTouchCaptured()) {
|
||||
|
@ -1530,6 +1682,17 @@ void Application::dropEvent(QDropEvent *event) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::dragEnterEvent(QDragEnterEvent* event) {
|
||||
const QMimeData* mimeData = event->mimeData();
|
||||
foreach(QUrl url, mimeData->urls()) {
|
||||
auto urlString = url.toString();
|
||||
if (canAcceptURL(urlString)) {
|
||||
event->acceptProposedAction();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::acceptSnapshot(const QString& urlString) {
|
||||
QUrl url(urlString);
|
||||
QString snapshotPath = url.toLocalFile();
|
||||
|
@ -1767,21 +1930,21 @@ int Application::getMouseDragStartedY() const {
|
|||
|
||||
FaceTracker* Application::getActiveFaceTracker() {
|
||||
auto faceshift = DependencyManager::get<Faceshift>();
|
||||
auto visage = DependencyManager::get<Visage>();
|
||||
auto dde = DependencyManager::get<DdeFaceTracker>();
|
||||
|
||||
return (dde->isActive() ? static_cast<FaceTracker*>(dde.data()) :
|
||||
(faceshift->isActive() ? static_cast<FaceTracker*>(faceshift.data()) :
|
||||
(visage->isActive() ? static_cast<FaceTracker*>(visage.data()) : NULL)));
|
||||
(faceshift->isActive() ? static_cast<FaceTracker*>(faceshift.data()) : NULL));
|
||||
}
|
||||
|
||||
void Application::setActiveFaceTracker() {
|
||||
#ifdef HAVE_FACESHIFT
|
||||
DependencyManager::get<Faceshift>()->setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift));
|
||||
#endif
|
||||
DependencyManager::get<DdeFaceTracker>()->setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::DDEFaceRegression));
|
||||
#ifdef HAVE_VISAGE
|
||||
DependencyManager::get<Visage>()->updateEnabled();
|
||||
#endif
|
||||
#ifdef HAVE_DDE
|
||||
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera);
|
||||
Menu::getInstance()->getActionForOption(MenuOption::UseAudioForMouth)->setVisible(isUsingDDE);
|
||||
Menu::getInstance()->getActionForOption(MenuOption::VelocityFilter)->setVisible(isUsingDDE);
|
||||
DependencyManager::get<DdeFaceTracker>()->setEnabled(isUsingDDE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1958,7 +2121,6 @@ void Application::init() {
|
|||
// initialize our face trackers after loading the menu settings
|
||||
DependencyManager::get<Faceshift>()->init();
|
||||
DependencyManager::get<DdeFaceTracker>()->init();
|
||||
DependencyManager::get<Visage>()->init();
|
||||
|
||||
Leapmotion::init();
|
||||
RealSense::init();
|
||||
|
@ -2268,6 +2430,7 @@ void Application::update(float deltaTime) {
|
|||
PerformanceTimer perfTimer("physics");
|
||||
_myAvatar->relayDriveKeysToCharacterController();
|
||||
_physicsEngine.stepSimulation();
|
||||
_physicsEngine.dumpStatsIfNecessary();
|
||||
}
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
|
@ -2675,8 +2838,9 @@ void Application::updateShadowMap() {
|
|||
activeRenderingThread = QThread::currentThread();
|
||||
|
||||
PerformanceTimer perfTimer("shadowMap");
|
||||
QOpenGLFramebufferObject* fbo = DependencyManager::get<TextureCache>()->getShadowFramebufferObject();
|
||||
fbo->bind();
|
||||
auto shadowFramebuffer = DependencyManager::get<TextureCache>()->getShadowFramebuffer();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(shadowFramebuffer));
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
@ -2692,16 +2856,18 @@ void Application::updateShadowMap() {
|
|||
loadViewFrustum(_myCamera, _viewFrustum);
|
||||
|
||||
int matrixCount = 1;
|
||||
int targetSize = fbo->width();
|
||||
//int targetSize = fbo->width();
|
||||
int sourceSize = shadowFramebuffer->getWidth();
|
||||
int targetSize = shadowFramebuffer->getWidth();
|
||||
float targetScale = 1.0f;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
|
||||
matrixCount = CASCADED_SHADOW_MATRIX_COUNT;
|
||||
targetSize = fbo->width() / 2;
|
||||
targetSize = sourceSize / 2;
|
||||
targetScale = 0.5f;
|
||||
}
|
||||
for (int i = 0; i < matrixCount; i++) {
|
||||
const glm::vec2& coord = MAP_COORDS[i];
|
||||
glViewport(coord.s * fbo->width(), coord.t * fbo->height(), targetSize, targetSize);
|
||||
glViewport(coord.s * sourceSize, coord.t * sourceSize, targetSize, targetSize);
|
||||
|
||||
// if simple shadow then since the resolution is twice as much as with cascaded, cover 2 regions with the map, not just one
|
||||
int regionIncrement = (matrixCount == 1 ? 2 : 1);
|
||||
|
@ -2824,7 +2990,13 @@ void Application::updateShadowMap() {
|
|||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
fbo->release();
|
||||
=======
|
||||
// fbo->release();
|
||||
|
||||
glViewport(0, 0, _glWidget->getDeviceWidth(), _glWidget->getDeviceHeight());
|
||||
>>>>>>> master
|
||||
activeRenderingThread = nullptr;
|
||||
}
|
||||
|
||||
|
@ -2864,7 +3036,8 @@ int Application::getBoundaryLevelAdjust() const {
|
|||
}
|
||||
|
||||
QImage Application::renderAvatarBillboard() {
|
||||
DependencyManager::get<TextureCache>()->getPrimaryFramebufferObject()->bind();
|
||||
auto primaryFramebuffer = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer));
|
||||
|
||||
// the "glow" here causes an alpha of one
|
||||
Glower glower;
|
||||
|
@ -2879,7 +3052,7 @@ QImage Application::renderAvatarBillboard() {
|
|||
QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32);
|
||||
glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
|
||||
|
||||
DependencyManager::get<TextureCache>()->getPrimaryFramebufferObject()->release();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
@ -3036,13 +3209,21 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
|
|||
PerformanceTimer perfTimer("entities");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... entities...");
|
||||
if (renderCollisionHulls) {
|
||||
_entities.render(RenderArgs::DEBUG_RENDER_MODE, renderSide);
|
||||
} else if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_entities.render(RenderArgs::MIRROR_RENDER_MODE, renderSide);
|
||||
} else {
|
||||
_entities.render(RenderArgs::DEFAULT_RENDER_MODE, renderSide);
|
||||
|
||||
RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE;
|
||||
RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE;
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {
|
||||
renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) {
|
||||
renderDebugFlags =
|
||||
(RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
|
||||
}
|
||||
if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
}
|
||||
_entities.render(renderMode, renderSide, renderDebugFlags);
|
||||
}
|
||||
|
||||
// render JS/scriptable overlays
|
||||
|
@ -3071,7 +3252,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
|
|||
{
|
||||
DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());
|
||||
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
||||
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity());
|
||||
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity());
|
||||
DependencyManager::get<DeferredLightingEffect>()->setGlobalAtmosphere(skyStage->getAtmosphere());
|
||||
|
||||
PROFILE_RANGE("DeferredLighting");
|
||||
|
@ -3253,7 +3434,6 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
|
||||
void Application::resetSensors() {
|
||||
DependencyManager::get<Faceshift>()->reset();
|
||||
DependencyManager::get<Visage>()->reset();
|
||||
DependencyManager::get<DdeFaceTracker>()->reset();
|
||||
|
||||
getActiveRenderPlugin()->resetSensors();
|
||||
|
@ -3715,17 +3895,7 @@ bool Application::askToSetAvatarUrl(const QString& url) {
|
|||
}
|
||||
|
||||
// Download the FST file, to attempt to determine its model type
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(url);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(networkRequest);
|
||||
qCDebug(interfaceapp) << "Downloading avatar file at " << url;
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
QByteArray fstContents = reply->readAll();
|
||||
delete reply;
|
||||
QVariantHash fstMapping = FSTReader::readMapping(fstContents);
|
||||
QVariantHash fstMapping = FSTReader::downloadMapping(url);
|
||||
|
||||
FSTReader::ModelType modelType = FSTReader::predictModelType(fstMapping);
|
||||
|
||||
|
@ -3736,26 +3906,27 @@ bool Application::askToSetAvatarUrl(const QString& url) {
|
|||
QPushButton* bodyButton = NULL;
|
||||
QPushButton* bodyAndHeadButton = NULL;
|
||||
|
||||
QString modelName = fstMapping["name"].toString();
|
||||
QString message;
|
||||
QString typeInfo;
|
||||
switch (modelType) {
|
||||
case FSTReader::HEAD_MODEL:
|
||||
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar head?");
|
||||
message = QString("Would you like to use '") + modelName + QString("' for your avatar head?");
|
||||
headButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
|
||||
break;
|
||||
|
||||
case FSTReader::BODY_ONLY_MODEL:
|
||||
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar body?");
|
||||
message = QString("Would you like to use '") + modelName + QString("' for your avatar body?");
|
||||
bodyButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
|
||||
break;
|
||||
|
||||
case FSTReader::HEAD_AND_BODY_MODEL:
|
||||
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar?");
|
||||
message = QString("Would you like to use '") + modelName + QString("' for your avatar?");
|
||||
bodyAndHeadButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
|
||||
break;
|
||||
|
||||
default:
|
||||
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for some part of your avatar head?");
|
||||
message = QString("Would you like to use '") + modelName + QString("' for some part of your avatar head?");
|
||||
headButton = msgBox.addButton(tr("Use for Head"), QMessageBox::ActionRole);
|
||||
bodyButton = msgBox.addButton(tr("Use for Body"), QMessageBox::ActionRole);
|
||||
bodyAndHeadButton = msgBox.addButton(tr("Use for Body and Head"), QMessageBox::ActionRole);
|
||||
|
@ -3768,34 +3939,18 @@ bool Application::askToSetAvatarUrl(const QString& url) {
|
|||
msgBox.exec();
|
||||
|
||||
if (msgBox.clickedButton() == headButton) {
|
||||
qCDebug(interfaceapp) << "Chose to use for head: " << url;
|
||||
_myAvatar->setFaceModelURL(url);
|
||||
UserActivityLogger::getInstance().changedModel("head", url);
|
||||
_myAvatar->sendIdentityPacket();
|
||||
emit faceURLChanged(url);
|
||||
_myAvatar->useHeadURL(url, modelName);
|
||||
emit headURLChanged(url, modelName);
|
||||
} else if (msgBox.clickedButton() == bodyButton) {
|
||||
qCDebug(interfaceapp) << "Chose to use for body: " << url;
|
||||
_myAvatar->setSkeletonModelURL(url);
|
||||
// if the head is empty, reset it to the default head.
|
||||
if (_myAvatar->getFaceModelURLString().isEmpty()) {
|
||||
_myAvatar->setFaceModelURL(DEFAULT_HEAD_MODEL_URL);
|
||||
emit faceURLChanged(DEFAULT_HEAD_MODEL_URL.toString());
|
||||
UserActivityLogger::getInstance().changedModel("head", DEFAULT_HEAD_MODEL_URL.toString());
|
||||
}
|
||||
UserActivityLogger::getInstance().changedModel("skeleton", url);
|
||||
_myAvatar->sendIdentityPacket();
|
||||
emit skeletonURLChanged(url);
|
||||
_myAvatar->useBodyURL(url, modelName);
|
||||
emit bodyURLChanged(url, modelName);
|
||||
} else if (msgBox.clickedButton() == bodyAndHeadButton) {
|
||||
qCDebug(interfaceapp) << "Chose to use for body + head: " << url;
|
||||
_myAvatar->setFaceModelURL(QString());
|
||||
_myAvatar->setSkeletonModelURL(url);
|
||||
UserActivityLogger::getInstance().changedModel("skeleton", url);
|
||||
_myAvatar->sendIdentityPacket();
|
||||
emit faceURLChanged(QString());
|
||||
emit skeletonURLChanged(url);
|
||||
_myAvatar->useFullAvatarURL(url, modelName);
|
||||
emit fullAvatarURLChanged(url, modelName);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Declined to use the avatar: " << url;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3885,6 +4040,9 @@ void Application::stopAllScripts(bool restart) {
|
|||
// stops all current running scripts
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (it.value()->isFinished()) {
|
||||
continue;
|
||||
}
|
||||
if (restart && it.value()->isUserLoaded()) {
|
||||
connect(it.value(), SIGNAL(finished(const QString&)), SLOT(loadScript(const QString&)));
|
||||
}
|
||||
|
@ -4307,10 +4465,8 @@ void Application::checkSkeleton() {
|
|||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.exec();
|
||||
|
||||
_myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL);
|
||||
_myAvatar->sendIdentityPacket();
|
||||
_myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default");
|
||||
} else {
|
||||
_myAvatar->updateCharacterController();
|
||||
_physicsEngine.setCharacterController(_myAvatar->getCharacterController());
|
||||
}
|
||||
}
|
||||
|
@ -4334,6 +4490,10 @@ void Application::friendsWindowClosed() {
|
|||
}
|
||||
|
||||
|
||||
void Application::postLambdaEvent(std::function<void()> f) {
|
||||
QCoreApplication::postEvent(this, new LambdaEvent(f));
|
||||
}
|
||||
|
||||
PickRay Application::computePickRay() const {
|
||||
return computePickRay(getTrueMouseX(), getTrueMouseY());
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <QSet>
|
||||
#include <QStringList>
|
||||
#include <QUndoStack>
|
||||
#include <functional>
|
||||
|
||||
#include <AbstractScriptingServicesInterface.h>
|
||||
#include <AbstractViewStateInterface.h>
|
||||
|
@ -30,6 +31,7 @@
|
|||
#include <NetworkPacket.h>
|
||||
#include <NodeList.h>
|
||||
#include <OctreeQuery.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <PhysicsEngine.h>
|
||||
#include <ScriptEngine.h>
|
||||
|
@ -120,9 +122,9 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html";
|
|||
|
||||
#ifdef Q_OS_WIN
|
||||
static const UINT UWM_IDENTIFY_INSTANCES =
|
||||
RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}");
|
||||
RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME"));
|
||||
static const UINT UWM_SHOW_APPLICATION =
|
||||
RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}");
|
||||
RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}_" + qgetenv("USERNAME"));
|
||||
#endif
|
||||
|
||||
class Application;
|
||||
|
@ -149,11 +151,14 @@ public:
|
|||
Application(int& argc, char** argv, QElapsedTimer &startup_time);
|
||||
~Application();
|
||||
|
||||
void postLambdaEvent(std::function<void()> f);
|
||||
|
||||
void loadScripts();
|
||||
QString getPreviousScriptLocation();
|
||||
void setPreviousScriptLocation(const QString& previousScriptLocation);
|
||||
void clearScriptsBeforeRunning();
|
||||
void initializeGL();
|
||||
void initializeUi();
|
||||
void paintGL();
|
||||
void resizeGL(int width, int height);
|
||||
|
||||
|
@ -174,7 +179,8 @@ public:
|
|||
void touchUpdateEvent(QTouchEvent* event);
|
||||
|
||||
void wheelEvent(QWheelEvent* event);
|
||||
void dropEvent(QDropEvent *event);
|
||||
void dropEvent(QDropEvent* event);
|
||||
void dragEnterEvent(QDragEnterEvent* event);
|
||||
|
||||
bool event(QEvent* event);
|
||||
bool eventFilter(QObject* object, QEvent* event);
|
||||
|
@ -221,6 +227,7 @@ public:
|
|||
bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; }
|
||||
|
||||
FaceTracker* getActiveFaceTracker();
|
||||
|
||||
QSystemTrayIcon* getTrayIcon() { return _trayIcon; }
|
||||
ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; }
|
||||
Overlays& getOverlays() { return _overlays; }
|
||||
|
@ -348,8 +355,9 @@ signals:
|
|||
void checkBackgroundDownloads();
|
||||
void domainConnectionRefused(const QString& reason);
|
||||
|
||||
void faceURLChanged(const QString& newValue);
|
||||
void skeletonURLChanged(const QString& newValue);
|
||||
void headURLChanged(const QString& newValue, const QString& modelName);
|
||||
void bodyURLChanged(const QString& newValue, const QString& modelName);
|
||||
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
|
||||
|
||||
void beforeAboutToQuit();
|
||||
|
||||
|
@ -395,6 +403,8 @@ public slots:
|
|||
void setVSyncEnabled();
|
||||
|
||||
void resetSensors();
|
||||
void setActiveFaceTracker();
|
||||
|
||||
void aboutApp();
|
||||
void showEditEntitiesHelp();
|
||||
|
||||
|
@ -403,8 +413,6 @@ public slots:
|
|||
|
||||
void notifyPacketVersionMismatch();
|
||||
|
||||
void setActiveFaceTracker();
|
||||
|
||||
void domainConnectionDenied(const QString& reason);
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -84,7 +84,7 @@ void Bookmarks::persistToFile() {
|
|||
saveFile.write(data);
|
||||
}
|
||||
|
||||
void Bookmarks::setupMenus(Menu* menubar, QMenu* menu) {
|
||||
void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
|
||||
// Add menus/actions
|
||||
menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkLocation, 0,
|
||||
this, SLOT(bookmarkLocation()));
|
||||
|
@ -192,7 +192,7 @@ void Bookmarks::enableMenuItems(bool enabled) {
|
|||
}
|
||||
|
||||
void Bookmarks::addLocationToMenu(Menu* menubar, QString& name, QString& address) {
|
||||
QAction* teleportAction = new QAction(_bookmarksMenu);
|
||||
QAction* teleportAction = _bookmarksMenu->newAction();
|
||||
teleportAction->setData(address);
|
||||
connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
class QAction;
|
||||
class QMenu;
|
||||
class Menu;
|
||||
class MenuWrapper;
|
||||
|
||||
class Bookmarks: public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -26,7 +27,7 @@ class Bookmarks: public QObject {
|
|||
public:
|
||||
Bookmarks();
|
||||
|
||||
void setupMenus(Menu* menubar, QMenu* menu);
|
||||
void setupMenus(Menu* menubar, MenuWrapper* menu);
|
||||
|
||||
private slots:
|
||||
void bookmarkLocation();
|
||||
|
@ -36,7 +37,7 @@ private slots:
|
|||
private:
|
||||
QVariantMap _bookmarks; // { name: address, ... }
|
||||
|
||||
QPointer<QMenu> _bookmarksMenu;
|
||||
QPointer<MenuWrapper> _bookmarksMenu;
|
||||
QPointer<QAction> _deleteBookmarksAction;
|
||||
|
||||
const QString BOOKMARKS_FILENAME = "bookmarks.json";
|
||||
|
|
|
@ -54,8 +54,7 @@ Camera::Camera() :
|
|||
_nearClip(DEFAULT_NEAR_CLIP), // default
|
||||
_farClip(DEFAULT_FAR_CLIP), // default
|
||||
_hmdPosition(),
|
||||
_hmdRotation(),
|
||||
_scale(1.0f)
|
||||
_hmdRotation()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -76,8 +75,8 @@ void Camera::setHmdRotation(const glm::quat& hmdRotation) {
|
|||
}
|
||||
|
||||
float Camera::getFarClip() const {
|
||||
return (_scale * _farClip < std::numeric_limits<int16_t>::max())
|
||||
? _scale * _farClip
|
||||
return (_farClip < std::numeric_limits<int16_t>::max())
|
||||
? _farClip
|
||||
: std::numeric_limits<int16_t>::max() - 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@ public:
|
|||
void setFarClip(float f);
|
||||
void setEyeOffsetPosition(const glm::vec3& p) { _eyeOffsetPosition = p; }
|
||||
void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; }
|
||||
void setScale(const float s) { _scale = s; }
|
||||
|
||||
glm::quat getRotation() const { return _rotation * _hmdRotation; }
|
||||
const glm::vec3& getHmdPosition() const { return _hmdPosition; }
|
||||
|
@ -61,11 +60,10 @@ public:
|
|||
CameraMode getMode() const { return _mode; }
|
||||
float getFieldOfView() const { return _fieldOfView; }
|
||||
float getAspectRatio() const { return _aspectRatio; }
|
||||
float getNearClip() const { return _scale * _nearClip; }
|
||||
float getNearClip() const { return _nearClip; }
|
||||
float getFarClip() const;
|
||||
const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; }
|
||||
const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; }
|
||||
float getScale() const { return _scale; }
|
||||
public slots:
|
||||
QString getModeString() const;
|
||||
void setModeString(const QString& mode);
|
||||
|
@ -91,7 +89,6 @@ private:
|
|||
glm::quat _rotation;
|
||||
glm::vec3 _hmdPosition;
|
||||
glm::quat _hmdRotation;
|
||||
float _scale;
|
||||
};
|
||||
|
||||
#endif // hifi_Camera_h
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <UUID.h>
|
||||
|
||||
#include "DiscoverabilityManager.h"
|
||||
#include "Menu.h"
|
||||
|
||||
const Discoverability::Mode DEFAULT_DISCOVERABILITY_MODE = Discoverability::All;
|
||||
|
||||
|
@ -96,4 +97,32 @@ void DiscoverabilityManager::setDiscoverabilityMode(Discoverability::Mode discov
|
|||
|
||||
emit discoverabilityModeChanged(discoverabilityMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoverabilityManager::setVisibility() {
|
||||
Menu* menu = Menu::getInstance();
|
||||
|
||||
if (menu->isOptionChecked(MenuOption::VisibleToEveryone)) {
|
||||
this->setDiscoverabilityMode(Discoverability::All);
|
||||
} else if (menu->isOptionChecked(MenuOption::VisibleToFriends)) {
|
||||
this->setDiscoverabilityMode(Discoverability::Friends);
|
||||
} else if (menu->isOptionChecked(MenuOption::VisibleToNoOne)) {
|
||||
this->setDiscoverabilityMode(Discoverability::None);
|
||||
} else {
|
||||
qDebug() << "ERROR DiscoverabilityManager::setVisibility() called with unrecognized value.";
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoverabilityManager::visibilityChanged(Discoverability::Mode discoverabilityMode) {
|
||||
Menu* menu = Menu::getInstance();
|
||||
|
||||
if (discoverabilityMode == Discoverability::All) {
|
||||
menu->setIsOptionChecked(MenuOption::VisibleToEveryone, true);
|
||||
} else if (discoverabilityMode == Discoverability::Friends) {
|
||||
menu->setIsOptionChecked(MenuOption::VisibleToFriends, true);
|
||||
} else if (discoverabilityMode == Discoverability::None) {
|
||||
menu->setIsOptionChecked(MenuOption::VisibleToNoOne, true);
|
||||
} else {
|
||||
qDebug() << "ERROR DiscoverabilityManager::visibilityChanged() called with unrecognized value.";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,9 @@ public slots:
|
|||
Discoverability::Mode getDiscoverabilityMode() { return static_cast<Discoverability::Mode>(_mode.get()); }
|
||||
void setDiscoverabilityMode(Discoverability::Mode discoverabilityMode);
|
||||
|
||||
void setVisibility();
|
||||
void visibilityChanged(Discoverability::Mode discoverabilityMode);
|
||||
|
||||
signals:
|
||||
void discoverabilityModeChanged(Discoverability::Mode discoverabilityMode);
|
||||
|
||||
|
|
|
@ -88,30 +88,6 @@ void GLCanvas::resizeGL(int width, int height) {
|
|||
Application::getInstance()->resizeGL(width, height);
|
||||
}
|
||||
|
||||
void GLCanvas::keyPressEvent(QKeyEvent* event) {
|
||||
Application::getInstance()->keyPressEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::keyReleaseEvent(QKeyEvent* event) {
|
||||
Application::getInstance()->keyReleaseEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::focusOutEvent(QFocusEvent* event) {
|
||||
Application::getInstance()->focusOutEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::mouseMoveEvent(QMouseEvent* event) {
|
||||
Application::getInstance()->mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::mousePressEvent(QMouseEvent* event) {
|
||||
Application::getInstance()->mousePressEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::mouseReleaseEvent(QMouseEvent* event) {
|
||||
Application::getInstance()->mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::activeChanged(Qt::ApplicationState state) {
|
||||
switch (state) {
|
||||
case Qt::ApplicationActive:
|
||||
|
@ -147,6 +123,7 @@ void GLCanvas::throttleRender() {
|
|||
}
|
||||
#endif
|
||||
|
||||
makeCurrent();
|
||||
Application::getInstance()->paintGL();
|
||||
swapBuffers();
|
||||
|
||||
|
@ -161,40 +138,37 @@ void GLCanvas::throttleRender() {
|
|||
int updateTime = 0;
|
||||
bool GLCanvas::event(QEvent* event) {
|
||||
switch (event->type()) {
|
||||
case QEvent::MouseMove:
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
case QEvent::FocusIn:
|
||||
case QEvent::FocusOut:
|
||||
case QEvent::Resize:
|
||||
case QEvent::TouchBegin:
|
||||
Application::getInstance()->touchBeginEvent(static_cast<QTouchEvent*>(event));
|
||||
event->accept();
|
||||
return true;
|
||||
case QEvent::TouchEnd:
|
||||
Application::getInstance()->touchEndEvent(static_cast<QTouchEvent*>(event));
|
||||
return true;
|
||||
case QEvent::TouchUpdate:
|
||||
Application::getInstance()->touchUpdateEvent(static_cast<QTouchEvent*>(event));
|
||||
return true;
|
||||
case QEvent::Wheel:
|
||||
case QEvent::DragEnter:
|
||||
case QEvent::Drop:
|
||||
if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::Paint:
|
||||
// Ignore paint events that occur after we've decided to quit
|
||||
if (Application::getInstance()->isAboutToQuit()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QGLWidget::event(event);
|
||||
}
|
||||
|
||||
void GLCanvas::wheelEvent(QWheelEvent* event) {
|
||||
Application::getInstance()->wheelEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::dragEnterEvent(QDragEnterEvent* event) {
|
||||
const QMimeData* mimeData = event->mimeData();
|
||||
foreach (QUrl url, mimeData->urls()) {
|
||||
auto urlString = url.toString();
|
||||
if (Application::getInstance()->canAcceptURL(urlString)) {
|
||||
event->acceptProposedAction();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLCanvas::dropEvent(QDropEvent* event) {
|
||||
Application::getInstance()->dropEvent(event);
|
||||
}
|
||||
|
||||
// Pressing Alt (and Meta) key alone activates the menubar because its style inherits the
|
||||
// SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to
|
||||
|
|
|
@ -40,23 +40,8 @@ protected:
|
|||
virtual void initializeGL();
|
||||
virtual void paintGL();
|
||||
virtual void resizeGL(int width, int height);
|
||||
|
||||
virtual void keyPressEvent(QKeyEvent* event);
|
||||
virtual void keyReleaseEvent(QKeyEvent* event);
|
||||
|
||||
virtual void focusOutEvent(QFocusEvent* event);
|
||||
|
||||
virtual void mouseMoveEvent(QMouseEvent* event);
|
||||
virtual void mousePressEvent(QMouseEvent* event);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event);
|
||||
|
||||
virtual bool event(QEvent* event);
|
||||
|
||||
virtual void wheelEvent(QWheelEvent* event);
|
||||
|
||||
virtual void dragEnterEvent(QDragEnterEvent *event);
|
||||
virtual void dropEvent(QDropEvent* event);
|
||||
|
||||
private slots:
|
||||
void activeChanged(Qt::ApplicationState state);
|
||||
void throttleRender();
|
||||
|
|
|
@ -55,6 +55,10 @@ void MainWindow::saveGeometry() {
|
|||
}
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent* event) {
|
||||
qApp->quit();
|
||||
}
|
||||
|
||||
void MainWindow::moveEvent(QMoveEvent* event) {
|
||||
emit windowGeometryChanged(QRect(event->pos(), size()));
|
||||
QMainWindow::moveEvent(event);
|
||||
|
|
|
@ -30,6 +30,7 @@ signals:
|
|||
void windowShown(bool shown);
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent* event);
|
||||
virtual void moveEvent(QMoveEvent* event);
|
||||
virtual void resizeEvent(QResizeEvent* event);
|
||||
virtual void showEvent(QShowEvent* event);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <PathUtils.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <VrMenu.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "AccountManager.h"
|
||||
|
@ -30,7 +31,6 @@
|
|||
#include "devices/Faceshift.h"
|
||||
#include "devices/RealSense.h"
|
||||
#include "devices/SixenseManager.h"
|
||||
#include "devices/Visage.h"
|
||||
#include "MainWindow.h"
|
||||
#include "scripting/MenuScriptingInterface.h"
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
|
@ -53,7 +53,6 @@ Menu* Menu::getInstance() {
|
|||
|
||||
if (!_instance) {
|
||||
qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu.");
|
||||
|
||||
_instance = new Menu();
|
||||
}
|
||||
|
||||
|
@ -63,8 +62,7 @@ Menu* Menu::getInstance() {
|
|||
}
|
||||
|
||||
Menu::Menu() {
|
||||
QMenu* fileMenu = addMenu("File");
|
||||
|
||||
MenuWrapper * fileMenu = addMenu("File");
|
||||
#ifdef Q_OS_MAC
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole);
|
||||
#endif
|
||||
|
@ -97,7 +95,7 @@ Menu::Menu() {
|
|||
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::AddressBar,
|
||||
Qt::Key_Enter,
|
||||
Qt::CTRL | Qt::Key_L,
|
||||
dialogsManager.data(),
|
||||
SLOT(toggleAddressBar()));
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
|
@ -114,7 +112,7 @@ Menu::Menu() {
|
|||
QAction::QuitRole);
|
||||
|
||||
|
||||
QMenu* editMenu = addMenu("Edit");
|
||||
MenuWrapper* editMenu = addMenu("Edit");
|
||||
|
||||
QUndoStack* undoStack = qApp->getUndoStack();
|
||||
QAction* undoAction = undoStack->createUndoAction(editMenu);
|
||||
|
@ -137,7 +135,7 @@ Menu::Menu() {
|
|||
addActionToQMenuAndActionHash(editMenu, MenuOption::Animations, 0,
|
||||
dialogsManager.data(), SLOT(editAnimations()));
|
||||
|
||||
QMenu* toolsMenu = addMenu("Tools");
|
||||
MenuWrapper* toolsMenu = addMenu("Tools");
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S,
|
||||
dialogsManager.data(), SLOT(showScriptEditor()));
|
||||
|
||||
|
@ -151,33 +149,34 @@ Menu::Menu() {
|
|||
connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
|
||||
#endif
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, Qt::Key_Backslash,
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat,
|
||||
0, // QML Qt::Key_Backslash,
|
||||
dialogsManager.data(), SLOT(showIRCLink()));
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0,
|
||||
qApp, SLOT(showFriendsWindow()));
|
||||
|
||||
QMenu* visibilityMenu = toolsMenu->addMenu("I Am Visible To");
|
||||
MenuWrapper* visibilityMenu = toolsMenu->addMenu("I Am Visible To");
|
||||
{
|
||||
QActionGroup* visibilityGroup = new QActionGroup(toolsMenu);
|
||||
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
||||
|
||||
QAction* visibleToEveryone = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToEveryone,
|
||||
0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::All,
|
||||
this, SLOT(setVisibility()));
|
||||
discoverabilityManager.data(), SLOT(setVisibility()));
|
||||
visibilityGroup->addAction(visibleToEveryone);
|
||||
|
||||
QAction* visibleToFriends = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToFriends,
|
||||
0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends,
|
||||
this, SLOT(setVisibility()));
|
||||
discoverabilityManager.data(), SLOT(setVisibility()));
|
||||
visibilityGroup->addAction(visibleToFriends);
|
||||
|
||||
QAction* visibleToNoOne = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToNoOne,
|
||||
0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::None,
|
||||
this, SLOT(setVisibility()));
|
||||
discoverabilityManager.data(), SLOT(setVisibility()));
|
||||
visibilityGroup->addAction(visibleToNoOne);
|
||||
|
||||
connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged,
|
||||
this, &Menu::visibilityChanged);
|
||||
discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged);
|
||||
}
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
|
@ -194,30 +193,30 @@ Menu::Menu() {
|
|||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::ResetSensors,
|
||||
Qt::Key_Apostrophe,
|
||||
0, // QML Qt::Key_Apostrophe,
|
||||
qApp,
|
||||
SLOT(resetSensors()));
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
|
||||
qApp, SLOT(packageModel()));
|
||||
|
||||
QMenu* avatarMenu = addMenu("Avatar");
|
||||
MenuWrapper* avatarMenu = addMenu("Avatar");
|
||||
QObject* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
QMenu* avatarSizeMenu = avatarMenu->addMenu("Size");
|
||||
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::IncreaseAvatarSize,
|
||||
Qt::Key_Plus,
|
||||
0, // QML Qt::Key_Plus,
|
||||
avatar,
|
||||
SLOT(increaseSize()));
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::DecreaseAvatarSize,
|
||||
Qt::Key_Minus,
|
||||
0, // QML Qt::Key_Minus,
|
||||
avatar,
|
||||
SLOT(decreaseSize()));
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::ResetAvatarSize,
|
||||
Qt::Key_Equal,
|
||||
0, // QML Qt::Key_Equal,
|
||||
avatar,
|
||||
SLOT(resetSize()));
|
||||
|
||||
|
@ -233,35 +232,39 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false,
|
||||
avatar, SLOT(updateMotionBehavior()));
|
||||
|
||||
QMenu* viewMenu = addMenu("View");
|
||||
MenuWrapper* viewMenu = addMenu("View");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::Fullscreen,
|
||||
#ifdef Q_OS_MAC
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::Fullscreen,
|
||||
Qt::CTRL | Qt::META | Qt::Key_F,
|
||||
false,
|
||||
qApp,
|
||||
SLOT(setFullscreen(bool)));
|
||||
#else
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::Fullscreen,
|
||||
Qt::CTRL | Qt::Key_F,
|
||||
#endif
|
||||
false,
|
||||
qApp,
|
||||
SLOT(setFullscreen(bool)));
|
||||
#endif
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true,
|
||||
qApp,SLOT(cameraMenuChanged()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false,
|
||||
qApp, SLOT(cameraMenuChanged()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, Qt::META | Qt::Key_H,
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson,
|
||||
0, // QML Qt::Key_P,
|
||||
true, qApp, SLOT(cameraMenuChanged()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
|
||||
0, //QML Qt::SHIFT | Qt::Key_H,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror,
|
||||
0, // QML Qt::Key_H,
|
||||
false, qApp, SLOT(cameraMenuChanged()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools,
|
||||
#ifdef Q_OS_MAC
|
||||
Qt::META | Qt::Key_H,
|
||||
#else
|
||||
Qt::CTRL | Qt::Key_H,
|
||||
#endif
|
||||
false,
|
||||
dialogsManager.data(),
|
||||
SLOT(hmdTools(bool)));
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::EnableVRMode, 0,
|
||||
false,
|
||||
qApp,
|
||||
|
@ -273,7 +276,7 @@ Menu::Menu() {
|
|||
SLOT(setEnable3DTVMode(bool)));
|
||||
|
||||
|
||||
QMenu* nodeBordersMenu = viewMenu->addMenu("Server Borders");
|
||||
MenuWrapper* nodeBordersMenu = viewMenu->addMenu("Server Borders");
|
||||
NodeBounds& nodeBounds = qApp->getNodeBoundsDisplay();
|
||||
addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersEntityNodes,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_1, false,
|
||||
|
@ -282,23 +285,26 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::OffAxisProjection, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, qApp, SLOT(toggleLogDialog()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats);
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
qApp, SLOT(toggleLogDialog()));
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0,
|
||||
dialogsManager.data(), SLOT(bandwidthDetails()));
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0,
|
||||
dialogsManager.data(), SLOT(octreeStatsDetails()));
|
||||
|
||||
|
||||
QMenu* developerMenu = addMenu("Developer");
|
||||
MenuWrapper* developerMenu = addMenu("Developer");
|
||||
|
||||
QMenu* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true);
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere,
|
||||
0, // QML Qt::SHIFT | Qt::Key_A,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges);
|
||||
|
||||
QMenu* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight);
|
||||
MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight);
|
||||
QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu);
|
||||
ambientLightGroup->setExclusive(true);
|
||||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLightGlobal, 0, true));
|
||||
|
@ -313,14 +319,14 @@ Menu::Menu() {
|
|||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false));
|
||||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false));
|
||||
|
||||
QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows");
|
||||
MenuWrapper* shadowMenu = renderOptionsMenu->addMenu("Shadows");
|
||||
QActionGroup* shadowGroup = new QActionGroup(shadowMenu);
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
|
||||
|
||||
{
|
||||
QMenu* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate);
|
||||
MenuWrapper* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate);
|
||||
QActionGroup* framerateGroup = new QActionGroup(framerateMenu);
|
||||
framerateGroup->setExclusive(true);
|
||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true));
|
||||
|
@ -337,7 +343,7 @@ Menu::Menu() {
|
|||
}
|
||||
|
||||
|
||||
QMenu* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
|
||||
MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
|
||||
QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu);
|
||||
resolutionGroup->setExclusive(true);
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, true));
|
||||
|
@ -346,17 +352,20 @@ Menu::Menu() {
|
|||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars,
|
||||
0, // QML Qt::Key_Asterisk,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true,
|
||||
DependencyManager::get<GlowEffect>().data(), SLOT(toggleGlowEffect(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false);
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L,
|
||||
dialogsManager.data(), SLOT(lodTools()));
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
|
||||
0, // QML Qt::SHIFT | Qt::Key_L,
|
||||
dialogsManager.data(), SLOT(lodTools()));
|
||||
|
||||
QMenu* avatarDebugMenu = developerMenu->addMenu("Avatar");
|
||||
MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar");
|
||||
|
||||
QMenu* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking");
|
||||
MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking");
|
||||
{
|
||||
QActionGroup* faceTrackerGroup = new QActionGroup(avatarDebugMenu);
|
||||
|
||||
|
@ -371,19 +380,24 @@ Menu::Menu() {
|
|||
qApp, SLOT(setActiveFaceTracker()));
|
||||
faceTrackerGroup->addAction(faceshiftFaceTracker);
|
||||
#endif
|
||||
|
||||
QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::DDEFaceRegression,
|
||||
#ifdef HAVE_DDE
|
||||
QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera,
|
||||
0, false,
|
||||
qApp, SLOT(setActiveFaceTracker()));
|
||||
faceTrackerGroup->addAction(ddeFaceTracker);
|
||||
|
||||
#ifdef HAVE_VISAGE
|
||||
QAction* visageFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::Visage,
|
||||
0, false,
|
||||
qApp, SLOT(setActiveFaceTracker()));
|
||||
faceTrackerGroup->addAction(visageFaceTracker);
|
||||
#endif
|
||||
}
|
||||
#ifdef HAVE_DDE
|
||||
faceTrackingMenu->addSeparator();
|
||||
QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true);
|
||||
useAudioForMouth->setVisible(false);
|
||||
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);
|
||||
ddeFiltering->setVisible(false);
|
||||
#endif
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false,
|
||||
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSkeletonCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderHeadCollisionShapes);
|
||||
|
@ -391,14 +405,14 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
|
||||
|
||||
QMenu* handOptionsMenu = developerMenu->addMenu("Hands");
|
||||
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
||||
|
||||
QMenu* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
|
||||
MenuWrapper* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
|
||||
#ifdef __APPLE__
|
||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
||||
MenuOption::SixenseEnabled,
|
||||
|
@ -421,7 +435,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false);
|
||||
|
||||
QMenu* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
|
||||
MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
|
||||
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
|
||||
|
||||
#ifdef HAVE_RSSDK
|
||||
|
@ -430,7 +444,7 @@ Menu::Menu() {
|
|||
RealSense::getInstance(), SLOT(loadRSSDKFile()));
|
||||
#endif
|
||||
|
||||
QMenu* networkMenu = developerMenu->addMenu("Network");
|
||||
MenuWrapper* networkMenu = developerMenu->addMenu("Network");
|
||||
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(networkMenu,
|
||||
MenuOption::DisableActivityLogger,
|
||||
|
@ -443,8 +457,8 @@ Menu::Menu() {
|
|||
addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0,
|
||||
dialogsManager.data(), SLOT(toggleDiskCacheEditor()));
|
||||
|
||||
QMenu* timingMenu = developerMenu->addMenu("Timing and Stats");
|
||||
QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
MenuWrapper* timingMenu = developerMenu->addMenu("Timing and Stats");
|
||||
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
|
||||
|
@ -460,7 +474,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
QMenu* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction,
|
||||
0,
|
||||
true,
|
||||
|
@ -471,8 +485,6 @@ Menu::Menu() {
|
|||
audioIO.data(), SLOT(toggleServerEcho()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false,
|
||||
audioIO.data(), SLOT(toggleLocalEcho()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::StereoAudio, 0, false,
|
||||
audioIO.data(), SLOT(toggleStereoInput()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteAudio,
|
||||
Qt::CTRL | Qt::Key_M,
|
||||
false,
|
||||
|
@ -483,34 +495,10 @@ Menu::Menu() {
|
|||
0,
|
||||
audioIO.data(),
|
||||
SLOT(sendMuteEnvironmentPacket()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioSourceInject,
|
||||
0,
|
||||
false,
|
||||
audioIO.data(),
|
||||
SLOT(toggleAudioSourceInject()));
|
||||
QMenu* audioSourceMenu = audioDebugMenu->addMenu("Generated Audio Source");
|
||||
{
|
||||
QAction *pinkNoise = addCheckableActionToQMenuAndActionHash(audioSourceMenu, MenuOption::AudioSourcePinkNoise,
|
||||
0,
|
||||
false,
|
||||
audioIO.data(),
|
||||
SLOT(selectAudioSourcePinkNoise()));
|
||||
|
||||
QAction *sine440 = addCheckableActionToQMenuAndActionHash(audioSourceMenu, MenuOption::AudioSourceSine440,
|
||||
0,
|
||||
true,
|
||||
audioIO.data(),
|
||||
SLOT(selectAudioSourceSine440()));
|
||||
|
||||
QActionGroup* audioSourceGroup = new QActionGroup(audioSourceMenu);
|
||||
audioSourceGroup->addAction(pinkNoise);
|
||||
audioSourceGroup->addAction(sine440);
|
||||
}
|
||||
|
||||
auto scope = DependencyManager::get<AudioScope>();
|
||||
|
||||
QMenu* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope");
|
||||
MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope");
|
||||
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope,
|
||||
Qt::CTRL | Qt::Key_P, false,
|
||||
scope.data(),
|
||||
|
@ -559,7 +547,12 @@ Menu::Menu() {
|
|||
statsRenderer.data(),
|
||||
SLOT(toggleShowInjectedStreams()));
|
||||
|
||||
QMenu* helpMenu = addMenu("Help");
|
||||
|
||||
MenuWrapper* physicsOptionsMenu = developerMenu->addMenu("Physics");
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned);
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls);
|
||||
|
||||
MenuWrapper* helpMenu = addMenu("Help");
|
||||
addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp()));
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
|
@ -605,7 +598,7 @@ void Menu::scanMenu(QMenu& menu, settingsAction modifySetting, Settings& setting
|
|||
settings.endGroup();
|
||||
}
|
||||
|
||||
void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName, int menuItemLocation) {
|
||||
void Menu::addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, int menuItemLocation) {
|
||||
QAction* actionBefore = NULL;
|
||||
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
|
||||
actionBefore = destinationMenu->actions()[menuItemLocation];
|
||||
|
@ -625,7 +618,7 @@ void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString&
|
|||
}
|
||||
}
|
||||
|
||||
QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
||||
QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
const QObject* receiver,
|
||||
|
@ -662,7 +655,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
|||
return action;
|
||||
}
|
||||
|
||||
QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
||||
QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
QAction* action,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
|
@ -697,7 +690,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
|||
return action;
|
||||
}
|
||||
|
||||
QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
|
||||
QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
const bool checked,
|
||||
|
@ -713,7 +706,7 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
|
|||
return action;
|
||||
}
|
||||
|
||||
void Menu::removeAction(QMenu* menu, const QString& actionName) {
|
||||
void Menu::removeAction(MenuWrapper* menu, const QString& actionName) {
|
||||
menu->removeAction(_actionHash.value(actionName));
|
||||
_actionHash.remove(actionName);
|
||||
}
|
||||
|
@ -752,7 +745,7 @@ QAction* Menu::getActionForOption(const QString& menuOption) {
|
|||
return _actionHash.value(menuOption);
|
||||
}
|
||||
|
||||
QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) {
|
||||
QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) {
|
||||
QList<QAction*> menuActions;
|
||||
if (menu) {
|
||||
menuActions = menu->actions();
|
||||
|
@ -761,6 +754,7 @@ QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) {
|
|||
}
|
||||
|
||||
foreach (QAction* menuAction, menuActions) {
|
||||
QString actionText = menuAction->text();
|
||||
if (menuName == menuAction->text()) {
|
||||
return menuAction;
|
||||
}
|
||||
|
@ -768,18 +762,18 @@ QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
QMenu* Menu::getSubMenuFromName(const QString& menuName, QMenu* menu) {
|
||||
MenuWrapper* Menu::getSubMenuFromName(const QString& menuName, MenuWrapper* menu) {
|
||||
QAction* action = getActionFromName(menuName, menu);
|
||||
if (action) {
|
||||
return action->menu();
|
||||
return MenuWrapper::fromMenu(action->menu());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QMenu* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) {
|
||||
MenuWrapper* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
QMenu* parent = NULL;
|
||||
QMenu* menu = NULL;
|
||||
MenuWrapper* parent = NULL;
|
||||
MenuWrapper* menu = NULL;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
parent = menu;
|
||||
finalMenuPart = menuTreePart.trimmed();
|
||||
|
@ -791,10 +785,10 @@ QMenu* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) {
|
|||
return parent;
|
||||
}
|
||||
|
||||
QMenu* Menu::getMenu(const QString& menuName) {
|
||||
MenuWrapper* Menu::getMenu(const QString& menuName) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
QMenu* parent = NULL;
|
||||
QMenu* menu = NULL;
|
||||
MenuWrapper* parent = NULL;
|
||||
MenuWrapper* menu = NULL;
|
||||
int item = 0;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
menu = getSubMenuFromName(menuTreePart.trimmed(), parent);
|
||||
|
@ -809,19 +803,19 @@ QMenu* Menu::getMenu(const QString& menuName) {
|
|||
|
||||
QAction* Menu::getMenuAction(const QString& menuName) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
QMenu* parent = NULL;
|
||||
MenuWrapper* parent = NULL;
|
||||
QAction* action = NULL;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
action = getActionFromName(menuTreePart.trimmed(), parent);
|
||||
if (!action) {
|
||||
break;
|
||||
}
|
||||
parent = action->menu();
|
||||
parent = MenuWrapper::fromMenu(action->menu());
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
int Menu::findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem) {
|
||||
int Menu::findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem) {
|
||||
int position = 0;
|
||||
foreach(QAction* action, menu->actions()) {
|
||||
if (action->text() == searchMenuItem) {
|
||||
|
@ -832,7 +826,7 @@ int Menu::findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem) {
|
|||
return UNSPECIFIED_POSITION; // not found
|
||||
}
|
||||
|
||||
int Menu::positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition) {
|
||||
int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition) {
|
||||
QList<QAction*> menuActions = menu->actions();
|
||||
if (requestedPosition > 1 && requestedPosition < menuActions.size()) {
|
||||
QAction* beforeRequested = menuActions[requestedPosition - 1];
|
||||
|
@ -844,15 +838,15 @@ int Menu::positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition) {
|
|||
}
|
||||
|
||||
|
||||
QMenu* Menu::addMenu(const QString& menuName) {
|
||||
MenuWrapper* Menu::addMenu(const QString& menuName) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
QMenu* addTo = NULL;
|
||||
QMenu* menu = NULL;
|
||||
MenuWrapper* addTo = NULL;
|
||||
MenuWrapper* menu = NULL;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
menu = getSubMenuFromName(menuTreePart.trimmed(), addTo);
|
||||
if (!menu) {
|
||||
if (!addTo) {
|
||||
menu = QMenuBar::addMenu(menuTreePart.trimmed());
|
||||
menu = new MenuWrapper(QMenuBar::addMenu(menuTreePart.trimmed()));
|
||||
} else {
|
||||
menu = addTo->addMenu(menuTreePart.trimmed());
|
||||
}
|
||||
|
@ -870,7 +864,7 @@ void Menu::removeMenu(const QString& menuName) {
|
|||
// only proceed if the menu actually exists
|
||||
if (action) {
|
||||
QString finalMenuPart;
|
||||
QMenu* parent = getMenuParent(menuName, finalMenuPart);
|
||||
MenuWrapper* parent = getMenuParent(menuName, finalMenuPart);
|
||||
if (parent) {
|
||||
parent->removeAction(action);
|
||||
} else {
|
||||
|
@ -892,14 +886,14 @@ bool Menu::menuExists(const QString& menuName) {
|
|||
}
|
||||
|
||||
void Menu::addSeparator(const QString& menuName, const QString& separatorName) {
|
||||
QMenu* menuObj = getMenu(menuName);
|
||||
MenuWrapper* menuObj = getMenu(menuName);
|
||||
if (menuObj) {
|
||||
addDisabledActionAndSeparator(menuObj, separatorName);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::removeSeparator(const QString& menuName, const QString& separatorName) {
|
||||
QMenu* menu = getMenu(menuName);
|
||||
MenuWrapper* menu = getMenu(menuName);
|
||||
bool separatorRemoved = false;
|
||||
if (menu) {
|
||||
int textAt = findPositionOfMenuItem(menu, separatorName);
|
||||
|
@ -922,7 +916,7 @@ void Menu::removeSeparator(const QString& menuName, const QString& separatorName
|
|||
}
|
||||
|
||||
void Menu::addMenuItem(const MenuItemProperties& properties) {
|
||||
QMenu* menuObj = getMenu(properties.menuName);
|
||||
MenuWrapper* menuObj = getMenu(properties.menuName);
|
||||
if (menuObj) {
|
||||
QShortcut* shortcut = NULL;
|
||||
if (!properties.shortcutKeySequence.isEmpty()) {
|
||||
|
@ -963,7 +957,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
|
|||
}
|
||||
|
||||
void Menu::removeMenuItem(const QString& menu, const QString& menuitem) {
|
||||
QMenu* menuObj = getMenu(menu);
|
||||
MenuWrapper* menuObj = getMenu(menu);
|
||||
if (menuObj) {
|
||||
removeAction(menuObj, menuitem);
|
||||
QMenuBar::repaint();
|
||||
|
@ -978,28 +972,62 @@ bool Menu::menuItemExists(const QString& menu, const QString& menuitem) {
|
|||
return false;
|
||||
};
|
||||
|
||||
void Menu::setVisibility() {
|
||||
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::VisibleToEveryone)) {
|
||||
discoverabilityManager->setDiscoverabilityMode(Discoverability::All);
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::VisibleToFriends)) {
|
||||
discoverabilityManager->setDiscoverabilityMode(Discoverability::Friends);
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::VisibleToNoOne)) {
|
||||
discoverabilityManager->setDiscoverabilityMode(Discoverability::None);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "ERROR Menu::setVisibility() called with unrecognized value.";
|
||||
}
|
||||
MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) {
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addMenu(menu);
|
||||
});
|
||||
_backMap[menu] = this;
|
||||
}
|
||||
|
||||
void Menu::visibilityChanged(Discoverability::Mode discoverabilityMode) {
|
||||
if (discoverabilityMode == Discoverability::All) {
|
||||
setIsOptionChecked(MenuOption::VisibleToEveryone, true);
|
||||
} else if (discoverabilityMode == Discoverability::Friends) {
|
||||
setIsOptionChecked(MenuOption::VisibleToFriends, true);
|
||||
} else if (discoverabilityMode == Discoverability::None) {
|
||||
setIsOptionChecked(MenuOption::VisibleToNoOne, true);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "ERROR Menu::visibilityChanged() called with unrecognized value.";
|
||||
}
|
||||
QList<QAction*> MenuWrapper::actions() {
|
||||
return _realMenu->actions();
|
||||
}
|
||||
|
||||
MenuWrapper* MenuWrapper::addMenu(const QString& menuName) {
|
||||
return new MenuWrapper(_realMenu->addMenu(menuName));
|
||||
}
|
||||
|
||||
void MenuWrapper::setEnabled(bool enabled) {
|
||||
_realMenu->setEnabled(enabled);
|
||||
}
|
||||
|
||||
void MenuWrapper::addSeparator() {
|
||||
_realMenu->addSeparator();
|
||||
}
|
||||
|
||||
void MenuWrapper::addAction(QAction* action) {
|
||||
_realMenu->addAction(action);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addAction(_realMenu, action);
|
||||
});
|
||||
}
|
||||
|
||||
QAction* MenuWrapper::addAction(const QString& menuName) {
|
||||
QAction* action = _realMenu->addAction(menuName);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addAction(_realMenu, action);
|
||||
});
|
||||
return action;
|
||||
}
|
||||
|
||||
QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) {
|
||||
QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addAction(_realMenu, action);
|
||||
});
|
||||
return action;
|
||||
}
|
||||
|
||||
void MenuWrapper::removeAction(QAction* action) {
|
||||
_realMenu->removeAction(action);
|
||||
}
|
||||
|
||||
void MenuWrapper::insertAction(QAction* before, QAction* action) {
|
||||
_realMenu->insertAction(before, action);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->insertAction(before, action);
|
||||
});
|
||||
}
|
||||
|
||||
QHash<QMenu*, MenuWrapper*> MenuWrapper::_backMap;
|
||||
|
|
|
@ -25,6 +25,35 @@
|
|||
|
||||
class Settings;
|
||||
|
||||
class MenuWrapper : public QObject {
|
||||
public:
|
||||
QList<QAction*> actions();
|
||||
MenuWrapper* addMenu(const QString& menuName);
|
||||
void setEnabled(bool enabled = true);
|
||||
void addSeparator();
|
||||
void addAction(QAction* action);
|
||||
|
||||
QAction* addAction(const QString& menuName);
|
||||
void insertAction(QAction* before, QAction* menuName);
|
||||
|
||||
QAction* addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0);
|
||||
void removeAction(QAction* action);
|
||||
|
||||
QAction* newAction() {
|
||||
return new QAction(_realMenu);
|
||||
}
|
||||
private:
|
||||
MenuWrapper(QMenu* menu);
|
||||
|
||||
static MenuWrapper* fromMenu(QMenu* menu) {
|
||||
return _backMap[menu];
|
||||
}
|
||||
|
||||
QMenu* const _realMenu;
|
||||
static QHash<QMenu*, MenuWrapper*> _backMap;
|
||||
friend class Menu;
|
||||
};
|
||||
|
||||
class Menu : public QMenuBar {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -33,29 +62,29 @@ public:
|
|||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
||||
QMenu* getMenu(const QString& menuName);
|
||||
MenuWrapper* getMenu(const QString& menuName);
|
||||
|
||||
void triggerOption(const QString& menuOption);
|
||||
QAction* getActionForOption(const QString& menuOption);
|
||||
|
||||
QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
||||
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
const QObject* receiver = NULL,
|
||||
const char* member = NULL,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
||||
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
QAction* action,
|
||||
const QString& actionName = QString(),
|
||||
const QKeySequence& shortcut = 0,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
void removeAction(QMenu* menu, const QString& actionName);
|
||||
void removeAction(MenuWrapper* menu, const QString& actionName);
|
||||
|
||||
public slots:
|
||||
QMenu* addMenu(const QString& menuName);
|
||||
MenuWrapper* addMenu(const QString& menuName);
|
||||
void removeMenu(const QString& menuName);
|
||||
bool menuExists(const QString& menuName);
|
||||
void addSeparator(const QString& menuName, const QString& separatorName);
|
||||
|
@ -66,9 +95,6 @@ public slots:
|
|||
bool isOptionChecked(const QString& menuOption) const;
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
private slots:
|
||||
void setVisibility();
|
||||
|
||||
private:
|
||||
static Menu* _instance;
|
||||
Menu();
|
||||
|
@ -80,10 +106,10 @@ private:
|
|||
void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings);
|
||||
|
||||
/// helper method to have separators with labels that are also compatible with OS X
|
||||
void addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName,
|
||||
void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
QAction* addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
|
||||
QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
const bool checked = false,
|
||||
|
@ -91,15 +117,13 @@ private:
|
|||
const char* member = NULL,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
QAction* getActionFromName(const QString& menuName, QMenu* menu);
|
||||
QMenu* getSubMenuFromName(const QString& menuName, QMenu* menu);
|
||||
QMenu* getMenuParent(const QString& menuName, QString& finalMenuPart);
|
||||
QAction* getActionFromName(const QString& menuName, MenuWrapper* menu);
|
||||
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
|
||||
MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart);
|
||||
|
||||
QAction* getMenuAction(const QString& menuName);
|
||||
int findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem);
|
||||
int positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition);
|
||||
|
||||
void visibilityChanged(Discoverability::Mode discoverabilityMode);
|
||||
int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem);
|
||||
int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition);
|
||||
|
||||
QHash<QString, QAction*> _actionHash;
|
||||
};
|
||||
|
@ -123,9 +147,7 @@ namespace MenuOption {
|
|||
const QString AudioScopeTwentyFrames = "Twenty";
|
||||
const QString AudioStats = "Audio Stats";
|
||||
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
|
||||
const QString AudioSourceInject = "Generated Audio";
|
||||
const QString AudioSourcePinkNoise = "Pink Noise";
|
||||
const QString AudioSourceSine440 = "Sine 440hz";
|
||||
const QString AvatarReceiveStats = "Show Receive Stats";
|
||||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
|
||||
const QString BookmarkLocation = "Bookmark Location";
|
||||
|
@ -138,7 +160,6 @@ namespace MenuOption {
|
|||
const QString ControlWithSpeech = "Control With Speech";
|
||||
const QString CopyAddress = "Copy Address to Clipboard";
|
||||
const QString CopyPath = "Copy Path to Clipboard";
|
||||
const QString DDEFaceRegression = "DDE Face Regression";
|
||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||
const QString DeleteBookmark = "Delete Bookmark...";
|
||||
const QString DisableActivityLogger = "Disable Activity Logger";
|
||||
|
@ -194,7 +215,10 @@ namespace MenuOption {
|
|||
const QString OctreeStats = "Entity Statistics";
|
||||
const QString OffAxisProjection = "Off-Axis Projection";
|
||||
const QString OnlyDisplayTopTen = "Only Display Top Ten";
|
||||
const QString PackageModel = "Package Model...";
|
||||
const QString Pair = "Pair";
|
||||
const QString PhysicsShowOwned = "Highlight Simulation Ownership";
|
||||
const QString PhysicsShowHulls = "Draw Collision Hulls";
|
||||
const QString PipelineWarnings = "Log Render Pipeline Warnings";
|
||||
const QString Preferences = "Preferences...";
|
||||
const QString Quit = "Quit";
|
||||
|
@ -244,15 +268,15 @@ namespace MenuOption {
|
|||
const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations";
|
||||
const QString Stars = "Stars";
|
||||
const QString Stats = "Stats";
|
||||
const QString StereoAudio = "Stereo Audio (disables spatial sound)";
|
||||
const QString StopAllScripts = "Stop All Scripts";
|
||||
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
|
||||
const QString TestPing = "Test Ping";
|
||||
const QString ToolWindow = "Tool Window";
|
||||
const QString TransmitterDrive = "Transmitter Drive";
|
||||
const QString TurnWithHead = "Turn using Head";
|
||||
const QString PackageModel = "Package Model...";
|
||||
const QString Visage = "Visage";
|
||||
const QString UseAudioForMouth = "Use Audio for Mouth";
|
||||
const QString UseCamera = "Use Camera";
|
||||
const QString VelocityFilter = "Velocity Filter";
|
||||
const QString VisibleToEveryone = "Everyone";
|
||||
const QString VisibleToFriends = "Friends";
|
||||
const QString VisibleToNoOne = "No one";
|
||||
|
|
|
@ -50,7 +50,7 @@ public:
|
|||
TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin);
|
||||
const QString& getLocalPath() { return _localPath; }
|
||||
const QString& getFullPath() { return _fullPath; };
|
||||
const ScriptOrigin getOrigin() { return _origin; };
|
||||
ScriptOrigin getOrigin() { return _origin; };
|
||||
|
||||
private:
|
||||
QString _localPath;
|
||||
|
|
|
@ -581,7 +581,8 @@ void Avatar::renderBillboard() {
|
|||
glm::vec2 texCoordTopLeft(0.0f, 0.0f);
|
||||
glm::vec2 texCoordBottomRight(1.0f, 1.0f);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
|
@ -709,11 +710,24 @@ void Avatar::renderDisplayName() {
|
|||
glm::vec4(0.2f, 0.2f, 0.2f, _displayNameAlpha * DISPLAYNAME_BACKGROUND_ALPHA / DISPLAYNAME_ALPHA));
|
||||
|
||||
glm::vec4 color(0.93f, 0.93f, 0.93f, _displayNameAlpha);
|
||||
|
||||
// optionally render timing stats for this avatar with the display name
|
||||
QString renderedDisplayName = _displayName;
|
||||
|
||||
if (DependencyManager::get<AvatarManager>()->shouldShowReceiveStats()) {
|
||||
const float BYTES_PER_KILOBYTE = 1000.0f;
|
||||
float kilobytesPerSecond = getAverageBytesReceivedPerSecond() / BYTES_PER_KILOBYTE;
|
||||
|
||||
renderedDisplayName += QString(" - (%1 KBps, %2 Hz)")
|
||||
.arg(QString::number(kilobytesPerSecond, 'f', 2))
|
||||
.arg(getReceiveRate());
|
||||
}
|
||||
|
||||
QByteArray ba = _displayName.toLocal8Bit();
|
||||
const char* text = ba.data();
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
textRenderer(DISPLAYNAME)->draw(text_x, text_y, text, color);
|
||||
textRenderer(DISPLAYNAME)->draw(text_x, text_y, renderedDisplayName, color);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
|
@ -1058,3 +1072,20 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
|
|||
|
||||
}
|
||||
|
||||
// virtual
|
||||
void Avatar::rebuildSkeletonBody() {
|
||||
/* TODO: implement this and remove override from MyAvatar (when we have AvatarMotionStates working)
|
||||
if (_motionState) {
|
||||
// compute localAABox
|
||||
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
|
||||
float radius = capsule.getRadius();
|
||||
float height = 2.0f * (capsule.getHalfHeight() + radius);
|
||||
glm::vec3 corner(-radius, -0.5f * height, -radius);
|
||||
corner += _skeletonModel.getBoundingShapeOffset();
|
||||
glm::vec3 scale(2.0f * radius, height, 2.0f * radius);
|
||||
//_characterController.setLocalBoundingBox(corner, scale);
|
||||
_motionState->setBoundingBox(corner, scale);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
|
@ -162,6 +162,8 @@ public:
|
|||
// (otherwise floating point error will cause problems at large positions).
|
||||
void applyPositionDelta(const glm::vec3& delta);
|
||||
|
||||
virtual void rebuildSkeletonBody();
|
||||
|
||||
signals:
|
||||
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
|
||||
|
||||
|
@ -228,6 +230,8 @@ private:
|
|||
static int _jointConesID;
|
||||
|
||||
int _voiceSphereID;
|
||||
|
||||
//AvatarMotionState* _motionState = nullptr;
|
||||
};
|
||||
|
||||
#endif // hifi_Avatar_h
|
||||
|
|
|
@ -40,7 +40,9 @@ public:
|
|||
void renderAvatars(RenderArgs::RenderMode renderMode, bool postLighting = false, bool selfAvatarOnly = false);
|
||||
|
||||
void clearOtherAvatars();
|
||||
|
||||
|
||||
bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; }
|
||||
|
||||
class LocalLight {
|
||||
public:
|
||||
glm::vec3 color;
|
||||
|
@ -49,7 +51,10 @@ public:
|
|||
|
||||
Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
|
||||
Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;
|
||||
|
||||
|
||||
public slots:
|
||||
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
||||
|
||||
private:
|
||||
AvatarManager(QObject* parent = 0);
|
||||
AvatarManager(const AvatarManager& other);
|
||||
|
@ -59,7 +64,7 @@ private:
|
|||
|
||||
AvatarSharedPointer newSharedAvatar();
|
||||
|
||||
// virtual override
|
||||
// virtual overrides
|
||||
AvatarHash::iterator erase(const AvatarHash::iterator& iterator);
|
||||
|
||||
QVector<AvatarSharedPointer> _avatarFades;
|
||||
|
@ -67,6 +72,8 @@ private:
|
|||
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
|
||||
|
||||
QVector<AvatarManager::LocalLight> _localLights;
|
||||
|
||||
bool _shouldShowReceiveStats = false;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AvatarManager::LocalLight)
|
||||
|
|
|
@ -73,25 +73,6 @@ void Head::reset() {
|
|||
}
|
||||
|
||||
void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
||||
if (isMine) {
|
||||
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
||||
|
||||
// Only use face trackers when not playing back a recording.
|
||||
if (!myAvatar->isPlaying()) {
|
||||
FaceTracker* faceTracker = Application::getInstance()->getActiveFaceTracker();
|
||||
_isFaceTrackerConnected = faceTracker != NULL;
|
||||
if (_isFaceTrackerConnected) {
|
||||
_blendshapeCoefficients = faceTracker->getBlendshapeCoefficients();
|
||||
}
|
||||
}
|
||||
// Twist the upper body to follow the rotation of the head, but only do this with my avatar,
|
||||
// since everyone else will see the full joint rotations for other people.
|
||||
const float BODY_FOLLOW_HEAD_YAW_RATE = 0.1f;
|
||||
const float BODY_FOLLOW_HEAD_FACTOR = 0.66f;
|
||||
float currentTwist = getTorsoTwist();
|
||||
setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE);
|
||||
}
|
||||
|
||||
// Update audio trailing average for rendering facial animations
|
||||
const float AUDIO_AVERAGING_SECS = 0.05f;
|
||||
const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f;
|
||||
|
@ -102,7 +83,44 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
|||
} else {
|
||||
_longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f));
|
||||
}
|
||||
|
||||
|
||||
if (isMine) {
|
||||
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
||||
|
||||
// Only use face trackers when not playing back a recording.
|
||||
if (!myAvatar->isPlaying()) {
|
||||
FaceTracker* faceTracker = Application::getInstance()->getActiveFaceTracker();
|
||||
_isFaceTrackerConnected = faceTracker != NULL;
|
||||
if (_isFaceTrackerConnected) {
|
||||
_blendshapeCoefficients = faceTracker->getBlendshapeCoefficients();
|
||||
|
||||
if (typeid(*faceTracker) == typeid(DdeFaceTracker)
|
||||
&& Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) {
|
||||
|
||||
calculateMouthShapes();
|
||||
|
||||
const int JAW_OPEN_BLENDSHAPE = 21;
|
||||
const int MMMM_BLENDSHAPE = 34;
|
||||
const int FUNNEL_BLENDSHAPE = 40;
|
||||
const int SMILE_LEFT_BLENDSHAPE = 28;
|
||||
const int SMILE_RIGHT_BLENDSHAPE = 29;
|
||||
_blendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen;
|
||||
_blendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4;
|
||||
_blendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4;
|
||||
_blendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2;
|
||||
_blendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// Twist the upper body to follow the rotation of the head, but only do this with my avatar,
|
||||
// since everyone else will see the full joint rotations for other people.
|
||||
const float BODY_FOLLOW_HEAD_YAW_RATE = 0.1f;
|
||||
const float BODY_FOLLOW_HEAD_FACTOR = 0.66f;
|
||||
float currentTwist = getTorsoTwist();
|
||||
setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE);
|
||||
}
|
||||
|
||||
if (!(_isFaceTrackerConnected || billboard)) {
|
||||
// Update eye saccades
|
||||
const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
|
||||
|
@ -177,33 +195,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
|||
}
|
||||
|
||||
// use data to update fake Faceshift blendshape coefficients
|
||||
|
||||
const float JAW_OPEN_SCALE = 0.015f;
|
||||
const float JAW_OPEN_RATE = 0.9f;
|
||||
const float JAW_CLOSE_RATE = 0.90f;
|
||||
float audioDelta = sqrtf(glm::max(_averageLoudness - _longTermAverageLoudness, 0.0f)) * JAW_OPEN_SCALE;
|
||||
if (audioDelta > _audioJawOpen) {
|
||||
_audioJawOpen += (audioDelta - _audioJawOpen) * JAW_OPEN_RATE;
|
||||
} else {
|
||||
_audioJawOpen *= JAW_CLOSE_RATE;
|
||||
}
|
||||
_audioJawOpen = glm::clamp(_audioJawOpen, 0.0f, 1.0f);
|
||||
|
||||
// _mouth2 = "mmmm" shape
|
||||
// _mouth3 = "funnel" shape
|
||||
// _mouth4 = "smile" shape
|
||||
const float FUNNEL_PERIOD = 0.985f;
|
||||
const float FUNNEL_RANDOM_PERIOD = 0.01f;
|
||||
const float MMMM_POWER = 0.25f;
|
||||
const float MMMM_PERIOD = 0.91f;
|
||||
const float MMMM_RANDOM_PERIOD = 0.15f;
|
||||
const float SMILE_PERIOD = 0.925f;
|
||||
const float SMILE_RANDOM_PERIOD = 0.05f;
|
||||
|
||||
_mouth3 = glm::mix(_audioJawOpen, _mouth3, FUNNEL_PERIOD + randFloat() * FUNNEL_RANDOM_PERIOD);
|
||||
_mouth2 = glm::mix(_audioJawOpen * MMMM_POWER, _mouth2, MMMM_PERIOD + randFloat() * MMMM_RANDOM_PERIOD);
|
||||
_mouth4 = glm::mix(_audioJawOpen, _mouth4, SMILE_PERIOD + randFloat() * SMILE_RANDOM_PERIOD);
|
||||
|
||||
calculateMouthShapes();
|
||||
DependencyManager::get<Faceshift>()->updateFakeCoefficients(_leftEyeBlink,
|
||||
_rightEyeBlink,
|
||||
_browAudioLift,
|
||||
|
@ -230,6 +222,34 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
|||
|
||||
}
|
||||
|
||||
void Head::calculateMouthShapes() {
|
||||
const float JAW_OPEN_SCALE = 0.015f;
|
||||
const float JAW_OPEN_RATE = 0.9f;
|
||||
const float JAW_CLOSE_RATE = 0.90f;
|
||||
float audioDelta = sqrtf(glm::max(_averageLoudness - _longTermAverageLoudness, 0.0f)) * JAW_OPEN_SCALE;
|
||||
if (audioDelta > _audioJawOpen) {
|
||||
_audioJawOpen += (audioDelta - _audioJawOpen) * JAW_OPEN_RATE;
|
||||
} else {
|
||||
_audioJawOpen *= JAW_CLOSE_RATE;
|
||||
}
|
||||
_audioJawOpen = glm::clamp(_audioJawOpen, 0.0f, 1.0f);
|
||||
|
||||
// _mouth2 = "mmmm" shape
|
||||
// _mouth3 = "funnel" shape
|
||||
// _mouth4 = "smile" shape
|
||||
const float FUNNEL_PERIOD = 0.985f;
|
||||
const float FUNNEL_RANDOM_PERIOD = 0.01f;
|
||||
const float MMMM_POWER = 0.25f;
|
||||
const float MMMM_PERIOD = 0.91f;
|
||||
const float MMMM_RANDOM_PERIOD = 0.15f;
|
||||
const float SMILE_PERIOD = 0.925f;
|
||||
const float SMILE_RANDOM_PERIOD = 0.05f;
|
||||
|
||||
_mouth3 = glm::mix(_audioJawOpen, _mouth3, FUNNEL_PERIOD + randFloat() * FUNNEL_RANDOM_PERIOD);
|
||||
_mouth2 = glm::mix(_audioJawOpen * MMMM_POWER, _mouth2, MMMM_PERIOD + randFloat() * MMMM_RANDOM_PERIOD);
|
||||
_mouth4 = glm::mix(_audioJawOpen, _mouth4, SMILE_PERIOD + randFloat() * SMILE_RANDOM_PERIOD);
|
||||
}
|
||||
|
||||
void Head::relaxLean(float deltaTime) {
|
||||
// restore rotation, lean to neutral positions
|
||||
const float LEAN_RELAXATION_PERIOD = 0.25f; // seconds
|
||||
|
|
|
@ -81,7 +81,7 @@ public:
|
|||
FaceModel& getFaceModel() { return _faceModel; }
|
||||
const FaceModel& getFaceModel() const { return _faceModel; }
|
||||
|
||||
const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
|
||||
bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
|
||||
float getAverageLoudness() const { return _averageLoudness; }
|
||||
/// \return the point about which scaling occurs.
|
||||
glm::vec3 getScalePivot() const;
|
||||
|
@ -154,6 +154,7 @@ private:
|
|||
|
||||
// private methods
|
||||
void renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition);
|
||||
void calculateMouthShapes();
|
||||
|
||||
friend class FaceModel;
|
||||
};
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <ShapeCollider.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <TextRenderer.h>
|
||||
#include <UserActivityLogger.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "AvatarManager.h"
|
||||
|
@ -126,7 +127,7 @@ void MyAvatar::reset() {
|
|||
_skeletonModel.reset();
|
||||
getHead()->reset();
|
||||
|
||||
_velocity = glm::vec3(0.0f);
|
||||
_targetVelocity = glm::vec3(0.0f);
|
||||
setThrust(glm::vec3(0.0f));
|
||||
// Reset the pitch and roll components of the avatar's orientation, preserve yaw direction
|
||||
glm::vec3 eulers = safeEulerAngles(getOrientation());
|
||||
|
@ -165,7 +166,6 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
if (_scale != _targetScale) {
|
||||
float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale;
|
||||
setScale(scale);
|
||||
Application::getInstance()->getCamera()->setScale(scale);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -607,10 +607,15 @@ void MyAvatar::saveData() {
|
|||
|
||||
settings.setValue("leanScale", _leanScale);
|
||||
settings.setValue("scale", _targetScale);
|
||||
|
||||
settings.setValue("faceModelURL", _faceModelURL);
|
||||
settings.setValue("skeletonModelURL", _skeletonModelURL);
|
||||
|
||||
|
||||
settings.setValue("useFullAvatar", _useFullAvatar);
|
||||
settings.setValue("fullAvatarURL", _fullAvatarURLFromPreferences);
|
||||
settings.setValue("faceModelURL", _headURLFromPreferences);
|
||||
settings.setValue("skeletonModelURL", _skeletonURLFromPreferences);
|
||||
settings.setValue("headModelName", _headModelName);
|
||||
settings.setValue("bodyModelName", _bodyModelName);
|
||||
settings.setValue("fullAvatarModelName", _fullAvatarModelName);
|
||||
|
||||
settings.beginWriteArray("attachmentData");
|
||||
for (int i = 0; i < _attachmentData.size(); i++) {
|
||||
settings.setArrayIndex(i);
|
||||
|
@ -642,7 +647,6 @@ void MyAvatar::saveData() {
|
|||
settings.setValue("firstFrame", pointer->getFirstFrame());
|
||||
settings.setValue("lastFrame", pointer->getLastFrame());
|
||||
settings.setValue("maskedJoints", pointer->getMaskedJoints());
|
||||
settings.setValue("running", pointer->getLoop() && pointer->isRunning());
|
||||
}
|
||||
settings.endArray();
|
||||
|
||||
|
@ -670,10 +674,62 @@ void MyAvatar::loadData() {
|
|||
_leanScale = loadSetting(settings, "leanScale", 0.05f);
|
||||
_targetScale = loadSetting(settings, "scale", 1.0f);
|
||||
setScale(_scale);
|
||||
Application::getInstance()->getCamera()->setScale(_scale);
|
||||
|
||||
// The old preferences only stored the face and skeleton URLs, we didn't track if the user wanted to use 1 or 2 urls
|
||||
// for their avatar, So we need to attempt to detect this old case and set our new preferences accordingly. If
|
||||
// the head URL is empty, then we will assume they are using a full url...
|
||||
bool isOldSettings = !(settings.contains("useFullAvatar") || settings.contains("fullAvatarURL"));
|
||||
|
||||
_useFullAvatar = settings.value("useFullAvatar").toBool();
|
||||
_headURLFromPreferences = settings.value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl();
|
||||
_fullAvatarURLFromPreferences = settings.value("fullAvatarURL", DEFAULT_FULL_AVATAR_MODEL_URL).toUrl();
|
||||
_skeletonURLFromPreferences = settings.value("skeletonModelURL", DEFAULT_BODY_MODEL_URL).toUrl();
|
||||
_headModelName = settings.value("headModelName", DEFAULT_HEAD_MODEL_NAME).toString();
|
||||
_bodyModelName = settings.value("bodyModelName", DEFAULT_BODY_MODEL_NAME).toString();
|
||||
_fullAvatarModelName = settings.value("fullAvatarModelName", DEFAULT_FULL_AVATAR_MODEL_NAME).toString();
|
||||
|
||||
setFaceModelURL(settings.value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl());
|
||||
setSkeletonModelURL(settings.value("skeletonModelURL").toUrl());
|
||||
if (isOldSettings) {
|
||||
bool assumeFullAvatar = _headURLFromPreferences.isEmpty();
|
||||
_useFullAvatar = assumeFullAvatar;
|
||||
|
||||
if (_useFullAvatar) {
|
||||
_fullAvatarURLFromPreferences = settings.value("skeletonModelURL").toUrl();
|
||||
_headURLFromPreferences = DEFAULT_HEAD_MODEL_URL;
|
||||
_skeletonURLFromPreferences = DEFAULT_BODY_MODEL_URL;
|
||||
|
||||
QVariantHash fullAvatarFST = FSTReader::downloadMapping(_fullAvatarURLFromPreferences.toString());
|
||||
|
||||
_headModelName = "Default";
|
||||
_bodyModelName = "Default";
|
||||
_fullAvatarModelName = fullAvatarFST["name"].toString();
|
||||
|
||||
} else {
|
||||
_fullAvatarURLFromPreferences = DEFAULT_FULL_AVATAR_MODEL_URL;
|
||||
_skeletonURLFromPreferences = settings.value("skeletonModelURL", DEFAULT_BODY_MODEL_URL).toUrl();
|
||||
|
||||
if (_skeletonURLFromPreferences == DEFAULT_BODY_MODEL_URL) {
|
||||
_bodyModelName = DEFAULT_BODY_MODEL_NAME;
|
||||
} else {
|
||||
QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString());
|
||||
_bodyModelName = bodyFST["name"].toString();
|
||||
}
|
||||
|
||||
if (_headURLFromPreferences == DEFAULT_HEAD_MODEL_URL) {
|
||||
_headModelName = DEFAULT_HEAD_MODEL_NAME;
|
||||
} else {
|
||||
QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString());
|
||||
_headModelName = headFST["name"].toString();
|
||||
}
|
||||
|
||||
_fullAvatarModelName = "Default";
|
||||
}
|
||||
}
|
||||
|
||||
if (_useFullAvatar) {
|
||||
useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
|
||||
} else {
|
||||
useHeadAndBodyURLs(_headURLFromPreferences, _skeletonURLFromPreferences, _headModelName, _bodyModelName);
|
||||
}
|
||||
|
||||
QVector<AttachmentData> attachmentData;
|
||||
int attachmentCount = settings.beginReadArray("attachmentData");
|
||||
|
@ -712,13 +768,10 @@ void MyAvatar::loadData() {
|
|||
handle->setPriority(loadSetting(settings, "priority", 1.0f));
|
||||
handle->setLoop(settings.value("loop", true).toBool());
|
||||
handle->setHold(settings.value("hold", false).toBool());
|
||||
handle->setStartAutomatically(settings.value("startAutomatically", true).toBool());
|
||||
handle->setFirstFrame(settings.value("firstFrame", 0.0f).toFloat());
|
||||
handle->setLastFrame(settings.value("lastFrame", INT_MAX).toFloat());
|
||||
handle->setMaskedJoints(settings.value("maskedJoints").toStringList());
|
||||
if (settings.value("loop", true).toBool() && settings.value("running", false).toBool()) {
|
||||
handle->setRunning(true);
|
||||
}
|
||||
handle->setStartAutomatically(settings.value("startAutomatically", true).toBool());
|
||||
}
|
||||
settings.endArray();
|
||||
|
||||
|
@ -903,6 +956,29 @@ void MyAvatar::clearJointAnimationPriorities() {
|
|||
}
|
||||
}
|
||||
|
||||
QString MyAvatar::getModelDescription() const {
|
||||
QString result;
|
||||
if (_useFullAvatar) {
|
||||
if (!getFullAvartarModelName().isEmpty()) {
|
||||
result = "Full Avatar \"" + getFullAvartarModelName() + "\"";
|
||||
} else {
|
||||
result = "Full Avatar \"" + _fullAvatarURLFromPreferences.fileName() + "\"";
|
||||
}
|
||||
} else {
|
||||
if (!getHeadModelName().isEmpty()) {
|
||||
result = "Head \"" + getHeadModelName() + "\"";
|
||||
} else {
|
||||
result = "Head \"" + _headURLFromPreferences.fileName() + "\"";
|
||||
}
|
||||
if (!getBodyModelName().isEmpty()) {
|
||||
result += " and Body \"" + getBodyModelName() + "\"";
|
||||
} else {
|
||||
result += " and Body \"" + _skeletonURLFromPreferences.fileName() + "\"";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) {
|
||||
Avatar::setFaceModelURL(faceModelURL);
|
||||
_billboardValid = false;
|
||||
|
@ -913,6 +989,90 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
_billboardValid = false;
|
||||
}
|
||||
|
||||
void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "useFullAvatarURL", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QUrl&, fullAvatarURL),
|
||||
Q_ARG(const QString&, modelName));
|
||||
return;
|
||||
}
|
||||
|
||||
_useFullAvatar = true;
|
||||
|
||||
if (_fullAvatarURLFromPreferences != fullAvatarURL) {
|
||||
_fullAvatarURLFromPreferences = fullAvatarURL;
|
||||
if (modelName.isEmpty()) {
|
||||
QVariantHash fullAvatarFST = FSTReader::downloadMapping(_fullAvatarURLFromPreferences.toString());
|
||||
_fullAvatarModelName = fullAvatarFST["name"].toString();
|
||||
} else {
|
||||
_fullAvatarModelName = modelName;
|
||||
}
|
||||
}
|
||||
|
||||
if (!getFaceModelURLString().isEmpty()) {
|
||||
setFaceModelURL(QString());
|
||||
}
|
||||
|
||||
if (fullAvatarURL != getSkeletonModelURL()) {
|
||||
setSkeletonModelURL(fullAvatarURL);
|
||||
UserActivityLogger::getInstance().changedModel("skeleton", fullAvatarURL.toString());
|
||||
}
|
||||
sendIdentityPacket();
|
||||
}
|
||||
|
||||
void MyAvatar::useHeadURL(const QUrl& headURL, const QString& modelName) {
|
||||
useHeadAndBodyURLs(headURL, _skeletonURLFromPreferences, modelName, _bodyModelName);
|
||||
}
|
||||
|
||||
void MyAvatar::useBodyURL(const QUrl& bodyURL, const QString& modelName) {
|
||||
useHeadAndBodyURLs(_headURLFromPreferences, bodyURL, _headModelName, modelName);
|
||||
}
|
||||
|
||||
void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, const QString& headName, const QString& bodyName) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "useFullAvatarURL", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QUrl&, headURL),
|
||||
Q_ARG(const QUrl&, bodyURL),
|
||||
Q_ARG(const QString&, headName),
|
||||
Q_ARG(const QString&, bodyName));
|
||||
return;
|
||||
}
|
||||
|
||||
_useFullAvatar = false;
|
||||
|
||||
if (_headURLFromPreferences != headURL) {
|
||||
_headURLFromPreferences = headURL;
|
||||
if (headName.isEmpty()) {
|
||||
QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString());
|
||||
_headModelName = headFST["name"].toString();
|
||||
} else {
|
||||
_headModelName = headName;
|
||||
}
|
||||
}
|
||||
|
||||
if (_skeletonURLFromPreferences != bodyURL) {
|
||||
_skeletonURLFromPreferences = bodyURL;
|
||||
if (bodyName.isEmpty()) {
|
||||
QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString());
|
||||
_bodyModelName = bodyFST["name"].toString();
|
||||
} else {
|
||||
_bodyModelName = bodyName;
|
||||
}
|
||||
}
|
||||
|
||||
if (headURL != getFaceModelURL()) {
|
||||
setFaceModelURL(headURL);
|
||||
UserActivityLogger::getInstance().changedModel("head", headURL.toString());
|
||||
}
|
||||
|
||||
if (bodyURL != getSkeletonModelURL()) {
|
||||
setSkeletonModelURL(bodyURL);
|
||||
UserActivityLogger::getInstance().changedModel("skeleton", bodyURL.toString());
|
||||
}
|
||||
sendIdentityPacket();
|
||||
}
|
||||
|
||||
|
||||
void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||
Avatar::setAttachmentData(attachmentData);
|
||||
if (QThread::currentThread() != thread()) {
|
||||
|
@ -938,7 +1098,7 @@ glm::vec3 MyAvatar::getSkeletonPosition() const {
|
|||
return Avatar::getPosition();
|
||||
}
|
||||
|
||||
void MyAvatar::updateCharacterController() {
|
||||
void MyAvatar::rebuildSkeletonBody() {
|
||||
// compute localAABox
|
||||
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
|
||||
float radius = capsule.getRadius();
|
||||
|
@ -1253,28 +1413,28 @@ glm::vec3 MyAvatar::applyScriptedMotor(float deltaTime, const glm::vec3& localVe
|
|||
void MyAvatar::updatePosition(float deltaTime) {
|
||||
// rotate velocity into camera frame
|
||||
glm::quat rotation = getHead()->getCameraOrientation();
|
||||
glm::vec3 localVelocity = glm::inverse(rotation) * _velocity;
|
||||
glm::vec3 localVelocity = glm::inverse(rotation) * _targetVelocity;
|
||||
|
||||
bool isHovering = _characterController.isHovering();
|
||||
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isHovering);
|
||||
newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity);
|
||||
|
||||
// rotate back into world-frame
|
||||
_velocity = rotation * newLocalVelocity;
|
||||
_targetVelocity = rotation * newLocalVelocity;
|
||||
|
||||
_velocity += _thrust * deltaTime;
|
||||
_targetVelocity += _thrust * deltaTime;
|
||||
_thrust = glm::vec3(0.0f);
|
||||
|
||||
// cap avatar speed
|
||||
float speed = glm::length(_velocity);
|
||||
float speed = glm::length(_targetVelocity);
|
||||
if (speed > MAX_AVATAR_SPEED) {
|
||||
_velocity *= MAX_AVATAR_SPEED / speed;
|
||||
_targetVelocity *= MAX_AVATAR_SPEED / speed;
|
||||
speed = MAX_AVATAR_SPEED;
|
||||
}
|
||||
|
||||
if (speed > MIN_AVATAR_SPEED && !_characterController.isEnabled()) {
|
||||
// update position ourselves
|
||||
applyPositionDelta(deltaTime * _velocity);
|
||||
applyPositionDelta(deltaTime * _targetVelocity);
|
||||
measureMotionDerivatives(deltaTime);
|
||||
} // else physics will move avatar later
|
||||
|
||||
|
|
|
@ -116,14 +116,28 @@ public:
|
|||
virtual void setJointData(int index, const glm::quat& rotation);
|
||||
virtual void clearJointData(int index);
|
||||
virtual void clearJointsData();
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
|
||||
Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString());
|
||||
Q_INVOKABLE void useHeadURL(const QUrl& headURL, const QString& modelName = QString());
|
||||
Q_INVOKABLE void useBodyURL(const QUrl& bodyURL, const QString& modelName = QString());
|
||||
Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, const QString& headName = QString(), const QString& bodyName = QString());
|
||||
|
||||
Q_INVOKABLE bool getUseFullAvatar() const { return _useFullAvatar; }
|
||||
Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; }
|
||||
Q_INVOKABLE const QUrl& getHeadURLFromPreferences() const { return _headURLFromPreferences; }
|
||||
Q_INVOKABLE const QUrl& getBodyURLFromPreferences() const { return _skeletonURLFromPreferences; }
|
||||
|
||||
Q_INVOKABLE const QString& getHeadModelName() const { return _headModelName; }
|
||||
Q_INVOKABLE const QString& getBodyModelName() const { return _bodyModelName; }
|
||||
Q_INVOKABLE const QString& getFullAvartarModelName() const { return _fullAvatarModelName; }
|
||||
|
||||
Q_INVOKABLE QString getModelDescription() const;
|
||||
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||
|
||||
virtual glm::vec3 getSkeletonPosition() const;
|
||||
void updateLocalAABox();
|
||||
DynamicCharacterController* getCharacterController() { return &_characterController; }
|
||||
void updateCharacterController();
|
||||
|
||||
void clearJointAnimationPriorities();
|
||||
|
||||
|
@ -177,6 +191,8 @@ public slots:
|
|||
void stopRecording();
|
||||
void saveRecording(QString filename);
|
||||
void loadLastRecording();
|
||||
|
||||
virtual void rebuildSkeletonBody();
|
||||
|
||||
signals:
|
||||
void transformChanged();
|
||||
|
@ -185,6 +201,11 @@ protected:
|
|||
virtual void renderAttachments(RenderArgs::RenderMode renderMode, RenderArgs* args);
|
||||
|
||||
private:
|
||||
|
||||
// These are made private for MyAvatar so that you will use the "use" methods instead
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
|
||||
float _turningKeyPressTime;
|
||||
glm::vec3 _gravity;
|
||||
|
||||
|
@ -229,6 +250,16 @@ private:
|
|||
void updatePosition(float deltaTime);
|
||||
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
|
||||
void maybeUpdateBillboard();
|
||||
|
||||
// Avatar Preferences
|
||||
bool _useFullAvatar = false;
|
||||
QUrl _fullAvatarURLFromPreferences;
|
||||
QUrl _headURLFromPreferences;
|
||||
QUrl _skeletonURLFromPreferences;
|
||||
|
||||
QString _headModelName;
|
||||
QString _bodyModelName;
|
||||
QString _fullAvatarModelName;
|
||||
};
|
||||
|
||||
#endif // hifi_MyAvatar_h
|
||||
|
|
|
@ -40,13 +40,14 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
|
|||
_clampedFootPosition(0.0f),
|
||||
_headClipDistance(DEFAULT_NEAR_CLIP)
|
||||
{
|
||||
assert(_owningAvatar);
|
||||
}
|
||||
|
||||
SkeletonModel::~SkeletonModel() {
|
||||
}
|
||||
|
||||
void SkeletonModel::setJointStates(QVector<JointState> states) {
|
||||
Model::setJointStates(states);
|
||||
void SkeletonModel::initJointStates(QVector<JointState> states) {
|
||||
Model::initJointStates(states);
|
||||
|
||||
// Determine the default eye position for avatar scale = 1.0
|
||||
int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
|
||||
|
@ -83,6 +84,7 @@ void SkeletonModel::setJointStates(QVector<JointState> states) {
|
|||
_headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z);
|
||||
_headClipDistance = std::max(_headClipDistance, DEFAULT_NEAR_CLIP);
|
||||
|
||||
_owningAvatar->rebuildSkeletonBody();
|
||||
emit skeletonLoaded();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL);
|
||||
~SkeletonModel();
|
||||
|
||||
void setJointStates(QVector<JointState> states);
|
||||
virtual void initJointStates(QVector<JointState> states);
|
||||
|
||||
void simulate(float deltaTime, bool fullUpdate = true);
|
||||
|
||||
|
|
|
@ -11,18 +11,32 @@
|
|||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QElapsedTimer>
|
||||
#include <QTimer>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include "DdeFaceTracker.h"
|
||||
#include "FaceshiftConstants.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "Menu.h"
|
||||
|
||||
|
||||
static const QHostAddress DDE_FEATURE_POINT_SERVER_ADDR("127.0.0.1");
|
||||
static const quint16 DDE_FEATURE_POINT_SERVER_PORT = 5555;
|
||||
static const QHostAddress DDE_SERVER_ADDR("127.0.0.1");
|
||||
static const quint16 DDE_SERVER_PORT = 64204;
|
||||
static const quint16 DDE_CONTROL_PORT = 64205;
|
||||
#if defined(Q_OS_WIN)
|
||||
static const QString DDE_PROGRAM_PATH = "/dde/dde.exe";
|
||||
#elif defined(Q_OS_MAC)
|
||||
static const QString DDE_PROGRAM_PATH = "/dde.app/Contents/MacOS/dde";
|
||||
#endif
|
||||
static const QStringList DDE_ARGUMENTS = QStringList()
|
||||
<< "--udp=" + DDE_SERVER_ADDR.toString() + ":" + QString::number(DDE_SERVER_PORT)
|
||||
<< "--receiver=" + QString::number(DDE_CONTROL_PORT)
|
||||
<< "--headless";
|
||||
|
||||
static const int NUM_EXPRESSIONS = 46;
|
||||
static const int MIN_PACKET_SIZE = (8 + NUM_EXPRESSIONS) * sizeof(float) + sizeof(int);
|
||||
|
@ -120,15 +134,20 @@ struct Packet {
|
|||
char name[MAX_NAME_SIZE + 1];
|
||||
};
|
||||
|
||||
const float STARTING_DDE_MESSAGE_TIME = 0.033f;
|
||||
|
||||
DdeFaceTracker::DdeFaceTracker() :
|
||||
DdeFaceTracker(QHostAddress::Any, DDE_FEATURE_POINT_SERVER_PORT)
|
||||
DdeFaceTracker(QHostAddress::Any, DDE_SERVER_PORT, DDE_CONTROL_PORT)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 port) :
|
||||
DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, quint16 controlPort) :
|
||||
_ddeProcess(NULL),
|
||||
_ddeStopping(false),
|
||||
_host(host),
|
||||
_port(port),
|
||||
_serverPort(serverPort),
|
||||
_controlPort(controlPort),
|
||||
_lastReceiveTimestamp(0),
|
||||
_reset(false),
|
||||
_leftBlinkIndex(0), // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes
|
||||
|
@ -143,11 +162,16 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 port) :
|
|||
_mouthSmileLeftIndex(28),
|
||||
_mouthSmileRightIndex(29),
|
||||
_jawOpenIndex(21),
|
||||
_previousTranslation(glm::vec3()),
|
||||
_previousRotation(glm::quat())
|
||||
_lastMessageReceived(0),
|
||||
_averageMessageTime(STARTING_DDE_MESSAGE_TIME),
|
||||
_lastHeadTranslation(glm::vec3(0.0f)),
|
||||
_filteredHeadTranslation(glm::vec3(0.0f)),
|
||||
_lastLeftEyeBlink(0.0f),
|
||||
_filteredLeftEyeBlink(0.0f),
|
||||
_lastRightEyeBlink(0.0f),
|
||||
_filteredRightEyeBlink(0.0f)
|
||||
{
|
||||
_coefficients.resize(NUM_FACESHIFT_BLENDSHAPES);
|
||||
_previousCoefficients.resize(NUM_FACESHIFT_BLENDSHAPES);
|
||||
|
||||
_blendshapeCoefficients.resize(NUM_FACESHIFT_BLENDSHAPES);
|
||||
|
||||
|
@ -157,16 +181,65 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 port) :
|
|||
}
|
||||
|
||||
DdeFaceTracker::~DdeFaceTracker() {
|
||||
if (_udpSocket.isOpen()) {
|
||||
_udpSocket.close();
|
||||
}
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
void DdeFaceTracker::setEnabled(bool enabled) {
|
||||
#ifdef HAVE_DDE
|
||||
// isOpen() does not work as one might expect on QUdpSocket; don't test isOpen() before closing socket.
|
||||
_udpSocket.close();
|
||||
if (enabled) {
|
||||
_udpSocket.bind(_host, _port);
|
||||
_udpSocket.bind(_host, _serverPort);
|
||||
}
|
||||
|
||||
const char* DDE_EXIT_COMMAND = "exit";
|
||||
|
||||
if (enabled && !_ddeProcess) {
|
||||
// Terminate any existing DDE process, perhaps left running after an Interface crash
|
||||
_udpSocket.writeDatagram(DDE_EXIT_COMMAND, DDE_SERVER_ADDR, _controlPort);
|
||||
_ddeStopping = false;
|
||||
|
||||
qCDebug(interfaceapp) << "DDE Face Tracker: Starting";
|
||||
_ddeProcess = new QProcess(qApp);
|
||||
connect(_ddeProcess, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus)));
|
||||
_ddeProcess->start(QCoreApplication::applicationDirPath() + DDE_PROGRAM_PATH, DDE_ARGUMENTS);
|
||||
}
|
||||
|
||||
if (!enabled && _ddeProcess) {
|
||||
_ddeStopping = true;
|
||||
_udpSocket.writeDatagram(DDE_EXIT_COMMAND, DDE_SERVER_ADDR, _controlPort);
|
||||
qCDebug(interfaceapp) << "DDE Face Tracker: Stopping";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DdeFaceTracker::processFinished(int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
if (_ddeProcess) {
|
||||
if (_ddeStopping) {
|
||||
qCDebug(interfaceapp) << "DDE Face Tracker: Stopped";
|
||||
|
||||
} else {
|
||||
qCWarning(interfaceapp) << "DDE Face Tracker: Stopped unexpectedly";
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::NoFaceTracking, true);
|
||||
}
|
||||
_udpSocket.close();
|
||||
delete _ddeProcess;
|
||||
_ddeProcess = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void DdeFaceTracker::reset() {
|
||||
if (_udpSocket.state() == QAbstractSocket::BoundState) {
|
||||
_reset = true;
|
||||
|
||||
qCDebug(interfaceapp) << "DDE Face Tracker: Reset";
|
||||
|
||||
const char* DDE_RESET_COMMAND = "reset";
|
||||
_udpSocket.writeDatagram(DDE_RESET_COMMAND, DDE_SERVER_ADDR, _controlPort);
|
||||
|
||||
FaceTracker::reset();
|
||||
|
||||
_reset = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,7 +250,7 @@ bool DdeFaceTracker::isActive() const {
|
|||
|
||||
//private slots and methods
|
||||
void DdeFaceTracker::socketErrorOccurred(QAbstractSocket::SocketError socketError) {
|
||||
qCDebug(interfaceapp) << "[Error] DDE Face Tracker Socket Error: " << _udpSocket.errorString();
|
||||
qCWarning(interfaceapp) << "DDE Face Tracker: Socket error: " << _udpSocket.errorString();
|
||||
}
|
||||
|
||||
void DdeFaceTracker::socketStateChanged(QAbstractSocket::SocketState socketState) {
|
||||
|
@ -205,7 +278,7 @@ void DdeFaceTracker::socketStateChanged(QAbstractSocket::SocketState socketState
|
|||
state = "Unconnected";
|
||||
break;
|
||||
}
|
||||
qCDebug(interfaceapp) << "[Info] DDE Face Tracker Socket: " << state;
|
||||
qCDebug(interfaceapp) << "DDE Face Tracker: Socket: " << state;
|
||||
}
|
||||
|
||||
void DdeFaceTracker::readPendingDatagrams() {
|
||||
|
@ -223,6 +296,8 @@ float DdeFaceTracker::getBlendshapeCoefficient(int index) const {
|
|||
|
||||
void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
||||
if(buffer.size() > MIN_PACKET_SIZE) {
|
||||
bool isFiltering = Menu::getInstance()->isOptionChecked(MenuOption::VelocityFilter);
|
||||
|
||||
Packet packet;
|
||||
int bytesToCopy = glm::min((int)sizeof(packet), buffer.size());
|
||||
memset(&packet.name, '\n', MAX_NAME_SIZE + 1);
|
||||
|
@ -239,17 +314,40 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
|||
}
|
||||
|
||||
// Compute relative translation
|
||||
float LEAN_DAMPING_FACTOR = 200.0f;
|
||||
float LEAN_DAMPING_FACTOR = 75.0f;
|
||||
translation -= _referenceTranslation;
|
||||
translation /= LEAN_DAMPING_FACTOR;
|
||||
translation.x *= -1;
|
||||
_headTranslation = (translation + _previousTranslation) / 2.0f;
|
||||
_previousTranslation = translation;
|
||||
if (isFiltering) {
|
||||
glm::vec3 linearVelocity = (translation - _lastHeadTranslation) / _averageMessageTime;
|
||||
const float LINEAR_VELOCITY_FILTER_STRENGTH = 0.3f;
|
||||
float velocityFilter = glm::clamp(1.0f - glm::length(linearVelocity) *
|
||||
LINEAR_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f);
|
||||
_filteredHeadTranslation = velocityFilter * _filteredHeadTranslation + (1.0f - velocityFilter) * translation;
|
||||
_lastHeadTranslation = translation;
|
||||
_headTranslation = _filteredHeadTranslation;
|
||||
} else {
|
||||
_headTranslation = translation;
|
||||
}
|
||||
|
||||
// Compute relative rotation
|
||||
rotation = glm::inverse(_referenceRotation) * rotation;
|
||||
_headRotation = (rotation + _previousRotation) / 2.0f;
|
||||
_previousRotation = rotation;
|
||||
if (isFiltering) {
|
||||
glm::quat r = rotation * glm::inverse(_headRotation);
|
||||
float theta = 2 * acos(r.w);
|
||||
glm::vec3 angularVelocity;
|
||||
if (theta > EPSILON) {
|
||||
float rMag = glm::length(glm::vec3(r.x, r.y, r.z));
|
||||
angularVelocity = theta / _averageMessageTime * glm::vec3(r.x, r.y, r.z) / rMag;
|
||||
} else {
|
||||
angularVelocity = glm::vec3(0, 0, 0);
|
||||
}
|
||||
const float ANGULAR_VELOCITY_FILTER_STRENGTH = 0.3f;
|
||||
_headRotation = safeMix(_headRotation, rotation, glm::clamp(glm::length(angularVelocity) *
|
||||
ANGULAR_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f));
|
||||
} else {
|
||||
_headRotation = rotation;
|
||||
}
|
||||
|
||||
// Translate DDE coefficients to Faceshift compatible coefficients
|
||||
for (int i = 0; i < NUM_EXPRESSIONS; i += 1) {
|
||||
|
@ -258,8 +356,23 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
|||
|
||||
// Use EyeBlink values to control both EyeBlink and EyeOpen
|
||||
static const float RELAXED_EYE_VALUE = 0.1f;
|
||||
float leftEye = (_coefficients[_leftBlinkIndex] + _previousCoefficients[_leftBlinkIndex]) / 2.0f;
|
||||
float rightEye = (_coefficients[_rightBlinkIndex] + _previousCoefficients[_rightBlinkIndex]) / 2.0f;
|
||||
float leftEye = _coefficients[_leftBlinkIndex];
|
||||
float rightEye = _coefficients[_rightBlinkIndex];
|
||||
if (isFiltering) {
|
||||
const float BLINK_VELOCITY_FILTER_STRENGTH = 0.3f;
|
||||
|
||||
float velocity = fabs(leftEye - _lastLeftEyeBlink) / _averageMessageTime;
|
||||
float velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f);
|
||||
_filteredLeftEyeBlink = velocityFilter * leftEye + (1.0f - velocityFilter) * _filteredLeftEyeBlink;
|
||||
_lastLeftEyeBlink = leftEye;
|
||||
leftEye = _filteredLeftEyeBlink;
|
||||
|
||||
velocity = fabs(rightEye - _lastRightEyeBlink) / _averageMessageTime;
|
||||
velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f);
|
||||
_filteredRightEyeBlink = velocityFilter * rightEye + (1.0f - velocityFilter) * _filteredRightEyeBlink;
|
||||
_lastRightEyeBlink = rightEye;
|
||||
rightEye = _filteredRightEyeBlink;
|
||||
}
|
||||
if (leftEye > RELAXED_EYE_VALUE) {
|
||||
_coefficients[_leftBlinkIndex] = leftEye - RELAXED_EYE_VALUE;
|
||||
_coefficients[_leftEyeOpenIndex] = 0.0f;
|
||||
|
@ -294,12 +407,22 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
|||
// Scale all coefficients
|
||||
for (int i = 0; i < NUM_EXPRESSIONS; i += 1) {
|
||||
_blendshapeCoefficients[i]
|
||||
= glm::clamp(DDE_COEFFICIENT_SCALES[i] * (_coefficients[i] + _previousCoefficients[i]) / 2.0f, 0.0f, 1.0f);
|
||||
_previousCoefficients[i] = _coefficients[i];
|
||||
= glm::clamp(DDE_COEFFICIENT_SCALES[i] * _coefficients[i], 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
// Calculate average frame time
|
||||
const float FRAME_AVERAGING_FACTOR = 0.99f;
|
||||
quint64 usecsNow = usecTimestampNow();
|
||||
if (_lastMessageReceived != 0) {
|
||||
_averageMessageTime = FRAME_AVERAGING_FACTOR * _averageMessageTime
|
||||
+ (1.0f - FRAME_AVERAGING_FACTOR) * (float)(usecsNow - _lastMessageReceived) / 1000000.0f;
|
||||
}
|
||||
_lastMessageReceived = usecsNow;
|
||||
|
||||
FaceTracker::countFrame();
|
||||
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "[Error] DDE Face Tracker Decode Error";
|
||||
qCWarning(interfaceapp) << "DDE Face Tracker: Decode error";
|
||||
}
|
||||
_lastReceiveTimestamp = usecTimestampNow();
|
||||
}
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
#ifndef hifi_DdeFaceTracker_h
|
||||
#define hifi_DdeFaceTracker_h
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_OSX)
|
||||
#define HAVE_DDE
|
||||
#endif
|
||||
|
||||
#include <QProcess>
|
||||
#include <QUdpSocket>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
@ -23,7 +28,7 @@ class DdeFaceTracker : public FaceTracker, public Dependency {
|
|||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
virtual void reset() { _reset = true; }
|
||||
virtual void reset();
|
||||
|
||||
virtual bool isActive() const;
|
||||
virtual bool isTracking() const { return isActive(); }
|
||||
|
@ -47,19 +52,24 @@ public slots:
|
|||
void setEnabled(bool enabled);
|
||||
|
||||
private slots:
|
||||
|
||||
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
//sockets
|
||||
void socketErrorOccurred(QAbstractSocket::SocketError socketError);
|
||||
void readPendingDatagrams();
|
||||
void socketStateChanged(QAbstractSocket::SocketState socketState);
|
||||
|
||||
|
||||
private:
|
||||
DdeFaceTracker();
|
||||
DdeFaceTracker(const QHostAddress& host, quint16 port);
|
||||
~DdeFaceTracker();
|
||||
DdeFaceTracker(const QHostAddress& host, quint16 serverPort, quint16 controlPort);
|
||||
virtual ~DdeFaceTracker();
|
||||
|
||||
QProcess* _ddeProcess;
|
||||
bool _ddeStopping;
|
||||
|
||||
QHostAddress _host;
|
||||
quint16 _port;
|
||||
quint16 _serverPort;
|
||||
quint16 _controlPort;
|
||||
|
||||
float getBlendshapeCoefficient(int index) const;
|
||||
void decodePacket(const QByteArray& buffer);
|
||||
|
@ -91,10 +101,14 @@ private:
|
|||
|
||||
QVector<float> _coefficients;
|
||||
|
||||
// Previous values for simple smoothing
|
||||
glm::vec3 _previousTranslation;
|
||||
glm::quat _previousRotation;
|
||||
QVector<float> _previousCoefficients;
|
||||
quint64 _lastMessageReceived;
|
||||
float _averageMessageTime;
|
||||
glm::vec3 _lastHeadTranslation;
|
||||
glm::vec3 _filteredHeadTranslation;
|
||||
float _lastLeftEyeBlink;
|
||||
float _filteredLeftEyeBlink;
|
||||
float _lastRightEyeBlink;
|
||||
float _filteredRightEyeBlink;
|
||||
};
|
||||
|
||||
#endif // hifi_DdeFaceTracker_h
|
|
@ -9,9 +9,21 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include "FaceTracker.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
const int FPS_TIMER_DELAY = 2000; // ms
|
||||
const int FPS_TIMER_DURATION = 2000; // ms
|
||||
|
||||
FaceTracker::FaceTracker() :
|
||||
_isCalculatingFPS(false),
|
||||
_frameCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
inline float FaceTracker::getBlendshapeCoefficient(int index) const {
|
||||
return isValidBlendshapeIndex(index) ? glm::mix(0.0f, _blendshapeCoefficients[index], getFadeCoefficient())
|
||||
|
@ -65,4 +77,27 @@ void FaceTracker::update(float deltaTime) {
|
|||
_relaxationStatus = glm::clamp(_relaxationStatus - deltaTime / RELAXATION_TIME, 0.0f, 1.0f);
|
||||
_fadeCoefficient = std::exp(-(1.0f - _relaxationStatus) * INVERSE_AT_EPSILON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FaceTracker::reset() {
|
||||
if (isActive() && !_isCalculatingFPS) {
|
||||
QTimer::singleShot(FPS_TIMER_DELAY, this, SLOT(startFPSTimer()));
|
||||
_isCalculatingFPS = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FaceTracker::startFPSTimer() {
|
||||
_frameCount = 0;
|
||||
QTimer::singleShot(FPS_TIMER_DURATION, this, SLOT(finishFPSTimer()));
|
||||
}
|
||||
|
||||
void FaceTracker::countFrame() {
|
||||
if (_isCalculatingFPS) {
|
||||
_frameCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void FaceTracker::finishFPSTimer() {
|
||||
qCDebug(interfaceapp) << "Face tracker FPS =" << (float)_frameCount / ((float)FPS_TIMER_DURATION / 1000.0f);
|
||||
_isCalculatingFPS = false;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
/// Base class for face trackers (Faceshift, Visage, DDE).
|
||||
/// Base class for face trackers (Faceshift, DDE).
|
||||
class FaceTracker : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -28,7 +28,7 @@ public:
|
|||
|
||||
virtual void init() {}
|
||||
virtual void update(float deltaTime);
|
||||
virtual void reset() {}
|
||||
virtual void reset();
|
||||
|
||||
float getFadeCoefficient() const;
|
||||
|
||||
|
@ -44,6 +44,9 @@ public:
|
|||
float getBlendshapeCoefficient(int index) const;
|
||||
|
||||
protected:
|
||||
FaceTracker();
|
||||
virtual ~FaceTracker() {};
|
||||
|
||||
glm::vec3 _headTranslation = glm::vec3(0.0f);
|
||||
glm::quat _headRotation = glm::quat();
|
||||
float _estimatedEyePitch = 0.0f;
|
||||
|
@ -52,6 +55,16 @@ protected:
|
|||
|
||||
float _relaxationStatus = 0.0f; // Between 0.0f and 1.0f
|
||||
float _fadeCoefficient = 0.0f; // Between 0.0f and 1.0f
|
||||
|
||||
void countFrame();
|
||||
|
||||
private slots:
|
||||
void startFPSTimer();
|
||||
void finishFPSTimer();
|
||||
|
||||
private:
|
||||
bool _isCalculatingFPS;
|
||||
int _frameCount;
|
||||
};
|
||||
|
||||
#endif // hifi_FaceTracker_h
|
||||
|
|
|
@ -39,6 +39,7 @@ Faceshift::Faceshift() :
|
|||
connect(&_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(noteError(QAbstractSocket::SocketError)));
|
||||
connect(&_tcpSocket, SIGNAL(readyRead()), SLOT(readFromSocket()));
|
||||
connect(&_tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SIGNAL(connectionStateChanged()));
|
||||
connect(&_tcpSocket, SIGNAL(disconnected()), SLOT(noteDisconnected()));
|
||||
|
||||
connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
|
||||
|
||||
|
@ -78,6 +79,10 @@ void Faceshift::update(float deltaTime) {
|
|||
|
||||
void Faceshift::reset() {
|
||||
if (_tcpSocket.state() == QAbstractSocket::ConnectedState) {
|
||||
qCDebug(interfaceapp, "Faceshift: Reset");
|
||||
|
||||
FaceTracker::reset();
|
||||
|
||||
string message;
|
||||
fsBinaryStream::encode_message(message, fsMsgCalibrateNeutral());
|
||||
send(message);
|
||||
|
@ -127,6 +132,7 @@ void Faceshift::setTCPEnabled(bool enabled) {
|
|||
if ((_tcpEnabled = enabled)) {
|
||||
connectSocket();
|
||||
} else {
|
||||
qCDebug(interfaceapp, "Faceshift: Disconnecting...");
|
||||
_tcpSocket.disconnectFromHost();
|
||||
}
|
||||
#endif
|
||||
|
@ -145,7 +151,7 @@ void Faceshift::connectSocket() {
|
|||
|
||||
void Faceshift::noteConnected() {
|
||||
#ifdef HAVE_FACESHIFT
|
||||
qCDebug(interfaceapp, "Faceshift: Connected.");
|
||||
qCDebug(interfaceapp, "Faceshift: Connected");
|
||||
// request the list of blendshape names
|
||||
string message;
|
||||
fsBinaryStream::encode_message(message, fsMsgSendBlendshapeNames());
|
||||
|
@ -153,10 +159,16 @@ void Faceshift::noteConnected() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void Faceshift::noteDisconnected() {
|
||||
#ifdef HAVE_FACESHIFT
|
||||
qCDebug(interfaceapp, "Faceshift: Disconnected");
|
||||
#endif
|
||||
}
|
||||
|
||||
void Faceshift::noteError(QAbstractSocket::SocketError error) {
|
||||
if (!_tcpRetryCount) {
|
||||
// Only spam log with fail to connect the first time, so that we can keep waiting for server
|
||||
qCDebug(interfaceapp) << "Faceshift: " << _tcpSocket.errorString();
|
||||
qCWarning(interfaceapp) << "Faceshift: " << _tcpSocket.errorString();
|
||||
}
|
||||
// retry connection after a 2 second delay
|
||||
if (_tcpEnabled) {
|
||||
|
@ -283,6 +295,8 @@ void Faceshift::receive(const QByteArray& buffer) {
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
FaceTracker::countFrame();
|
||||
}
|
||||
|
||||
void Faceshift::setEyeDeflection(float faceshiftEyeDeflection) {
|
||||
|
@ -292,3 +306,4 @@ void Faceshift::setEyeDeflection(float faceshiftEyeDeflection) {
|
|||
void Faceshift::setHostname(const QString& hostname) {
|
||||
_hostname.set(hostname);
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,8 @@ private slots:
|
|||
void noteError(QAbstractSocket::SocketError error);
|
||||
void readPendingDatagrams();
|
||||
void readFromSocket();
|
||||
|
||||
void noteDisconnected();
|
||||
|
||||
private:
|
||||
Faceshift();
|
||||
virtual ~Faceshift() {}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include <QDesktopWidget>
|
||||
#include <QGuiApplication>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QScreen>
|
||||
#include <QOpenGLTimerQuery>
|
||||
|
||||
|
@ -35,6 +34,8 @@
|
|||
#include "InterfaceLogging.h"
|
||||
#include "Application.h"
|
||||
|
||||
#include <gpu/GLBackend.h>
|
||||
|
||||
template <typename Function>
|
||||
void for_each_eye(Function function) {
|
||||
for (ovrEyeType eye = ovrEyeType::ovrEye_Left;
|
||||
|
@ -114,8 +115,11 @@ void OculusManager::initSdk() {
|
|||
}
|
||||
|
||||
void OculusManager::shutdownSdk() {
|
||||
ovrHmd_Destroy(_ovrHmd);
|
||||
ovr_Shutdown();
|
||||
if (_ovrHmd) {
|
||||
ovrHmd_Destroy(_ovrHmd);
|
||||
_ovrHmd = nullptr;
|
||||
ovr_Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void OculusManager::init() {
|
||||
|
@ -124,6 +128,12 @@ void OculusManager::init() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void OculusManager::deinit() {
|
||||
#ifdef OVR_DIRECT_MODE
|
||||
shutdownSdk();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OculusManager::connect() {
|
||||
#ifndef OVR_DIRECT_MODE
|
||||
initSdk();
|
||||
|
@ -515,13 +525,14 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
|
|||
|
||||
// We only need to render the overlays to a texture once, then we just render the texture on the hemisphere
|
||||
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
|
||||
applicationOverlay.renderOverlay(true);
|
||||
applicationOverlay.renderOverlay();
|
||||
|
||||
//Bind our framebuffer object. If we are rendering the glow effect, we let the glow effect shader take care of it
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) {
|
||||
DependencyManager::get<GlowEffect>()->prepare();
|
||||
} else {
|
||||
DependencyManager::get<TextureCache>()->getPrimaryFramebufferObject()->bind();
|
||||
auto primaryFBO = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFBO));
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
|
@ -612,15 +623,15 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
|
|||
|
||||
glPopMatrix();
|
||||
|
||||
QOpenGLFramebufferObject * finalFbo = nullptr;
|
||||
gpu::FramebufferPointer finalFbo;
|
||||
//Bind the output texture from the glow shader. If glow effect is disabled, we just grab the texture
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) {
|
||||
//Full texture viewport for glow effect
|
||||
glViewport(0, 0, _renderTargetSize.w, _renderTargetSize.h);
|
||||
finalFbo = DependencyManager::get<GlowEffect>()->render(true);
|
||||
} else {
|
||||
finalFbo = DependencyManager::get<TextureCache>()->getPrimaryFramebufferObject();
|
||||
finalFbo->release();
|
||||
finalFbo = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
|
@ -644,7 +655,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
|
|||
//Left over from when OR was not connected.
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, finalFbo->texture());
|
||||
glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0)));
|
||||
|
||||
//Renders the distorted mesh onto the screen
|
||||
renderDistortionMesh(eyeRenderPose);
|
||||
|
@ -860,4 +871,4 @@ int OculusManager::getHMDScreen() {
|
|||
return hmdScreenIndex;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -51,6 +51,7 @@ class Text3DOverlay;
|
|||
class OculusManager {
|
||||
public:
|
||||
static void init();
|
||||
static void deinit();
|
||||
static void connect();
|
||||
static void disconnect();
|
||||
static bool isConnected();
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <QOpenGLFramebufferObject>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <GlowEffect.h>
|
||||
|
|
|
@ -1,191 +0,0 @@
|
|||
//
|
||||
// Visage.cpp
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Andrzej Kapolka on 2/11/14.
|
||||
// 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
|
||||
//
|
||||
|
||||
#include <QHash>
|
||||
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <FBXReader.h>
|
||||
#include <PathUtils.h>
|
||||
#include <PerfStat.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Faceshift.h"
|
||||
#include "Visage.h"
|
||||
|
||||
// this has to go after our normal includes, because its definition of HANDLE conflicts with Qt's
|
||||
#ifdef HAVE_VISAGE
|
||||
#include <VisageTracker2.h>
|
||||
#endif
|
||||
|
||||
namespace VisageSDK {
|
||||
#ifdef WIN32
|
||||
void __declspec(dllimport) initializeLicenseManager(char* licenseKeyFileName);
|
||||
#else
|
||||
void initializeLicenseManager(char* licenseKeyFileName);
|
||||
#endif
|
||||
}
|
||||
|
||||
using namespace VisageSDK;
|
||||
|
||||
const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.7f);
|
||||
|
||||
Visage::Visage() :
|
||||
_enabled(false),
|
||||
_headOrigin(DEFAULT_HEAD_ORIGIN) {
|
||||
|
||||
#ifdef HAVE_VISAGE
|
||||
#ifdef WIN32
|
||||
QByteArray licensePath = PathUtils::resourcesPath().toLatin1() + "visage";
|
||||
#else
|
||||
QByteArray licensePath = PathUtils::resourcesPath().toLatin1() + "visage/license.vlc";
|
||||
#endif
|
||||
initializeLicenseManager(licensePath.data());
|
||||
_tracker = new VisageTracker2(PathUtils::resourcesPath().toLatin1() + "visage/tracker.cfg");
|
||||
_data = new FaceData();
|
||||
#endif
|
||||
}
|
||||
|
||||
Visage::~Visage() {
|
||||
#ifdef HAVE_VISAGE
|
||||
_tracker->stop();
|
||||
// deleting the tracker crashes windows; disable for now
|
||||
//delete _tracker;
|
||||
delete _data;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_VISAGE
|
||||
static int leftEyeBlinkIndex = 0;
|
||||
static int rightEyeBlinkIndex = 1;
|
||||
|
||||
static QMultiHash<QByteArray, QPair<int, float> > createActionUnitNameMap() {
|
||||
QMultiHash<QByteArray, QPair<QByteArray, float> > blendshapeMap;
|
||||
blendshapeMap.insert("JawFwd", QPair<QByteArray, float>("au_jaw_z_push", 1.0f));
|
||||
blendshapeMap.insert("JawLeft", QPair<QByteArray, float>("au_jaw_x_push", 1.0f));
|
||||
blendshapeMap.insert("JawOpen", QPair<QByteArray, float>("au_jaw_drop", 1.0f));
|
||||
blendshapeMap.insert("LipsLowerDown", QPair<QByteArray, float>("au_lower_lip_drop", 1.0f));
|
||||
blendshapeMap.insert("LipsUpperOpen", QPair<QByteArray, float>("au_upper_lip_raiser", 1.0f));
|
||||
blendshapeMap.insert("LipsStretch_R", QPair<QByteArray, float>("au_lip_stretcher_left", 0.5f));
|
||||
blendshapeMap.insert("MouthSmile_L", QPair<QByteArray, float>("au_lip_corner_depressor", -1.0f));
|
||||
blendshapeMap.insert("MouthSmile_R", QPair<QByteArray, float>("au_lip_corner_depressor", -1.0f));
|
||||
blendshapeMap.insert("BrowsU_R", QPair<QByteArray, float>("au_left_outer_brow_raiser", 1.0f));
|
||||
blendshapeMap.insert("BrowsU_C", QPair<QByteArray, float>("au_left_inner_brow_raiser", 1.0f));
|
||||
blendshapeMap.insert("BrowsD_R", QPair<QByteArray, float>("au_left_brow_lowerer", 1.0f));
|
||||
blendshapeMap.insert("EyeBlink_L", QPair<QByteArray, float>("au_leye_closed", 1.0f));
|
||||
blendshapeMap.insert("EyeBlink_R", QPair<QByteArray, float>("au_reye_closed", 1.0f));
|
||||
blendshapeMap.insert("EyeOpen_L", QPair<QByteArray, float>("au_upper_lid_raiser", 1.0f));
|
||||
blendshapeMap.insert("EyeOpen_R", QPair<QByteArray, float>("au_upper_lid_raiser", 1.0f));
|
||||
blendshapeMap.insert("LipLowerOpen", QPair<QByteArray, float>("au_lower_lip_x_push", 1.0f));
|
||||
blendshapeMap.insert("LipsStretch_L", QPair<QByteArray, float>("au_lip_stretcher_right", 0.5f));
|
||||
blendshapeMap.insert("BrowsU_L", QPair<QByteArray, float>("au_right_outer_brow_raiser", 1.0f));
|
||||
blendshapeMap.insert("BrowsU_C", QPair<QByteArray, float>("au_right_inner_brow_raiser", 1.0f));
|
||||
blendshapeMap.insert("BrowsD_L", QPair<QByteArray, float>("au_right_brow_lowerer", 1.0f));
|
||||
|
||||
QMultiHash<QByteArray, QPair<int, float> > actionUnitNameMap;
|
||||
for (int i = 0;; i++) {
|
||||
QByteArray blendshape = FACESHIFT_BLENDSHAPES[i];
|
||||
if (blendshape.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
if (blendshape == "EyeBlink_L") {
|
||||
leftEyeBlinkIndex = i;
|
||||
|
||||
} else if (blendshape == "EyeBlink_R") {
|
||||
rightEyeBlinkIndex = i;
|
||||
}
|
||||
for (QMultiHash<QByteArray, QPair<QByteArray, float> >::const_iterator it = blendshapeMap.constFind(blendshape);
|
||||
it != blendshapeMap.constEnd() && it.key() == blendshape; it++) {
|
||||
actionUnitNameMap.insert(it.value().first, QPair<int, float>(i, it.value().second));
|
||||
}
|
||||
}
|
||||
|
||||
return actionUnitNameMap;
|
||||
}
|
||||
|
||||
static const QMultiHash<QByteArray, QPair<int, float> >& getActionUnitNameMap() {
|
||||
static QMultiHash<QByteArray, QPair<int, float> > actionUnitNameMap = createActionUnitNameMap();
|
||||
return actionUnitNameMap;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_VISAGE
|
||||
const float TRANSLATION_SCALE = 20.0f;
|
||||
void Visage::init() {
|
||||
connect(DependencyManager::get<Faceshift>().data(), SIGNAL(connectionStateChanged()), SLOT(updateEnabled()));
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
void Visage::update(float deltaTime) {
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
FaceTracker::update(deltaTime);
|
||||
|
||||
_headRotation = glm::quat(glm::vec3(-_data->faceRotation[0], -_data->faceRotation[1], _data->faceRotation[2]));
|
||||
_headTranslation = (glm::vec3(_data->faceTranslation[0], _data->faceTranslation[1], _data->faceTranslation[2]) -
|
||||
_headOrigin) * TRANSLATION_SCALE;
|
||||
_estimatedEyePitch = glm::degrees(-_data->gazeDirection[1]);
|
||||
_estimatedEyeYaw = glm::degrees(-_data->gazeDirection[0]);
|
||||
|
||||
if (_actionUnitIndexMap.isEmpty()) {
|
||||
int maxIndex = -1;
|
||||
for (int i = 0; i < _data->actionUnitCount; i++) {
|
||||
QByteArray name = _data->actionUnitsNames[i];
|
||||
for (QMultiHash<QByteArray, QPair<int, float> >::const_iterator it = getActionUnitNameMap().constFind(name);
|
||||
it != getActionUnitNameMap().constEnd() && it.key() == name; it++) {
|
||||
_actionUnitIndexMap.insert(i, it.value());
|
||||
maxIndex = qMax(maxIndex, it.value().first);
|
||||
}
|
||||
}
|
||||
_blendshapeCoefficients.resize(maxIndex + 1);
|
||||
}
|
||||
|
||||
qFill(_blendshapeCoefficients.begin(), _blendshapeCoefficients.end(), 0.0f);
|
||||
for (int i = 0; i < _data->actionUnitCount; i++) {
|
||||
if (!_data->actionUnitsUsed[i]) {
|
||||
continue;
|
||||
}
|
||||
for (QMultiHash<int, QPair<int, float> >::const_iterator it = _actionUnitIndexMap.constFind(i);
|
||||
it != _actionUnitIndexMap.constEnd() && it.key() == i; it++) {
|
||||
_blendshapeCoefficients[it.value().first] += _data->actionUnits[i] * it.value().second;
|
||||
}
|
||||
}
|
||||
_blendshapeCoefficients[leftEyeBlinkIndex] = 1.0f - _data->eyeClosure[1];
|
||||
_blendshapeCoefficients[rightEyeBlinkIndex] = 1.0f - _data->eyeClosure[0];
|
||||
}
|
||||
|
||||
void Visage::reset() {
|
||||
_headOrigin += _headTranslation / TRANSLATION_SCALE;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Visage::updateEnabled() {
|
||||
setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Visage) &&
|
||||
!(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) &&
|
||||
DependencyManager::get<Faceshift>()->isConnectedOrConnecting()) &&
|
||||
!Menu::getInstance()->isOptionChecked(MenuOption::DDEFaceRegression));
|
||||
}
|
||||
|
||||
void Visage::setEnabled(bool enabled) {
|
||||
#ifdef HAVE_VISAGE
|
||||
if (_enabled == enabled) {
|
||||
return;
|
||||
}
|
||||
if ((_enabled = enabled)) {
|
||||
_tracker->trackFromCam();
|
||||
} else {
|
||||
_tracker->stop();
|
||||
}
|
||||
#endif
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue