diff --git a/BUILD.md b/BUILD.md
index c868a8e9d9..c51e40cb58 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -1,7 +1,7 @@
###Dependencies
* [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 3.3.2
-* [Qt](http://www.qt.io/download-open-source) ~> 5.5.1
+* [Qt](http://www.qt.io/download-open-source) ~> 5.6.1
* [OpenSSL](https://www.openssl.org/community/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)
@@ -41,14 +41,14 @@ If you would like to use a specific install of a dependency instead of the versi
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.
+In order for CMake to find the Qt5 find modules, you will need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt installation.
-For example, a Qt5 5.5.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).
+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.5.1/clang_64/lib/cmake/
- export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1/lib/cmake
+ export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.1/clang_64/lib/cmake/
+ export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.1-1/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
####Generating build files
@@ -65,7 +65,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.5.1/lib/cmake
+ cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.1/lib/cmake
####Finding Dependencies
diff --git a/BUILD_OSX.md b/BUILD_OSX.md
index 1c9c5a9796..55d4276aa0 100644
--- a/BUILD_OSX.md
+++ b/BUILD_OSX.md
@@ -1,25 +1,31 @@
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file.
###Homebrew
-[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all High Fidelity dependencies very simple.
+[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of some High Fidelity dependencies very simple.
brew tap homebrew/versions
- brew install cmake openssl qt55
+ brew install cmake openssl
-We no longer require install of qt5 via our [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas). Versions of Qt that are 5.5.x provide a mechanism to disable the wireless scanning we previously had a custom patch for.
+###OpenSSL
-###OpenSSL and Qt
-
-Assuming you've installed OpenSSL or Qt 5 using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR and QT_CMAKE_PREFIX_PATH so CMake can find your installations.
+Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations.
For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR:
export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2h_1/
-
-For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows.
- export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt55/5.5.1/lib/cmake
+Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change.
-Note that these use the versions from homebrew formulae at the time of this writing, and the version in the path will likely change.
+###Qt
+You can use the online installer or the offline installer.
+
+* [Download the online installer](http://www.qt.io/download-open-source/#section-2)
+ * When it asks you to select components, select the following:
+ * Qt > Qt 5.6
+
+* [Download the offline installer](http://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-mac-x64-clang-5.6.1-1.dmg)
+
+Once Qt is installed, you need to manually configure the following:
+* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt5.6.1/5.6/clang_64/lib/cmake/` directory.
###Xcode
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.
diff --git a/BUILD_WIN.md b/BUILD_WIN.md
index 25e20952ca..b8adaad8d1 100644
--- a/BUILD_WIN.md
+++ b/BUILD_WIN.md
@@ -27,17 +27,17 @@ We expect nmake.exe to be located at the following path.
###Qt
You can use the online installer or the offline installer. If you use the offline installer, be sure to select the "OpenGL" version.
-* [Download the online installer](http://qt-project.org/downloads)
+* [Download the online installer](http://www.qt.io/download-open-source/#section-2)
* When it asks you to select components, ONLY select one of the following, 32- or 64-bit to match your build preference:
- * Qt > Qt 5.5.1 > **msvc2013 32-bit**
- * Qt > Qt 5.5.1 > **msvc2013 64-bit**
+ * Qt > Qt 5.6.1 > **msvc2013 32-bit**
+ * Qt > Qt 5.6.1 > **msvc2013 64-bit**
* Download the offline installer, 32- or 64-bit to match your build preference:
- * [32-bit](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013-5.5.1.exe)
- * [64-bit](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013_64-5.5.1.exe)
+ * [32-bit](http://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013-5.6.1-1.exe)
+ * [64-bit](http://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-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.5.1\msvc2013\lib\cmake` or `Qt\5.5.1\msvc2013_64\lib\cmake` directory.
+* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.6.1\msvc2013\lib\cmake` or `Qt\5.6.1\msvc2013_64\lib\cmake` directory.
* You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New
###External Libraries
diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt
index d0fd2c1176..54afabfd21 100644
--- a/assignment-client/CMakeLists.txt
+++ b/assignment-client/CMakeLists.txt
@@ -2,6 +2,11 @@ set(TARGET_NAME assignment-client)
setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets)
+# Fix up the rpath so macdeployqt works
+if (APPLE)
+ set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks")
+endif ()
+
# link in the shared libraries
link_hifi_libraries(
audio avatars octree gpu model fbx entities
diff --git a/cmake/macros/FixupInterface.cmake b/cmake/macros/FixupInterface.cmake
index 3e5ea7a3e2..93b5cc25fb 100644
--- a/cmake/macros/FixupInterface.cmake
+++ b/cmake/macros/FixupInterface.cmake
@@ -16,26 +16,6 @@ macro(fixup_interface)
string(REPLACE " " "\\ " ESCAPED_INSTALL_PATH ${INTERFACE_INSTALL_DIR})
set(_INTERFACE_INSTALL_PATH "${ESCAPED_INSTALL_PATH}/${ESCAPED_BUNDLE_NAME}.app")
- # install QtWebProcess from Qt to the application bundle
- # since it is missed by macdeployqt
- # https://bugreports.qt.io/browse/QTBUG-35211
- set(LIBEXEC_PATH "${_INTERFACE_INSTALL_PATH}/Contents/libexec")
- install(
- PROGRAMS "${QT_DIR}/libexec/QtWebProcess"
- DESTINATION ${LIBEXEC_PATH}
- COMPONENT ${CLIENT_COMPONENT}
- )
-
- set(QTWEBPROCESS_PATH "\${CMAKE_INSTALL_PREFIX}/${LIBEXEC_PATH}")
-
- # we also need a qt.conf in the directory of QtWebProcess
- install(CODE "
- file(WRITE ${QTWEBPROCESS_PATH}/qt.conf
- \"[Paths]\nPlugins = ../PlugIns\nImports = ../Resources/qml\nQml2Imports = ../Resources/qml\"
- )"
- COMPONENT ${CLIENT_COMPONENT}
- )
-
find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS "${QT_DIR}/bin" NO_DEFAULT_PATH)
if (NOT MACDEPLOYQT_COMMAND AND (PRODUCTION_BUILD OR PR_BUILD))
@@ -49,7 +29,6 @@ macro(fixup_interface)
execute_process(COMMAND ${MACDEPLOYQT_COMMAND}\
\${CMAKE_INSTALL_PREFIX}/${_INTERFACE_INSTALL_PATH}/\
-verbose=2 -qmldir=${CMAKE_SOURCE_DIR}/interface/resources/qml/\
- -executable=\${CMAKE_INSTALL_PREFIX}/${_INTERFACE_INSTALL_PATH}/Contents/libexec/QtWebProcess\
)"
COMPONENT ${CLIENT_COMPONENT}
)
diff --git a/cmake/macros/InstallBesideConsole.cmake b/cmake/macros/InstallBesideConsole.cmake
index 0eb6025f38..d5777fff12 100644
--- a/cmake/macros/InstallBesideConsole.cmake
+++ b/cmake/macros/InstallBesideConsole.cmake
@@ -59,7 +59,12 @@ macro(install_beside_console)
set(EXECUTABLE_NEEDING_FIXUP "\${CMAKE_INSTALL_PREFIX}/${COMPONENT_INSTALL_DIR}/${TARGET_NAME}")
string(REPLACE " " "\\ " ESCAPED_EXECUTABLE_NAME ${EXECUTABLE_NEEDING_FIXUP})
+ # configure Info.plist for COMPONENT_APP
install(CODE "
+ set(MACOSX_BUNDLE_EXECUTABLE_NAME domain-server)
+ set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.server-components)
+ set(MACOSX_BUNDLE_BUNDLE_NAME Sandbox\\ Components)
+ configure_file(${HF_CMAKE_DIR}/templates/MacOSXBundleSandboxComponentsInfo.plist.in ${ESCAPED_BUNDLE_NAME}/Contents/Info.plist)
execute_process(COMMAND ${MACDEPLOYQT_COMMAND} ${ESCAPED_BUNDLE_NAME} -verbose=2 -executable=${ESCAPED_EXECUTABLE_NAME})"
COMPONENT ${SERVER_COMPONENT}
)
diff --git a/cmake/templates/MacOSXBundleSandboxComponentsInfo.plist.in b/cmake/templates/MacOSXBundleSandboxComponentsInfo.plist.in
new file mode 100644
index 0000000000..a466dc7c4e
--- /dev/null
+++ b/cmake/templates/MacOSXBundleSandboxComponentsInfo.plist.in
@@ -0,0 +1,36 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${MACOSX_BUNDLE_EXECUTABLE_NAME}
+ CFBundleGetInfoString
+ ${MACOSX_BUNDLE_INFO_STRING}
+ CFBundleIconFile
+ ${MACOSX_BUNDLE_ICON_FILE}
+ CFBundleIdentifier
+ ${MACOSX_BUNDLE_GUI_IDENTIFIER}
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleLongVersionString
+ ${MACOSX_BUNDLE_LONG_VERSION_STRING}
+ CFBundleName
+ ${MACOSX_BUNDLE_BUNDLE_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ ${MACOSX_BUNDLE_SHORT_VERSION_STRING}
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ ${MACOSX_BUNDLE_BUNDLE_VERSION}
+ CSResourcesFileMapped
+
+ LSRequiresCarbon
+
+ NSHumanReadableCopyright
+ ${MACOSX_BUNDLE_COPYRIGHT}
+
+
diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt
index b7eb8c0138..746e599d4e 100644
--- a/domain-server/CMakeLists.txt
+++ b/domain-server/CMakeLists.txt
@@ -9,6 +9,11 @@ endif ()
# setup the project and link required Qt modules
setup_hifi_project(Network)
+# Fix up the rpath so macdeployqt works
+if (APPLE)
+ set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks")
+endif ()
+
# TODO: find a solution that will handle web file changes in resources on windows without a re-build.
# Currently the resources are only copied on post-build. If one is changed but the domain-server is not, they will
# not be re-copied. This is worked-around on OS X/UNIX by using a symlink.
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index b61fef8525..570db05871 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -1756,6 +1756,7 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
.arg(authorizationCode, oauthRedirectURL().toString(), _oauthClientID, _oauthClientSecret);
QNetworkRequest tokenRequest(tokenRequestUrl);
+ tokenRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
tokenRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
@@ -1949,6 +1950,7 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR
profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken));
QNetworkRequest profileRequest(profileURL);
+ profileRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
profileRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
return NetworkAccessManager::getInstance().get(profileRequest);
}
diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp
index 84c48ac5f9..3f229d2f87 100644
--- a/ice-server/src/IceServer.cpp
+++ b/ice-server/src/IceServer.cpp
@@ -213,6 +213,7 @@ void IceServer::requestDomainPublicKey(const QUuid& domainID) {
publicKeyURL.setPath(publicKeyPath);
QNetworkRequest publicKeyRequest { publicKeyURL };
+ publicKeyRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
publicKeyRequest.setAttribute(QNetworkRequest::User, domainID);
qDebug() << "Requesting public key for domain with ID" << domainID;
diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt
index 1d436695b1..3e8b55431d 100644
--- a/interface/CMakeLists.txt
+++ b/interface/CMakeLists.txt
@@ -42,12 +42,12 @@ endif ()
if (ANDROID)
set(PLATFORM_QT_COMPONENTS AndroidExtras)
else ()
- set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets WebKitWidgets)
+ set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets)
endif ()
find_package(
Qt5 COMPONENTS
- Gui Multimedia Network OpenGL Qml Quick Script ScriptTools Svg
+ Gui Multimedia Network OpenGL Qml Quick Script Svg
${PLATFORM_QT_COMPONENTS}
WebChannel WebSockets
)
@@ -123,7 +123,8 @@ if (APPLE)
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS} ${QM})
# make sure the output name for the .app bundle is correct
- set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME ${INTERFACE_BUNDLE_NAME})
+ # Fix up the rpath so macdeployqt works
+ set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks")
elseif (WIN32)
# configure an rc file for the chosen icon
set(CONFIGURE_ICON_PATH "${CMAKE_CURRENT_SOURCE_DIR}/icon/${INTERFACE_ICON_FILENAME}")
@@ -241,16 +242,10 @@ include_directories("${PROJECT_SOURCE_DIR}/src")
target_link_libraries(
${TARGET_NAME}
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
- Qt5::Qml Qt5::Quick Qt5::Script Qt5::ScriptTools Qt5::Svg
- Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets Qt5::WebKitWidgets
+ Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
+ Qt5::WebChannel Qt5::WebEngine
)
-# Issue causes build failure unless we add this directory.
-# See https://bugreports.qt.io/browse/QTBUG-43351
-if (WIN32)
- add_paths_to_fixup_libs(${Qt5_DIR}/../../../plugins/qtwebengine)
-endif()
-
if (UNIX)
target_link_libraries(${TARGET_NAME} pthread)
endif(UNIX)
diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml
index 050e10eead..62226859b6 100644
--- a/interface/resources/qml/Browser.qml
+++ b/interface/resources/qml/Browser.qml
@@ -223,12 +223,12 @@ ScrollingWindow {
var newWindow = component.createObject(desktop);
request.openIn(newWindow.webView)
}
- Component.onCompleted: {
- desktop.initWebviewProfileHandlers(webview.profile)
- }
-
- //profile: desktop.browserProfile
+ Component.onCompleted: {
+ desktop.initWebviewProfileHandlers(webview.profile)
+ }
+
+ profile: desktop.browserProfile
}
} // item
@@ -245,4 +245,4 @@ ScrollingWindow {
break;
}
}
-} // dialog
\ No newline at end of file
+} // dialog
diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml
index 7285db22d2..84ef31e87f 100644
--- a/interface/resources/qml/controls/WebView.qml
+++ b/interface/resources/qml/controls/WebView.qml
@@ -5,7 +5,7 @@ WebEngineView {
id: root
property var newUrl;
- profile.httpUserAgent: "Mozilla/5.0 Chrome (HighFidelityInterface)"
+ profile: desktop.browserProfile
Component.onCompleted: {
console.log("Connecting JS messaging to Hifi Logging")
@@ -13,7 +13,6 @@ WebEngineView {
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
});
-
}
// FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6
@@ -61,8 +60,4 @@ WebEngineView {
request.openIn(newWindow.webView);
}
}
-
- // This breaks the webchannel used for passing messages. Fixed in Qt 5.6
- // See https://bugreports.qt.io/browse/QTBUG-49521
- //profile: desktop.browserProfile
}
diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml
index 6a37886cb3..ff8be580db 100644
--- a/interface/resources/qml/dialogs/FileDialog.qml
+++ b/interface/resources/qml/dialogs/FileDialog.qml
@@ -52,7 +52,7 @@ ModalWindow {
// Set from OffscreenUi::getOpenFile()
property int options; // <-- FIXME unused
- property string iconText: text !== "" ? hifi.glyphs.scriptUpload : ""
+ property string iconText: root.title !== "" ? hifi.glyphs.scriptUpload : ""
property int iconSize: 40
property bool selectDirectory: false;
diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml
index 53829eed9e..5d8cbc97fe 100644
--- a/interface/resources/qml/hifi/Card.qml
+++ b/interface/resources/qml/hifi/Card.qml
@@ -89,6 +89,7 @@ Rectangle {
property int dropSamples: 9;
property int dropSpread: 0;
DropShadow {
+ visible: desktop.gradientsSupported;
source: place;
anchors.fill: place;
horizontalOffset: dropHorizontalOffset;
@@ -99,6 +100,7 @@ Rectangle {
spread: dropSpread;
}
DropShadow {
+ visible: desktop.gradientsSupported;
source: users;
anchors.fill: users;
horizontalOffset: dropHorizontalOffset;
diff --git a/interface/resources/qml/windows/DefaultFrameDecoration.qml b/interface/resources/qml/windows/DefaultFrameDecoration.qml
index 40e32aaa6b..1ddd83976e 100644
--- a/interface/resources/qml/windows/DefaultFrameDecoration.qml
+++ b/interface/resources/qml/windows/DefaultFrameDecoration.qml
@@ -109,7 +109,7 @@ Decoration {
verticalOffset: 2
samples: 2
color: hifi.colors.baseGrayShadow60
- visible: (window && window.focus)
+ visible: (desktop.gradientsSupported && window && window.focus)
cached: true
}
}
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index d846a5c6d1..8620b384ec 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -133,7 +133,6 @@
#include "scripting/LocationScriptingInterface.h"
#include "scripting/MenuScriptingInterface.h"
#include "scripting/SettingsScriptingInterface.h"
-#include "scripting/WebWindowClass.h"
#include "scripting/WindowScriptingInterface.h"
#include "scripting/ControllerScriptingInterface.h"
#include "scripting/ToolbarScriptingInterface.h"
@@ -176,8 +175,6 @@ static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16;
// For processing on QThreadPool, target 2 less than the ideal number of threads, leaving
// 2 logical cores available for time sensitive tasks.
static const int MIN_PROCESSING_THREAD_POOL_SIZE = 2;
-static const int PROCESSING_THREAD_POOL_SIZE = std::max(MIN_PROCESSING_THREAD_POOL_SIZE,
- QThread::idealThreadCount() - 2);
static const QString SNAPSHOT_EXTENSION = ".jpg";
static const QString SVO_EXTENSION = ".svo";
@@ -537,7 +534,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
PluginContainer* pluginContainer = dynamic_cast(this); // set the container for any plugins that care
PluginManager::getInstance()->setContainer(pluginContainer);
- QThreadPool::globalInstance()->setMaxThreadCount(PROCESSING_THREAD_POOL_SIZE);
+ QThreadPool::globalInstance()->setMaxThreadCount(MIN_PROCESSING_THREAD_POOL_SIZE);
thread()->setPriority(QThread::HighPriority);
thread()->setObjectName("Main Thread");
@@ -707,6 +704,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);
connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
+ connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateThreadPoolCount);
+
// Save avatar location immediately after a teleport.
connect(getMyAvatar(), &MyAvatar::positionGoneTo,
DependencyManager::get().data(), &AddressManager::storeCurrentAddress);
@@ -4858,7 +4857,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
LocationScriptingInterface::locationSetter);
- scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1);
scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor);
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
@@ -5034,6 +5032,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(url);
+ networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(networkRequest);
int requestNumber = ++_avatarAttachmentRequest;
@@ -5727,3 +5726,18 @@ void Application::sendHoverLeaveEntity(QUuid id, PointerEvent event) {
EntityItemID entityItemID(id);
emit getEntities()->hoverLeaveEntity(entityItemID, event);
}
+
+// FIXME? perhaps two, one for the main thread and one for the offscreen UI rendering thread?
+static const int UI_RESERVED_THREADS = 1;
+// Windows won't let you have all the cores
+static const int OS_RESERVED_THREADS = 1;
+
+void Application::updateThreadPoolCount() const {
+ auto reservedThreads = UI_RESERVED_THREADS + OS_RESERVED_THREADS + _displayPlugin->getRequiredThreadCount();
+ auto availableThreads = QThread::idealThreadCount() - reservedThreads;
+ auto threadPoolSize = std::max(MIN_PROCESSING_THREAD_POOL_SIZE, availableThreads);
+ qDebug() << "Ideal Thread Count " << QThread::idealThreadCount();
+ qDebug() << "Reserved threads " << reservedThreads;
+ qDebug() << "Setting thread pool size to " << threadPoolSize;
+ QThreadPool::globalInstance()->setMaxThreadCount(threadPoolSize);
+}
diff --git a/interface/src/Application.h b/interface/src/Application.h
index a0c67a9e73..8bfae51179 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -286,6 +286,7 @@ public slots:
bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr);
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
bool importEntities(const QString& url);
+ void updateThreadPoolCount() const;
static void setLowVelocityFilter(bool lowVelocityFilter);
Q_INVOKABLE void loadDialog();
diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp
index 1869980270..2bc4608e86 100644
--- a/interface/src/InterfaceActionFactory.cpp
+++ b/interface/src/InterfaceActionFactory.cpp
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include "InterfaceActionFactory.h"
@@ -29,6 +30,8 @@ EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& i
return std::make_shared(id, ownerEntity);
case ACTION_TYPE_HOLD:
return std::make_shared(id, ownerEntity);
+ case ACTION_TYPE_TRAVEL_ORIENTED:
+ return std::make_shared(id, ownerEntity);
}
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity action type");
diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 31fb2abe53..717a0eeac7 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -59,8 +59,6 @@ const float DISPLAYNAME_ALPHA = 1.0f;
const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;
const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f);
-const int SENSOR_TO_WORLD_MATRIX_INDEX = 65534;
-
namespace render {
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) {
return ItemKey::Builder::opaqueShape();
@@ -853,32 +851,54 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const {
}
glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
- if (index == SENSOR_TO_WORLD_MATRIX_INDEX) {
- glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
- bool success;
- Transform avatarTransform;
- Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
- glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
- return glmExtractRotation(invAvatarMat * sensorToWorldMatrix);
- } else {
- glm::quat rotation;
- _skeletonModel->getAbsoluteJointRotationInRigFrame(index, rotation);
- return Quaternions::Y_180 * rotation;
+ switch(index) {
+ case SENSOR_TO_WORLD_MATRIX_INDEX: {
+ glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
+ bool success;
+ Transform avatarTransform;
+ Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
+ glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
+ return glmExtractRotation(invAvatarMat * sensorToWorldMatrix);
+ }
+ case CONTROLLER_LEFTHAND_INDEX: {
+ Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix());
+ return controllerLeftHandTransform.getRotation();
+ }
+ case CONTROLLER_RIGHTHAND_INDEX: {
+ Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix());
+ return controllerRightHandTransform.getRotation();
+ }
+ default: {
+ glm::quat rotation;
+ _skeletonModel->getAbsoluteJointRotationInRigFrame(index, rotation);
+ return Quaternions::Y_180 * rotation;
+ }
}
}
glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
- if (index == SENSOR_TO_WORLD_MATRIX_INDEX) {
- glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
- bool success;
- Transform avatarTransform;
- Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
- glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
- return extractTranslation(invAvatarMat * sensorToWorldMatrix);
- } else {
- glm::vec3 translation;
- _skeletonModel->getAbsoluteJointTranslationInRigFrame(index, translation);
- return Quaternions::Y_180 * translation;
+ switch(index) {
+ case SENSOR_TO_WORLD_MATRIX_INDEX: {
+ glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
+ bool success;
+ Transform avatarTransform;
+ Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
+ glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
+ return extractTranslation(invAvatarMat * sensorToWorldMatrix);
+ }
+ case CONTROLLER_LEFTHAND_INDEX: {
+ Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix());
+ return controllerLeftHandTransform.getTranslation();
+ }
+ case CONTROLLER_RIGHTHAND_INDEX: {
+ Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix());
+ return controllerRightHandTransform.getTranslation();
+ }
+ default: {
+ glm::vec3 translation;
+ _skeletonModel->getAbsoluteJointTranslationInRigFrame(index, translation);
+ return Quaternions::Y_180 * translation;
+ }
}
}
@@ -889,6 +909,10 @@ int Avatar::getJointIndex(const QString& name) const {
Q_RETURN_ARG(int, result), Q_ARG(const QString&, name));
return result;
}
+ int result = getFauxJointIndex(name);
+ if (result != -1) {
+ return result;
+ }
return _skeletonModel->isActive() ? _skeletonModel->getFBXGeometry().getJointIndex(name) : -1;
}
diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp
index 5acee052f2..51171b9c6b 100644
--- a/interface/src/avatar/AvatarActionHold.cpp
+++ b/interface/src/avatar/AvatarActionHold.cpp
@@ -56,6 +56,10 @@ void AvatarActionHold::prepareForPhysicsSimulation() {
}
withWriteLock([&]{
+ glm::vec3 avatarRigidBodyPosition;
+ glm::quat avatarRigidBodyRotation;
+ getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
+
if (_ignoreIK) {
return;
}
@@ -70,9 +74,6 @@ void AvatarActionHold::prepareForPhysicsSimulation() {
palmRotation = holdingAvatar->getUncachedLeftPalmRotation();
}
- glm::vec3 avatarRigidBodyPosition;
- glm::quat avatarRigidBodyRotation;
- getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
// determine the difference in translation and rotation between the avatar's
// rigid body and the palm position. The avatar's rigid body will be moved by bullet
@@ -124,13 +125,20 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
if (pose.isValid()) {
linearVelocity = pose.getVelocity();
angularVelocity = pose.getAngularVelocity();
+
+ if (isRightHand) {
+ pose = avatarManager->getMyAvatar()->getRightHandControllerPoseInAvatarFrame();
+ } else {
+ pose = avatarManager->getMyAvatar()->getLeftHandControllerPoseInAvatarFrame();
+ }
}
if (_ignoreIK && pose.isValid()) {
- // We cannot ignore other avatars IK and this is not the point of this option
- // This is meant to make the grabbing behavior more reactive.
- palmPosition = pose.getTranslation();
- palmRotation = pose.getRotation();
+ Transform avatarTransform;
+ auto myAvatar = DependencyManager::get()->getMyAvatar();
+ avatarTransform = myAvatar->getTransform();
+ palmPosition = avatarTransform.transform(pose.getTranslation() / myAvatar->getTargetScale());
+ palmRotation = avatarTransform.getRotation() * pose.getRotation();
} else {
glm::vec3 avatarRigidBodyPosition;
glm::quat avatarRigidBodyRotation;
@@ -159,11 +167,17 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
}
} else { // regular avatar
if (isRightHand) {
- palmPosition = holdingAvatar->getRightPalmPosition();
- palmRotation = holdingAvatar->getRightPalmRotation();
+ Transform controllerRightTransform = Transform(holdingAvatar->getControllerRightHandMatrix());
+ Transform avatarTransform = holdingAvatar->getTransform();
+ palmRotation = avatarTransform.getRotation() * controllerRightTransform.getRotation();
+ palmPosition = avatarTransform.getTranslation() +
+ (avatarTransform.getRotation() * controllerRightTransform.getTranslation());
} else {
- palmPosition = holdingAvatar->getLeftPalmPosition();
- palmRotation = holdingAvatar->getLeftPalmRotation();
+ Transform controllerLeftTransform = Transform(holdingAvatar->getControllerLeftHandMatrix());
+ Transform avatarTransform = holdingAvatar->getTransform();
+ palmRotation = avatarTransform.getRotation() * controllerLeftTransform.getRotation();
+ palmPosition = avatarTransform.getTranslation() +
+ (avatarTransform.getRotation() * controllerLeftTransform.getTranslation());
}
}
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 5687b5025c..24dbc62318 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -532,11 +532,21 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
_hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation);
}
+void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache) {
+ assert(QThread::currentThread() == thread());
+ auto userInputMapper = DependencyManager::get();
+ controller::Pose controllerPose = userInputMapper->getPoseState(poseKey);
+ Transform transform;
+ transform.setTranslation(controllerPose.getTranslation());
+ transform.setRotation(controllerPose.getRotation());
+ glm::mat4 controllerMatrix = transform.getMatrix();
+ matrixCache.set(controllerMatrix);
+}
+
// best called at end of main loop, after physics.
// update sensor to world matrix from current body position and hmd sensor.
// This is so the correct camera can be used for rendering.
void MyAvatar::updateSensorToWorldMatrix() {
-
// update the sensor mat so that the body position will end up in the desired
// position when driven from the head.
glm::mat4 desiredMat = createMatFromQuatAndPos(getOrientation(), getPosition());
@@ -545,10 +555,14 @@ void MyAvatar::updateSensorToWorldMatrix() {
lateUpdatePalms();
if (_enableDebugDrawSensorToWorldMatrix) {
- DebugDraw::getInstance().addMarker("sensorToWorldMatrix", glmExtractRotation(_sensorToWorldMatrix), extractTranslation(_sensorToWorldMatrix), glm::vec4(1));
+ DebugDraw::getInstance().addMarker("sensorToWorldMatrix", glmExtractRotation(_sensorToWorldMatrix),
+ extractTranslation(_sensorToWorldMatrix), glm::vec4(1));
}
_sensorToWorldMatrixCache.set(_sensorToWorldMatrix);
+
+ updateJointFromController(controller::Action::LEFT_HAND, _controllerLeftHandMatrixCache);
+ updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache);
}
// Update avatar head rotation with sensor data
@@ -2215,3 +2229,31 @@ bool MyAvatar::didTeleport() {
bool MyAvatar::hasDriveInput() const {
return fabsf(_driveKeys[TRANSLATE_X]) > 0.0f || fabsf(_driveKeys[TRANSLATE_Y]) > 0.0f || fabsf(_driveKeys[TRANSLATE_Z]) > 0.0f;
}
+
+glm::quat MyAvatar::getAbsoluteJointRotationInObjectFrame(int index) const {
+ switch(index) {
+ case CONTROLLER_LEFTHAND_INDEX: {
+ return getLeftHandControllerPoseInAvatarFrame().getRotation();
+ }
+ case CONTROLLER_RIGHTHAND_INDEX: {
+ return getRightHandControllerPoseInAvatarFrame().getRotation();
+ }
+ default: {
+ return Avatar::getAbsoluteJointRotationInObjectFrame(index);
+ }
+ }
+}
+
+glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
+ switch(index) {
+ case CONTROLLER_LEFTHAND_INDEX: {
+ return getLeftHandControllerPoseInAvatarFrame().getTranslation();
+ }
+ case CONTROLLER_RIGHTHAND_INDEX: {
+ return getRightHandControllerPoseInAvatarFrame().getTranslation();
+ }
+ default: {
+ return Avatar::getAbsoluteJointTranslationInObjectFrame(index);
+ }
+ }
+}
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 1f212a1fec..c4ffc08cbc 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -19,6 +19,7 @@
#include
#include
+#include
#include "Avatar.h"
#include "AtRestDetector.h"
@@ -117,6 +118,9 @@ public:
// as it moves through the world.
void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix);
+ // read the location of a hand controller and save the transform
+ void updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache);
+
// best called at end of main loop, just before rendering.
// update sensor to world matrix from current body position and hmd sensor.
// This is so the correct camera can be used for rendering.
@@ -270,6 +274,9 @@ public:
Q_INVOKABLE void setCharacterControllerEnabled(bool enabled);
Q_INVOKABLE bool getCharacterControllerEnabled();
+ virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
+ virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
+
public slots:
void increaseSize();
void decreaseSize();
@@ -410,9 +417,8 @@ private:
bool _useSnapTurn { true };
bool _clearOverlayWhenMoving { true };
- // working copy of sensorToWorldMatrix.
- // See AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access
- glm::mat4 _sensorToWorldMatrix;
+ // working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access
+ glm::mat4 _sensorToWorldMatrix { glm::mat4() };
// cache of the current HMD sensor position and orientation
// in sensor space.
diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp
deleted file mode 100644
index 18beee4bbf..0000000000
--- a/interface/src/scripting/WebWindowClass.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-//
-// WebWindowClass.cpp
-// interface/src/scripting
-//
-// Created by Ryan Huffman on 11/06/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
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "Application.h"
-#include "ui/DataWebPage.h"
-#include "MainWindow.h"
-#include "WebWindowClass.h"
-#include "WindowScriptingInterface.h"
-
-ScriptEventBridge::ScriptEventBridge(QObject* parent) : QObject(parent) {
-}
-
-void ScriptEventBridge::emitWebEvent(const QString& data) {
- emit webEventReceived(data);
-}
-
-void ScriptEventBridge::emitScriptEvent(const QString& data) {
- emit scriptEventReceived(data);
-}
-
-WebWindowClass::WebWindowClass(const QString& title, const QString& url, int width, int height)
- : QObject(NULL), _eventBridge(new ScriptEventBridge(this)) {
- auto dialogWidget = new QDialog(qApp->getWindow(), Qt::Window);
- dialogWidget->setWindowTitle(title);
- dialogWidget->resize(width, height);
- dialogWidget->installEventFilter(this);
- connect(dialogWidget, &QDialog::finished, this, &WebWindowClass::hasClosed);
-
- auto layout = new QVBoxLayout(dialogWidget);
- layout->setContentsMargins(0, 0, 0, 0);
- dialogWidget->setLayout(layout);
-
- _webView = new QWebView(dialogWidget);
-
- layout->addWidget(_webView);
-
- addEventBridgeToWindowObject();
-
- _windowWidget = dialogWidget;
-
- auto style = QStyleFactory::create("fusion");
- if (style) {
- _webView->setStyle(style);
- }
-
- _webView->setPage(new DataWebPage());
- if (!url.startsWith("http") && !url.startsWith("file://")) {
- _webView->setUrl(QUrl::fromLocalFile(url));
- } else {
- _webView->setUrl(url);
- }
-
- connect(this, &WebWindowClass::destroyed, _windowWidget, &QWidget::deleteLater);
- connect(_webView->page()->mainFrame(), &QWebFrame::javaScriptWindowObjectCleared,
- this, &WebWindowClass::addEventBridgeToWindowObject);
-}
-
-WebWindowClass::~WebWindowClass() {
-}
-
-bool WebWindowClass::eventFilter(QObject* sender, QEvent* event) {
- if (sender == _windowWidget) {
- if (event->type() == QEvent::Move) {
- emit moved(getPosition());
- }
- if (event->type() == QEvent::Resize) {
- emit resized(getSize());
- }
- }
-
- return false;
-}
-
-void WebWindowClass::hasClosed() {
- emit closed();
-}
-
-void WebWindowClass::addEventBridgeToWindowObject() {
- _webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge);
-}
-
-void WebWindowClass::setVisible(bool visible) {
- if (visible) {
- QMetaObject::invokeMethod(_windowWidget, "showNormal", Qt::AutoConnection);
- QMetaObject::invokeMethod(_windowWidget, "raise", Qt::AutoConnection);
- }
- QMetaObject::invokeMethod(_windowWidget, "setVisible", Qt::AutoConnection, Q_ARG(bool, visible));
-}
-
-void WebWindowClass::setURL(const QString& url) {
- if (QThread::currentThread() != thread()) {
- QMetaObject::invokeMethod(this, "setURL", Qt::AutoConnection, Q_ARG(QString, url));
- return;
- }
- _webView->setUrl(url);
-}
-
-QSizeF WebWindowClass::getSize() const {
- QSizeF size = _windowWidget->size();
- return size;
-}
-
-void WebWindowClass::setSize(QSizeF size) {
- setSize(size.width(), size.height());
-}
-
-void WebWindowClass::setSize(int width, int height) {
- if (QThread::currentThread() != thread()) {
- QMetaObject::invokeMethod(this, "setSize", Qt::AutoConnection, Q_ARG(int, width), Q_ARG(int, height));
- return;
- }
- _windowWidget->resize(width, height);
-}
-
-glm::vec2 WebWindowClass::getPosition() const {
- QPoint position = _windowWidget->pos();
- return glm::vec2(position.x(), position.y());
-}
-
-void WebWindowClass::setPosition(glm::vec2 position) {
- setPosition(position.x, position.y);
-}
-
-void WebWindowClass::setPosition(int x, int y) {
- if (QThread::currentThread() != thread()) {
- QMetaObject::invokeMethod(this, "setPosition", Qt::AutoConnection, Q_ARG(int, x), Q_ARG(int, y));
- return;
- }
- _windowWidget->move(x, y);
-}
-
-void WebWindowClass::raise() {
- QMetaObject::invokeMethod(_windowWidget, "showNormal", Qt::AutoConnection);
- QMetaObject::invokeMethod(_windowWidget, "raise", Qt::AutoConnection);
-}
-
-QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
- WebWindowClass* retVal;
- QString file = context->argument(0).toString();
- if (context->argument(4).toBool()) {
- qWarning() << "ToolWindow views with WebWindow are no longer supported. Use OverlayWebWindow instead";
- return QScriptValue();
- } else {
- qWarning() << "WebWindow views are deprecated. Use OverlayWebWindow instead";
- }
- QMetaObject::invokeMethod(DependencyManager::get().data(), "doCreateWebWindow", Qt::BlockingQueuedConnection,
- Q_RETURN_ARG(WebWindowClass*, retVal),
- Q_ARG(const QString&, file),
- Q_ARG(QString, context->argument(1).toString()),
- Q_ARG(int, context->argument(2).toInteger()),
- Q_ARG(int, context->argument(3).toInteger()));
-
- connect(engine, &QScriptEngine::destroyed, retVal, &WebWindowClass::deleteLater);
-
- return engine->newQObject(retVal);
-}
-
-void WebWindowClass::setTitle(const QString& title) {
- if (QThread::currentThread() != thread()) {
- QMetaObject::invokeMethod(this, "setTitle", Qt::AutoConnection, Q_ARG(QString, title));
- return;
- }
- _windowWidget->setWindowTitle(title);
-}
diff --git a/interface/src/scripting/WebWindowClass.h b/interface/src/scripting/WebWindowClass.h
deleted file mode 100644
index d7a610dd39..0000000000
--- a/interface/src/scripting/WebWindowClass.h
+++ /dev/null
@@ -1,80 +0,0 @@
-//
-// WebWindowClass.h
-// interface/src/scripting
-//
-// Created by Ryan Huffman on 11/06/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
-//
-
-#ifndef hifi_WebWindowClass_h
-#define hifi_WebWindowClass_h
-
-#include
-#include
-#include
-
-class ScriptEventBridge : public QObject {
- Q_OBJECT
-public:
- ScriptEventBridge(QObject* parent = NULL);
-
-public slots:
- void emitWebEvent(const QString& data);
- void emitScriptEvent(const QString& data);
-
-signals:
- void webEventReceived(const QString& data);
- void scriptEventReceived(const QString& data);
-
-};
-
-class WebWindowClass : public QObject {
- Q_OBJECT
- Q_PROPERTY(QObject* eventBridge READ getEventBridge)
- Q_PROPERTY(QString url READ getURL)
- Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition);
- Q_PROPERTY(QSizeF size READ getSize WRITE setSize);
-
-public:
- WebWindowClass(const QString& title, const QString& url, int width, int height);
- ~WebWindowClass();
-
- static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
-
-public slots:
- void setVisible(bool visible);
- glm::vec2 getPosition() const;
- void setPosition(int x, int y);
- void setPosition(glm::vec2 position);
- QSizeF getSize() const;
- void setSize(QSizeF size);
- void setSize(int width, int height);
- QString getURL() const { return _webView->url().url(); }
- void setURL(const QString& url);
- void raise();
- ScriptEventBridge* getEventBridge() const { return _eventBridge; }
- void addEventBridgeToWindowObject();
- void setTitle(const QString& title);
-
-signals:
- void visibilityChanged(bool visible); // Tool window
- void moved(glm::vec2 position);
- void resized(QSizeF size);
- void closed();
-
-protected:
- virtual bool eventFilter(QObject* sender, QEvent* event) override;
-
-private slots:
- void hasClosed();
-
-private:
- QWidget* _windowWidget;
- QWebView* _webView;
- ScriptEventBridge* _eventBridge;
-};
-
-#endif
diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp
index 4eb8c67250..c528c26b99 100644
--- a/interface/src/scripting/WindowScriptingInterface.cpp
+++ b/interface/src/scripting/WindowScriptingInterface.cpp
@@ -21,7 +21,6 @@
#include "MainWindow.h"
#include "Menu.h"
#include "OffscreenUi.h"
-#include "WebWindowClass.h"
#include "WindowScriptingInterface.h"
@@ -61,10 +60,6 @@ WindowScriptingInterface::WindowScriptingInterface() {
});
}
-WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height) {
- return new WebWindowClass(title, url, width, height);
-}
-
QScriptValue WindowScriptingInterface::hasFocus() {
return qApp->hasFocus();
}
diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h
index 7a01be7fac..715d0657a3 100644
--- a/interface/src/scripting/WindowScriptingInterface.h
+++ b/interface/src/scripting/WindowScriptingInterface.h
@@ -16,9 +16,6 @@
#include
#include
-class WebWindowClass;
-
-
class CustomPromptResult {
public:
QVariant value;
@@ -65,9 +62,6 @@ signals:
void snapshotTaken(const QString& path, bool notify);
void snapshotShared(const QString& error);
-private slots:
- WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height);
-
private:
QString getPreviousBrowseLocation() const;
void setPreviousBrowseLocation(const QString& location);
diff --git a/interface/src/ui/DataWebPage.cpp b/interface/src/ui/DataWebPage.cpp
deleted file mode 100644
index 01feacc393..0000000000
--- a/interface/src/ui/DataWebPage.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-// DataWebPage.cpp
-// interface/src/ui
-//
-// Created by Stephen Birarda on 2014-09-22.
-// 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
-
-#include "Application.h"
-#include
-#include
-
-#include "DataWebPage.h"
-
-DataWebPage::DataWebPage(QObject* parent) :
- QWebPage(parent)
-{
- // use an OAuthNetworkAccessManager instead of regular QNetworkAccessManager so our requests are authed
- setNetworkAccessManager(OAuthNetworkAccessManager::getInstance());
-
- // give the page an empty stylesheet
- settings()->setUserStyleSheetUrl(QUrl());
-}
-
-void DataWebPage::javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID) {
- qDebug() << "JS console message at line" << lineNumber << "from" << sourceID << "-" << message;
-}
-
-bool DataWebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type) {
- // Handle hifi:// links and links to files with particular extensions
- QString urlString = request.url().toString();
- if (qApp->canAcceptURL(urlString)) {
- if (qApp->acceptURL(urlString)) {
- return false; // we handled it, so QWebPage doesn't need to handle it
- }
- }
-
- // Make hyperlinks with target="_blank" open in user's Web browser
- if (type == QWebPage::NavigationTypeLinkClicked && frame == nullptr) {
- qApp->openUrl(request.url());
- return false; // We handled it.
- }
-
- return true;
-}
-
-QString DataWebPage::userAgentForUrl(const QUrl& url) const {
- return HIGH_FIDELITY_USER_AGENT;
-}
diff --git a/interface/src/ui/DataWebPage.h b/interface/src/ui/DataWebPage.h
deleted file mode 100644
index f9aa5be8a8..0000000000
--- a/interface/src/ui/DataWebPage.h
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-// DataWebPage.h
-// interface/src/ui
-//
-// Created by Stephen Birarda on 2014-09-22.
-// 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
-//
-
-#ifndef hifi_DataWebPage_h
-#define hifi_DataWebPage_h
-
-#include
-
-class DataWebPage : public QWebPage {
-public:
- DataWebPage(QObject* parent = 0);
-protected:
- void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID) override;
- bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type) override;
- virtual QString userAgentForUrl(const QUrl& url) const override;
-};
-
-#endif // hifi_DataWebPage_h
diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp
index 91de4e36ac..430cc805ed 100644
--- a/interface/src/ui/ModelsBrowser.cpp
+++ b/interface/src/ui/ModelsBrowser.cpp
@@ -228,6 +228,7 @@ void ModelHandler::update() {
QUrl url(_model.item(i,0)->data(Qt::UserRole).toString());
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.head(request);
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
@@ -280,6 +281,7 @@ void ModelHandler::queryNewFiles(QString marker) {
url.setQuery(query);
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(request);
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp
index f76a5dff78..ada6b11355 100644
--- a/interface/src/ui/ScriptEditorWidget.cpp
+++ b/interface/src/ui/ScriptEditorWidget.cpp
@@ -161,6 +161,7 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) {
} else {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(url);
+ networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(networkRequest);
qDebug() << "Downloading included script at" << scriptPath;
diff --git a/libraries/auto-updater/src/AutoUpdater.cpp b/libraries/auto-updater/src/AutoUpdater.cpp
index ea4e43ff41..43563b1d71 100644
--- a/libraries/auto-updater/src/AutoUpdater.cpp
+++ b/libraries/auto-updater/src/AutoUpdater.cpp
@@ -33,6 +33,7 @@ void AutoUpdater::checkForUpdate() {
void AutoUpdater::getLatestVersionData() {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest latestVersionRequest(BUILDS_XML_URL);
+ latestVersionRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(latestVersionRequest);
connect(reply, &QNetworkReply::finished, this, &AutoUpdater::parseLatestVersionData);
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index 9088ee0577..413180738a 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -374,6 +374,16 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
}
}
+ // faux joints
+ Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix());
+ destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerLeftHandTransform.getRotation());
+ destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerLeftHandTransform.getTranslation(),
+ TRANSLATION_COMPRESSION_RADIX);
+ Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix());
+ destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerRightHandTransform.getRotation());
+ destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(),
+ TRANSLATION_COMPRESSION_RADIX);
+
#ifdef WANT_DEBUG
if (sendAll) {
qDebug() << "AvatarData::toByteArray" << cullSmallChanges << sendAll
@@ -429,6 +439,20 @@ bool AvatarData::shouldLogError(const quint64& now) {
return false;
}
+
+const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSafeValueCache& matrixCache) {
+ glm::quat orientation;
+ glm::vec3 position;
+ Transform transform;
+ sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, orientation);
+ sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, position, TRANSLATION_COMPRESSION_RADIX);
+ transform.setTranslation(position);
+ transform.setRotation(orientation);
+ matrixCache.set(transform.getMatrix());
+ return sourceBuffer;
+}
+
+
#define PACKET_READ_CHECK(ITEM_NAME, SIZE_TO_READ) \
if ((endPosition - sourceBuffer) < (int)SIZE_TO_READ) { \
if (shouldLogError(now)) { \
@@ -655,6 +679,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
}
#endif
+ // faux joints
+ sourceBuffer = unpackFauxJoint(sourceBuffer, _controllerLeftHandMatrixCache);
+ sourceBuffer = unpackFauxJoint(sourceBuffer, _controllerRightHandMatrixCache);
+
int numBytesRead = sourceBuffer - startPosition;
_averageBytesReceived.updateAverage(numBytesRead);
return numBytesRead;
@@ -915,7 +943,24 @@ void AvatarData::clearJointsData() {
}
}
+int AvatarData::getFauxJointIndex(const QString& name) const {
+ if (name == "_SENSOR_TO_WORLD_MATRIX") {
+ return SENSOR_TO_WORLD_MATRIX_INDEX;
+ }
+ if (name == "_CONTROLLER_LEFTHAND") {
+ return CONTROLLER_LEFTHAND_INDEX;
+ }
+ if (name == "_CONTROLLER_RIGHTHAND") {
+ return CONTROLLER_RIGHTHAND_INDEX;
+ }
+ return -1;
+}
+
int AvatarData::getJointIndex(const QString& name) const {
+ int result = getFauxJointIndex(name);
+ if (result != -1) {
+ return result;
+ }
QReadLocker readLock(&_jointDataLock);
return _jointIndices.value(name) - 1;
}
@@ -1161,6 +1206,7 @@ void AvatarData::updateJointMappings() {
if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL);
+ networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
connect(networkReply, &QNetworkReply::finished, this, &AvatarData::setJointMappingsFromNetworkReply);
@@ -1743,6 +1789,17 @@ glm::mat4 AvatarData::getSensorToWorldMatrix() const {
return _sensorToWorldMatrixCache.get();
}
+// thread-safe
+glm::mat4 AvatarData::getControllerLeftHandMatrix() const {
+ return _controllerLeftHandMatrixCache.get();
+}
+
+// thread-safe
+glm::mat4 AvatarData::getControllerRightHandMatrix() const {
+ return _controllerRightHandMatrixCache.get();
+}
+
+
QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& value) {
QScriptValue obj = engine->newObject();
obj.setProperty("intersects", value.intersects);
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index 572657e921..715c24b8ee 100644
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -173,6 +173,8 @@ class AvatarData : public QObject, public SpatiallyNestable {
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID)
Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix)
+ Q_PROPERTY(glm::mat4 controllerLeftHandMatrix READ getControllerLeftHandMatrix)
+ Q_PROPERTY(glm::mat4 controllerRightHandMatrix READ getControllerRightHandMatrix)
public:
@@ -356,6 +358,8 @@ public:
// thread safe
Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const;
+ Q_INVOKABLE glm::mat4 getControllerLeftHandMatrix() const;
+ Q_INVOKABLE glm::mat4 getControllerRightHandMatrix() const;
public slots:
void sendAvatarDataPacket();
@@ -369,6 +373,8 @@ public slots:
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; }
+ float getTargetScale() { return _targetScale; }
+
protected:
glm::vec3 _handPosition;
@@ -433,6 +439,10 @@ protected:
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
ThreadSafeValueCache _sensorToWorldMatrixCache { glm::mat4() };
+ ThreadSafeValueCache _controllerLeftHandMatrixCache { glm::mat4() };
+ ThreadSafeValueCache _controllerRightHandMatrixCache { glm::mat4() };
+
+ int getFauxJointIndex(const QString& name) const;
private:
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);
@@ -519,5 +529,10 @@ Q_DECLARE_METATYPE(RayToAvatarIntersectionResult)
QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& results);
void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, RayToAvatarIntersectionResult& results);
+// faux joint indexes (-1 means invalid)
+const int SENSOR_TO_WORLD_MATRIX_INDEX = 65534; // -2
+const int CONTROLLER_RIGHTHAND_INDEX = 65533; // -3
+const int CONTROLLER_LEFTHAND_INDEX = 65532; // -4
+
#endif // hifi_AvatarData_h
diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp
index 7490a44c11..ff44d5d13d 100755
--- a/libraries/controllers/src/controllers/UserInputMapper.cpp
+++ b/libraries/controllers/src/controllers/UserInputMapper.cpp
@@ -325,7 +325,6 @@ QString UserInputMapper::getActionName(Action action) const {
return QString();
}
-
QVector UserInputMapper::getActionNames() const {
Locker locker(_lock);
QVector result;
@@ -335,6 +334,12 @@ QVector UserInputMapper::getActionNames() const {
return result;
}
+Pose UserInputMapper::getPoseState(Action action) const {
+ assert(QThread::currentThread() == thread());
+ return _poseStates[toInt(action)];
+}
+
+
bool UserInputMapper::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
Locker locker(_lock);
bool toReturn = false;
diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h
index 874e5054ea..baa05f2f9f 100644
--- a/libraries/controllers/src/controllers/UserInputMapper.h
+++ b/libraries/controllers/src/controllers/UserInputMapper.h
@@ -81,7 +81,7 @@ namespace controller {
QVector getAllActions() const;
QString getActionName(Action action) const;
float getActionState(Action action) const { return _actionStates[toInt(action)]; }
- Pose getPoseState(Action action) const { return _poseStates[toInt(action)]; }
+ Pose getPoseState(Action action) const;
int findAction(const QString& actionName) const;
QVector getActionNames() const;
Input inputFromAction(Action action) const { return getActionInputs()[toInt(action)].first; }
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
index afd8f7d45b..ef15861843 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h
@@ -69,6 +69,8 @@ public:
virtual bool wantVsync() const { return true; }
void setVsyncEnabled(bool vsyncEnabled) { _vsyncEnabled = vsyncEnabled; }
bool isVsyncEnabled() const { return _vsyncEnabled; }
+ // Three threads, one for rendering, one for texture transfers, one reserved for the GL driver
+ int getRequiredThreadCount() const override { return 3; }
protected:
friend class PresentThread;
diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
index 6904700be5..01f607b0ed 100644
--- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
@@ -527,9 +527,11 @@ void HmdDisplayPlugin::compositeExtra() {
if (_presentHandPoses[index] == IDENTITY_MATRIX) {
return;
}
- const auto& points = _presentHandLaserPoints[index];
- const auto& lasers = _presentHandLasers[index];
- geometryCache->renderGlowLine(batch, points.first, points.second, lasers.color);
+ const auto& laser = _presentHandLasers[index];
+ if (laser.valid()) {
+ const auto& points = _presentHandLaserPoints[index];
+ geometryCache->renderGlowLine(batch, points.first, points.second, laser.color);
+ }
});
});
}
diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp
index ce9a93a6ac..2ce4ce5555 100644
--- a/libraries/entities/src/EntityActionInterface.cpp
+++ b/libraries/entities/src/EntityActionInterface.cpp
@@ -100,6 +100,9 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS
if (normalizedActionTypeString == "hold") {
return ACTION_TYPE_HOLD;
}
+ if (normalizedActionTypeString == "traveloriented") {
+ return ACTION_TYPE_TRAVEL_ORIENTED;
+ }
qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString;
return ACTION_TYPE_NONE;
@@ -115,6 +118,8 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) {
return "spring";
case ACTION_TYPE_HOLD:
return "hold";
+ case ACTION_TYPE_TRAVEL_ORIENTED:
+ return "travel-oriented";
}
assert(false);
return "none";
diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h
index 9a881cf94c..d9a901f1f6 100644
--- a/libraries/entities/src/EntityActionInterface.h
+++ b/libraries/entities/src/EntityActionInterface.h
@@ -28,7 +28,8 @@ enum EntityActionType {
ACTION_TYPE_NONE = 0,
ACTION_TYPE_OFFSET = 1000,
ACTION_TYPE_SPRING = 2000,
- ACTION_TYPE_HOLD = 3000
+ ACTION_TYPE_HOLD = 3000,
+ ACTION_TYPE_TRAVEL_ORIENTED = 4000
};
diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp
index 6f5d0d7ec5..1f7999bdaa 100644
--- a/libraries/fbx/src/FSTReader.cpp
+++ b/libraries/fbx/src/FSTReader.cpp
@@ -191,6 +191,7 @@ FSTReader::ModelType FSTReader::predictModelType(const QVariantHash& mapping) {
QVariantHash FSTReader::downloadMapping(const QString& url) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(url);
+ networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(networkRequest);
qDebug() << "Downloading avatar file at " << url;
diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp
index 38121555ed..02b0afdf26 100644
--- a/libraries/fbx/src/OBJReader.cpp
+++ b/libraries/fbx/src/OBJReader.cpp
@@ -282,6 +282,7 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) {
});
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest netRequest(url);
+ netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
QNetworkReply* netReply = isTest ? networkAccessManager.head(netRequest) : networkAccessManager.get(netRequest);
if (!qApp || aboutToQuit) {
netReply->deleteLater();
diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index 2f4cbad26c..1c3850f8cb 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -218,7 +218,7 @@ void AccountManager::sendRequest(const QString& path,
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
-
+ networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
networkRequest.setRawHeader(METAVERSE_SESSION_ID_HEADER,
@@ -484,6 +484,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request;
+ request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
request.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
QUrl grantURL = _authURL;
@@ -578,6 +579,7 @@ void AccountManager::requestProfile() {
profileURL.setPath("/api/v1/user/profile");
QNetworkRequest profileRequest(profileURL);
+ profileRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
profileRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
profileRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, _accountInfo.getAccessToken().authorizationHeaderValue());
diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp
index c9c9d73394..b4145c73f5 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -836,6 +836,7 @@ void AddressManager::ifLocalSandboxRunningElse(std::function localSandbo
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest sandboxStatus(SANDBOX_STATUS_URL);
+ sandboxStatus.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(sandboxStatus);
diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp
index 11ab436933..392654a419 100644
--- a/libraries/networking/src/HTTPResourceRequest.cpp
+++ b/libraries/networking/src/HTTPResourceRequest.cpp
@@ -49,6 +49,7 @@ void HTTPResourceRequest::cleanupTimer() {
void HTTPResourceRequest::doSend() {
QNetworkRequest networkRequest(_url);
+ networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
if (_cacheEnabled) {
diff --git a/libraries/networking/src/OAuthNetworkAccessManager.cpp b/libraries/networking/src/OAuthNetworkAccessManager.cpp
index 92e7a2ff4f..15d5acbc67 100644
--- a/libraries/networking/src/OAuthNetworkAccessManager.cpp
+++ b/libraries/networking/src/OAuthNetworkAccessManager.cpp
@@ -37,6 +37,7 @@ QNetworkReply* OAuthNetworkAccessManager::createRequest(QNetworkAccessManager::O
if (accountManager->hasValidAccessToken()
&& req.url().host() == NetworkingConstants::METAVERSE_SERVER_URL.host()) {
QNetworkRequest authenticatedRequest(req);
+ authenticatedRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
authenticatedRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
authenticatedRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
accountManager->getAccountInfo().getAccessToken().authorizationHeaderValue());
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index 0d25d4f1be..c0272a644d 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -47,12 +47,12 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityAdd:
case PacketType::EntityEdit:
case PacketType::EntityData:
- return VERSION_WEB_ENTITIES_SUPPORT_DPI;
+ return VERSION_ENTITIES_ARROW_ACTION;
case PacketType::AvatarIdentity:
case PacketType::AvatarData:
case PacketType::BulkAvatarData:
case PacketType::KillAvatar:
- return static_cast(AvatarMixerPacketVersion::SensorToWorldMat);
+ return static_cast(AvatarMixerPacketVersion::HandControllerJoints);
case PacketType::ICEServerHeartbeat:
return 18; // ICE Server Heartbeat signing
case PacketType::AssetGetInfo:
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index 3ecdb75a18..8d08585c2b 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -187,13 +187,15 @@ const PacketVersion VERSION_ENTITIES_PROPERLY_ENCODE_SHAPE_EDITS = 60;
const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH = 61;
const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS = 62;
const PacketVersion VERSION_WEB_ENTITIES_SUPPORT_DPI = 63;
+const PacketVersion VERSION_ENTITIES_ARROW_ACTION = 64;
enum class AvatarMixerPacketVersion : PacketVersion {
TranslationSupport = 17,
SoftAttachmentSupport,
AvatarEntities,
AbsoluteSixByteRotations,
- SensorToWorldMat
+ SensorToWorldMat,
+ HandControllerJoints
};
enum class DomainConnectRequestVersion : PacketVersion {
diff --git a/libraries/physics/src/ObjectActionTravelOriented.cpp b/libraries/physics/src/ObjectActionTravelOriented.cpp
new file mode 100644
index 0000000000..18d09d21d9
--- /dev/null
+++ b/libraries/physics/src/ObjectActionTravelOriented.cpp
@@ -0,0 +1,206 @@
+//
+// ObjectActionTravelOriented.cpp
+// libraries/physics/src
+//
+// Created by Seth Alves 2015-6-5
+// 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
+//
+
+#include
+
+#include "QVariantGLM.h"
+#include "ObjectActionTravelOriented.h"
+
+const uint16_t ObjectActionTravelOriented::actionVersion = 1;
+
+
+ObjectActionTravelOriented::ObjectActionTravelOriented(const QUuid& id, EntityItemPointer ownerEntity) :
+ ObjectAction(ACTION_TYPE_TRAVEL_ORIENTED, id, ownerEntity) {
+ #if WANT_DEBUG
+ qDebug() << "ObjectActionTravelOriented::ObjectActionTravelOriented";
+ #endif
+}
+
+ObjectActionTravelOriented::~ObjectActionTravelOriented() {
+ #if WANT_DEBUG
+ qDebug() << "ObjectActionTravelOriented::~ObjectActionTravelOriented";
+ #endif
+}
+
+void ObjectActionTravelOriented::updateActionWorker(btScalar deltaTimeStep) {
+ withReadLock([&] {
+ auto ownerEntity = _ownerEntity.lock();
+ if (!ownerEntity) {
+ return;
+ }
+ void* physicsInfo = ownerEntity->getPhysicsInfo();
+ if (!physicsInfo) {
+ return;
+ }
+ ObjectMotionState* motionState = static_cast(physicsInfo);
+ btRigidBody* rigidBody = motionState->getRigidBody();
+ if (!rigidBody) {
+ qDebug() << "ObjectActionTravelOriented::updateActionWorker no rigidBody";
+ return;
+ }
+ const float MAX_TIMESCALE = 600.0f; // 10 min is a long time
+ if (_angularTimeScale > MAX_TIMESCALE) {
+ return;
+ }
+
+ // find normalized velocity
+ glm::vec3 velocity = bulletToGLM(rigidBody->getLinearVelocity());
+ float speed = glm::length(velocity);
+ const float TRAVEL_ORIENTED_TOO_SLOW = 0.001f; // meters / second
+ if (speed < TRAVEL_ORIENTED_TOO_SLOW) {
+ return;
+ }
+ glm::vec3 direction = glm::normalize(velocity);
+
+ // find current angle of "forward"
+ btQuaternion bodyRotation = rigidBody->getOrientation();
+ glm::quat orientation = bulletToGLM(bodyRotation);
+ glm::vec3 forwardInWorldFrame = glm::normalize(orientation * _forward);
+
+ // find the rotation that would line up direction and forward
+ glm::quat neededRotation = glm::rotation(forwardInWorldFrame, direction);
+ glm::quat rotationalTarget = neededRotation * orientation;
+
+ btVector3 targetAngularVelocity(0.0f, 0.0f, 0.0f);
+
+ auto alignmentDot = bodyRotation.dot(glmToBullet(rotationalTarget));
+ const float ALMOST_ONE = 0.99999f;
+ if (glm::abs(alignmentDot) < ALMOST_ONE) {
+ btQuaternion target = glmToBullet(rotationalTarget);
+ if (alignmentDot < 0.0f) {
+ target = -target;
+ }
+ // if dQ is the incremental rotation that gets an object from Q0 to Q1 then:
+ //
+ // Q1 = dQ * Q0
+ //
+ // solving for dQ gives:
+ //
+ // dQ = Q1 * Q0^
+ btQuaternion deltaQ = target * bodyRotation.inverse();
+ float speed = deltaQ.getAngle() / _angularTimeScale;
+ targetAngularVelocity = speed * deltaQ.getAxis();
+ if (speed > rigidBody->getAngularSleepingThreshold()) {
+ rigidBody->activate();
+ }
+ }
+ // this action is aggresively critically damped and defeats the current velocity
+ rigidBody->setAngularVelocity(targetAngularVelocity);
+ });
+}
+
+const float MIN_TIMESCALE = 0.1f;
+
+
+bool ObjectActionTravelOriented::updateArguments(QVariantMap arguments) {
+ glm::vec3 forward;
+ float angularTimeScale;
+
+ bool needUpdate = false;
+ bool somethingChanged = ObjectAction::updateArguments(arguments);
+ withReadLock([&]{
+ bool ok = true;
+ forward = EntityActionInterface::extractVec3Argument("travel oriented action", arguments, "forward", ok, true);
+ if (!ok) {
+ forward = _forward;
+ }
+ ok = true;
+ angularTimeScale =
+ EntityActionInterface::extractFloatArgument("travel oriented action", arguments, "angularTimeScale", ok, false);
+ if (!ok) {
+ angularTimeScale = _angularTimeScale;
+ }
+
+ if (somethingChanged ||
+ forward != _forward ||
+ angularTimeScale != _angularTimeScale) {
+ // something changed
+ needUpdate = true;
+ }
+ });
+
+ if (needUpdate) {
+ withWriteLock([&] {
+ _forward = forward;
+ _angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale));
+ _active = (_forward != glm::vec3());
+
+ auto ownerEntity = _ownerEntity.lock();
+ if (ownerEntity) {
+ ownerEntity->setActionDataDirty(true);
+ ownerEntity->setActionDataNeedsTransmit(true);
+ }
+ });
+ activateBody();
+ }
+
+ return true;
+}
+
+QVariantMap ObjectActionTravelOriented::getArguments() {
+ QVariantMap arguments = ObjectAction::getArguments();
+ withReadLock([&] {
+ arguments["forward"] = glmToQMap(_forward);
+ arguments["angularTimeScale"] = _angularTimeScale;
+ });
+ return arguments;
+}
+
+QByteArray ObjectActionTravelOriented::serialize() const {
+ QByteArray serializedActionArguments;
+ QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
+
+ dataStream << ACTION_TYPE_TRAVEL_ORIENTED;
+ dataStream << getID();
+ dataStream << ObjectActionTravelOriented::actionVersion;
+
+ withReadLock([&] {
+ dataStream << _forward;
+ dataStream << _angularTimeScale;
+
+ dataStream << localTimeToServerTime(_expires);
+ dataStream << _tag;
+ });
+
+ return serializedActionArguments;
+}
+
+void ObjectActionTravelOriented::deserialize(QByteArray serializedArguments) {
+ QDataStream dataStream(serializedArguments);
+
+ EntityActionType type;
+ dataStream >> type;
+ assert(type == getType());
+
+ QUuid id;
+ dataStream >> id;
+ assert(id == getID());
+
+ uint16_t serializationVersion;
+ dataStream >> serializationVersion;
+ if (serializationVersion != ObjectActionTravelOriented::actionVersion) {
+ assert(false);
+ return;
+ }
+
+ withWriteLock([&] {
+ dataStream >> _forward;
+ dataStream >> _angularTimeScale;
+
+ quint64 serverExpires;
+ dataStream >> serverExpires;
+ _expires = serverTimeToLocalTime(serverExpires);
+
+ dataStream >> _tag;
+
+ _active = (_forward != glm::vec3());
+ });
+}
diff --git a/libraries/physics/src/ObjectActionTravelOriented.h b/libraries/physics/src/ObjectActionTravelOriented.h
new file mode 100644
index 0000000000..66a425b409
--- /dev/null
+++ b/libraries/physics/src/ObjectActionTravelOriented.h
@@ -0,0 +1,39 @@
+//
+// ObjectActionTravelOriented.h
+// libraries/physics/src
+//
+// Created by Seth Alves 2016-8-28
+// 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
+//
+
+#ifndef hifi_ObjectActionTravelOriented_h
+#define hifi_ObjectActionTravelOriented_h
+
+#include "ObjectAction.h"
+
+class ObjectActionTravelOriented : public ObjectAction {
+public:
+ ObjectActionTravelOriented(const QUuid& id, EntityItemPointer ownerEntity);
+ virtual ~ObjectActionTravelOriented();
+
+ virtual bool updateArguments(QVariantMap arguments) override;
+ virtual QVariantMap getArguments() override;
+
+ virtual void updateActionWorker(float deltaTimeStep) override;
+
+ virtual QByteArray serialize() const override;
+ virtual void deserialize(QByteArray serializedArguments) override;
+
+protected:
+ static const uint16_t actionVersion;
+
+ glm::vec3 _forward { glm::vec3() }; // the vector in object space that should point in the direction of travel
+ float _angularTimeScale { 0.1f };
+
+ glm::vec3 _angularVelocityTarget;
+};
+
+#endif // hifi_ObjectActionTravelOriented_h
diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h
index 5111bda95f..eac08716a1 100644
--- a/libraries/plugins/src/plugins/DisplayPlugin.h
+++ b/libraries/plugins/src/plugins/DisplayPlugin.h
@@ -128,6 +128,7 @@ public:
Present = QEvent::User + 1
};
+ virtual int getRequiredThreadCount() const { return 0; }
virtual bool isHmd() const { return false; }
virtual int getHmdScreen() const { return -1; }
/// By default, all HMDs are stereo
diff --git a/libraries/script-engine/src/ScriptsModel.cpp b/libraries/script-engine/src/ScriptsModel.cpp
index 0ad2ad01a7..8755195932 100644
--- a/libraries/script-engine/src/ScriptsModel.cpp
+++ b/libraries/script-engine/src/ScriptsModel.cpp
@@ -185,6 +185,7 @@ void ScriptsModel::requestDefaultFiles(QString marker) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(request);
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp
index 15b2576331..818521ecc5 100644
--- a/libraries/script-engine/src/XMLHttpRequestClass.cpp
+++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp
@@ -45,6 +45,7 @@ XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) :
_timer(this),
_numRedirects(0) {
+ _request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
_timer.setSingleShot(true);
}
diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp
index 8e6c1ef6ed..171b58de17 100644
--- a/libraries/shared/src/RegisteredMetaTypes.cpp
+++ b/libraries/shared/src/RegisteredMetaTypes.cpp
@@ -18,6 +18,7 @@
#include
#include
#include
+#include
#include "RegisteredMetaTypes.h"
@@ -32,6 +33,7 @@ int xColorMetaTypeId = qRegisterMetaType();
int pickRayMetaTypeId = qRegisterMetaType();
int collisionMetaTypeId = qRegisterMetaType();
int qMapURLStringMetaTypeId = qRegisterMetaType>();
+int socketErrorMetaTypeId = qRegisterMetaType();
void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue);
diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp
index c2732197d3..f4b365265d 100644
--- a/libraries/ui/src/VrMenu.cpp
+++ b/libraries/ui/src/VrMenu.cpp
@@ -155,7 +155,7 @@ void bindActionToQmlAction(QObject* qmlAction, QAction* action) {
QObject::connect(qmlAction, SIGNAL(triggered()), action, SLOT(trigger()));
}
-class QQuickMenuItem;
+class QQuickMenuItem1;
void VrMenu::addAction(QMenu* menu, QAction* action) {
Q_ASSERT(!MenuUserData::hasData(action));
@@ -167,10 +167,9 @@ void VrMenu::addAction(QMenu* menu, QAction* action) {
}
QObject* menuQml = findMenuObject(userData->uuid.toString());
Q_ASSERT(menuQml);
- QQuickMenuItem* returnedValue { nullptr };
-
+ QQuickMenuItem1* returnedValue { nullptr };
bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItem", Qt::DirectConnection,
- Q_RETURN_ARG(QQuickMenuItem*, returnedValue),
+ Q_RETURN_ARG(QQuickMenuItem1*, returnedValue),
Q_ARG(QString, action->text()));
Q_ASSERT(invokeResult);
@@ -206,10 +205,10 @@ void VrMenu::insertAction(QAction* before, QAction* action) {
beforeQml = findMenuObject(beforeUserData->uuid.toString());
}
QObject* menu = beforeQml->parent();
- QQuickMenuItem* returnedValue { nullptr };
+ QQuickMenuItem1* returnedValue { nullptr };
// FIXME this needs to find the index of the beforeQml item and call insertItem(int, object)
bool invokeResult = QMetaObject::invokeMethod(menu, "addItem", Qt::DirectConnection,
- Q_RETURN_ARG(QQuickMenuItem*, returnedValue),
+ Q_RETURN_ARG(QQuickMenuItem1*, returnedValue),
Q_ARG(QString, action->text()));
Q_ASSERT(invokeResult);
QObject* result = reinterpret_cast(returnedValue); // returnedValue.value();
diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h
index 75f8c51b0e..025f879d84 100644
--- a/plugins/openvr/src/OpenVrDisplayPlugin.h
+++ b/plugins/openvr/src/OpenVrDisplayPlugin.h
@@ -58,6 +58,9 @@ public:
void unsuppressKeyboard() override;
bool isKeyboardVisible() override;
+ // Needs an additional thread for VR submission
+ int getRequiredThreadCount() const override { return Parent::getRequiredThreadCount() + 1; }
+
protected:
bool internalActivate() override;
void internalDeactivate() override;
diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js
index 60d41d9105..28488c9f3b 100644
--- a/scripts/system/controllers/handControllerGrab.js
+++ b/scripts/system/controllers/handControllerGrab.js
@@ -11,6 +11,7 @@
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+/* global setEntityCustomData, getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4 */
(function() { // BEGIN LOCAL_SCOPE
@@ -24,6 +25,9 @@ var WANT_DEBUG = false;
var WANT_DEBUG_STATE = false;
var WANT_DEBUG_SEARCH_NAME = null;
+var FORCE_IGNORE_IK = true;
+var SHOW_GRAB_POINT_SPHERE = true;
+
//
// these tune time-averaging and "on" value for analog trigger
//
@@ -59,6 +63,7 @@ var EQUIP_SPHERE_COLOR = {
var EQUIP_SPHERE_ALPHA = 0.15;
var EQUIP_SPHERE_SCALE_FACTOR = 0.65;
+
//
// distant manipulation
//
@@ -87,21 +92,21 @@ var COLORS_GRAB_DISTANCE_HOLD = {
blue: 214
};
-
var PICK_MAX_DISTANCE = 500; // max length of pick-ray
//
// near grabbing
//
-var EQUIP_RADIUS = 0.1; // radius used for palm vs equip-hotspot for equipping.
+var EQUIP_RADIUS = 0.2; // radius used for palm vs equip-hotspot for equipping.
// if EQUIP_HOTSPOT_RENDER_RADIUS is greater than zero, the hotspot will appear before the hand
// has reached the required position, and then grow larger once the hand is close enough to equip.
var EQUIP_HOTSPOT_RENDER_RADIUS = 0.0; // radius used for palm vs equip-hotspot for rendering hot-spots
+var MAX_EQUIP_HOTSPOT_RADIUS = 1.0;
var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position
-var NEAR_GRAB_RADIUS = 0.15; // radius used for palm vs object for near grabbing.
+var NEAR_GRAB_RADIUS = 0.07; // radius used for palm vs object for near grabbing.
var NEAR_GRAB_MAX_DISTANCE = 1.0; // you cannot grab objects that are this far away from your hand
var NEAR_GRAB_PICK_RADIUS = 0.25; // radius used for search ray vs object for near grabbing.
@@ -112,6 +117,13 @@ var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-g
// if an equipped item is "adjusted" to be too far from the hand it's in, it will be unequipped.
var CHECK_TOO_FAR_UNEQUIP_TIME = 0.3; // seconds, duration between checks
+
+var GRAB_POINT_SPHERE_OFFSET = { x: 0.0, y: 0.2, z: 0.0 };
+var GRAB_POINT_SPHERE_RADIUS = NEAR_GRAB_RADIUS;
+var GRAB_POINT_SPHERE_COLOR = { red: 20, green: 90, blue: 238 };
+var GRAB_POINT_SPHERE_ALPHA = 0.85;
+
+
//
// other constants
//
@@ -168,7 +180,7 @@ var USE_BLACKLIST = true;
var blacklist = [];
var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"];
-var FORBIDDEN_GRAB_TYPES = ['Unknown', 'Light', 'PolyLine', 'Zone'];
+var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"];
// states for the state machine
var STATE_OFF = 0;
@@ -183,7 +195,6 @@ var STATE_ENTITY_TOUCHING = 7;
// "collidesWith" is specified by comma-separated list of group names
// the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar
var COLLIDES_WITH_WHILE_GRABBED = "dynamic,otherAvatar";
-var COLLIDES_WITH_WHILE_MULTI_GRABBED = "dynamic";
var HEART_BEAT_INTERVAL = 5 * MSECS_PER_SEC;
var HEART_BEAT_TIMEOUT = 15 * MSECS_PER_SEC;
@@ -273,11 +284,9 @@ function projectOntoEntityXYPlane(entityID, worldPos) {
y: (1 - normalizedPos.y) * props.dimensions.y }; // flip y-axis
}
-function handLaserIntersectEntity(entityID, hand) {
- var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
- var pose = Controller.getPoseValue(standardControllerValue);
- var worldHandPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position);
- var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation);
+function handLaserIntersectEntity(entityID, start) {
+ var worldHandPosition = start.position;
+ var worldHandRotation = start.orientation;
var props = entityPropertiesCache.getProps(entityID);
@@ -355,7 +364,7 @@ function entityIsGrabbedByOther(entityID) {
for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
var actionID = actionIDs[actionIndex];
var actionArguments = Entities.getActionArguments(entityID, actionID);
- var tag = actionArguments["tag"];
+ var tag = actionArguments.tag;
if (tag == getTag()) {
// we see a grab-*uuid* shaped tag, but it's our tag, so that's okay.
continue;
@@ -680,9 +689,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp) {
if (overlayInfoSet.timestamp != timestamp && overlayInfoSet.currentSize <= 0.05) {
// this is an old overlay, that has finished fading out, delete it!
- overlayInfoSet.overlays.forEach(function(overlay) {
- Overlays.deleteOverlay(overlay);
- });
+ overlayInfoSet.overlays.forEach(Overlays.deleteOverlay);
delete this.map[keys[i]];
} else {
// update overlay position, rotation to follow the object it's attached to.
@@ -714,16 +721,36 @@ var equipHotspotBuddy = new EquipHotspotBuddy();
function MyController(hand) {
this.hand = hand;
- if (this.hand === RIGHT_HAND) {
- this.getHandPosition = MyAvatar.getRightPalmPosition;
- // this.getHandRotation = MyAvatar.getRightPalmRotation;
- } else {
- this.getHandPosition = MyAvatar.getLeftPalmPosition;
- // this.getHandRotation = MyAvatar.getLeftPalmRotation;
- }
- this.getHandRotation = function() {
- var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
- return Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
+ this.autoUnequipCounter = 0;
+
+ // handPosition is where the avatar's hand appears to be, in-world.
+ this.getHandPosition = function () {
+ if (this.hand === RIGHT_HAND) {
+ return MyAvatar.getRightPalmPosition();
+ } else {
+ return MyAvatar.getLeftPalmPosition();
+ }
+ };
+ this.getHandRotation = function () {
+ if (this.hand === RIGHT_HAND) {
+ return MyAvatar.getRightPalmRotation();
+ } else {
+ return MyAvatar.getLeftPalmRotation();
+ }
+ };
+ // controllerLocation is where the controller would be, in-world.
+ this.getControllerLocation = function (doOffset) {
+ var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
+ var pose = Controller.getPoseValue(standardControllerValue);
+
+ var orientation = Quat.multiply(MyAvatar.orientation, pose.rotation);
+ var position = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position);
+ // add to the real position so the grab-point is out in front of the hand, a bit
+ if (doOffset) {
+ position = Vec3.sum(position, Vec3.multiplyQbyV(orientation, GRAB_POINT_SPHERE_OFFSET));
+ }
+
+ return {position: position, orientation: orientation};
};
this.actionID = null; // action this script created...
@@ -830,6 +857,39 @@ function MyController(hand) {
}
};
+ this.grabPointSphereOn = function() {
+ if (!SHOW_GRAB_POINT_SPHERE) {
+ return;
+ }
+ if (!MyAvatar.sessionUUID) {
+ return;
+ }
+ if (!this.grabPointSphere) {
+ this.grabPointSphere = Overlays.addOverlay("sphere", {
+ localPosition: GRAB_POINT_SPHERE_OFFSET,
+ localRotation: { x: 0, y: 0, z: 0, w: 1 },
+ dimensions: GRAB_POINT_SPHERE_RADIUS,
+ color: GRAB_POINT_SPHERE_COLOR,
+ alpha: GRAB_POINT_SPHERE_ALPHA,
+ solid: true,
+ visible: true,
+ ignoreRayIntersection: true,
+ drawInFront: false,
+ parentID: MyAvatar.sessionUUID,
+ parentJointIndex: MyAvatar.getJointIndex(this.hand === RIGHT_HAND ?
+ "_CONTROLLER_RIGHTHAND" :
+ "_CONTROLLER_LEFTHAND")
+ });
+ }
+ };
+
+ this.grabPointSphereOff = function() {
+ if (this.grabPointSphere) {
+ Overlays.deleteOverlay(this.grabPointSphere);
+ this.grabPointSphere = null;
+ }
+ };
+
this.searchSphereOn = function(location, size, color) {
var rotation = Quat.lookAt(location, Camera.getPosition(), Vec3.UP);
@@ -905,10 +965,14 @@ function MyController(hand) {
var searchSphereLocation = Vec3.sum(distantPickRay.origin,
Vec3.multiply(distantPickRay.direction, this.searchSphereDistance));
this.searchSphereOn(searchSphereLocation, SEARCH_SPHERE_SIZE * this.searchSphereDistance,
- (this.triggerSmoothedGrab() || this.secondarySqueezed()) ? COLORS_GRAB_SEARCHING_FULL_SQUEEZE : COLORS_GRAB_SEARCHING_HALF_SQUEEZE);
+ (this.triggerSmoothedGrab() || this.secondarySqueezed()) ?
+ COLORS_GRAB_SEARCHING_FULL_SQUEEZE :
+ COLORS_GRAB_SEARCHING_HALF_SQUEEZE);
if (PICK_WITH_HAND_RAY) {
this.overlayLineOn(handPosition, searchSphereLocation,
- (this.triggerSmoothedGrab() || this.secondarySqueezed()) ? COLORS_GRAB_SEARCHING_FULL_SQUEEZE : COLORS_GRAB_SEARCHING_HALF_SQUEEZE);
+ (this.triggerSmoothedGrab() || this.secondarySqueezed()) ?
+ COLORS_GRAB_SEARCHING_FULL_SQUEEZE :
+ COLORS_GRAB_SEARCHING_HALF_SQUEEZE);
}
};
@@ -958,7 +1022,8 @@ function MyController(hand) {
this.turnOffVisualizations = function() {
this.overlayLineOff();
-
+ this.grabPointSphereOff();
+ this.lineOff();
this.searchSphereOff();
restore2DMode();
@@ -1029,19 +1094,20 @@ function MyController(hand) {
}
if (!this.waitForTriggerRelease && this.triggerSmoothedSqueezed()) {
this.lastPickTime = 0;
- var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
- this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
+ this.startingHandRotation = this.getControllerLocation(true).orientation;
if (this.triggerSmoothedSqueezed()) {
this.setState(STATE_SEARCHING, "trigger squeeze detected");
return;
}
}
- var candidateEntities = Entities.findEntities(this.getHandPosition(), EQUIP_HOTSPOT_RENDER_RADIUS);
+ this.grabPointSphereOn();
+
+ var candidateEntities = Entities.findEntities(this.getControllerLocation(true).position, MAX_EQUIP_HOTSPOT_RADIUS);
entityPropertiesCache.addEntities(candidateEntities);
var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities);
if (!this.waitForTriggerRelease) {
- this.updateEquipHaptics(potentialEquipHotspot, this.getHandPosition());
+ this.updateEquipHaptics(potentialEquipHotspot, this.getControllerLocation(true).position);
}
var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS);
@@ -1060,23 +1126,27 @@ function MyController(hand) {
!potentialEquipHotspot && this.prevPotentialEquipHotspot) {
Controller.triggerHapticPulse(HAPTIC_TEXTURE_STRENGTH, HAPTIC_TEXTURE_DURATION, this.hand);
this.lastHapticPulseLocation = currentLocation;
- } else if (potentialEquipHotspot && Vec3.distance(this.lastHapticPulseLocation, currentLocation) > HAPTIC_TEXTURE_DISTANCE) {
+ } else if (potentialEquipHotspot &&
+ Vec3.distance(this.lastHapticPulseLocation, currentLocation) > HAPTIC_TEXTURE_DISTANCE) {
Controller.triggerHapticPulse(HAPTIC_TEXTURE_STRENGTH, HAPTIC_TEXTURE_DURATION, this.hand);
this.lastHapticPulseLocation = currentLocation;
}
this.prevPotentialEquipHotspot = potentialEquipHotspot;
};
+ this.heartBeatIsStale = function(data) {
+ var now = Date.now();
+ return data.heartBeat === undefined || now - data.heartBeat > HEART_BEAT_TIMEOUT;
+ };
+
// Performs ray pick test from the hand controller into the world
// @param {number} which hand to use, RIGHT_HAND or LEFT_HAND
// @returns {object} returns object with two keys entityID and distance
//
this.calcRayPickInfo = function(hand) {
-
- var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
- var pose = Controller.getPoseValue(standardControllerValue);
- var worldHandPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position);
- var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation);
+ var controllerLocation = this.getControllerLocation(true);
+ var worldHandPosition = controllerLocation.position;
+ var worldHandRotation = controllerLocation.orientation;
var pickRay = {
origin: PICK_WITH_HAND_RAY ? worldHandPosition : Camera.position,
@@ -1196,7 +1266,7 @@ function MyController(hand) {
var okToEquipFromOtherHand = ((this.getOtherHandController().state == STATE_NEAR_GRABBING ||
this.getOtherHandController().state == STATE_DISTANCE_HOLDING) &&
this.getOtherHandController().grabbedEntity == hotspot.entityID);
- if (refCount > 0 && !okToEquipFromOtherHand) {
+ if (refCount > 0 && !this.heartBeatIsStale(grabProps) && !okToEquipFromOtherHand) {
if (debug) {
print("equip is skipping '" + props.name + "': grabbed by someone else");
}
@@ -1213,20 +1283,21 @@ function MyController(hand) {
var physical = propsArePhysical(props);
var grabbable = false;
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
+ var refCount = ("refCount" in grabProps) ? grabProps.refCount : 0;
if (physical) {
// physical things default to grabbable
grabbable = true;
} else {
// non-physical things default to non-grabbable unless they are already grabbed
- if ("refCount" in grabProps && grabProps.refCount > 0) {
+ if (refCount > 0) {
grabbable = true;
} else {
grabbable = false;
}
}
- if (grabbableProps.hasOwnProperty("grabbable")) {
+ if (grabbableProps.hasOwnProperty("grabbable") && refCount === 0) {
grabbable = grabbableProps.grabbable;
}
@@ -1321,7 +1392,7 @@ function MyController(hand) {
return _this.collectEquipHotspots(entityID);
})).filter(function(hotspot) {
return (_this.hotspotIsEquippable(hotspot) &&
- Vec3.distance(hotspot.worldPosition, _this.getHandPosition()) < hotspot.radius + distance);
+ Vec3.distance(hotspot.worldPosition, _this.getControllerLocation(true).position) < hotspot.radius + distance);
});
return equippableHotspots;
};
@@ -1332,8 +1403,8 @@ function MyController(hand) {
if (equippableHotspots.length > 0) {
// sort by distance
equippableHotspots.sort(function(a, b) {
- var aDistance = Vec3.distance(a.worldPosition, this.getHandPosition());
- var bDistance = Vec3.distance(b.worldPosition, this.getHandPosition());
+ var aDistance = Vec3.distance(a.worldPosition, this.getControllerLocation(true).position);
+ var bDistance = Vec3.distance(b.worldPosition, this.getControllerLocation(true).position);
return aDistance - bDistance;
});
return equippableHotspots[0];
@@ -1359,6 +1430,8 @@ function MyController(hand) {
this.isInitialGrab = false;
this.shouldResetParentOnRelease = false;
+ this.grabPointSphereOn();
+
this.checkForStrayChildren();
if (this.triggerSmoothedReleased()) {
@@ -1366,7 +1439,7 @@ function MyController(hand) {
return;
}
- var handPosition = this.getHandPosition();
+ var handPosition = this.getControllerLocation(true).position;
var rayPickInfo = this.calcRayPickInfo(this.hand);
@@ -1374,10 +1447,10 @@ function MyController(hand) {
entityPropertiesCache.addEntity(rayPickInfo.entityID);
}
- var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS);
- entityPropertiesCache.addEntities(candidateEntities);
+ var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS);
+ entityPropertiesCache.addEntities(candidateHotSpotEntities);
- var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities);
+ var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateHotSpotEntities);
if (potentialEquipHotspot) {
if (this.triggerSmoothedGrab()) {
this.grabbedHotspot = potentialEquipHotspot;
@@ -1387,6 +1460,7 @@ function MyController(hand) {
}
}
+ var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS);
var grabbableEntities = candidateEntities.filter(function(entity) {
return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE);
});
@@ -1546,18 +1620,13 @@ function MyController(hand) {
};
this.distanceHoldingEnter = function() {
- Messages.sendLocalMessage('Hifi-Teleport-Disabler','both');
+ Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'both');
this.clearEquipHaptics();
+ this.grabPointSphereOff();
- // controller pose is in avatar frame
- var device = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
- var avatarControllerPose = Controller.getPoseValue(device);
+ var worldControllerPosition = this.getControllerLocation(true).position;
- // transform it into world frame
- var worldControllerPosition = Vec3.sum(MyAvatar.position,
- Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation));
-
- // also transform the position into room space
+ // transform the position into room space
var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix());
var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition);
@@ -1620,14 +1689,10 @@ function MyController(hand) {
this.heartBeat(this.grabbedEntity);
- // controller pose is in avatar frame
- var device = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
- var avatarControllerPose = Controller.getPoseValue(device);
- // transform it into world frame
- var worldControllerPosition = Vec3.sum(MyAvatar.position,
- Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation));
- var worldControllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation);
+ var controllerLocation = this.getControllerLocation(true);
+ var worldControllerPosition = controllerLocation.position;
+ var worldControllerRotation = controllerLocation.orientation;
// also transform the position into room space
var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix());
@@ -1698,8 +1763,6 @@ function MyController(hand) {
}
}
- var handPosition = this.getHandPosition();
-
// visualizations
var rayPickInfo = this.calcRayPickInfo(this.hand);
@@ -1764,10 +1827,7 @@ function MyController(hand) {
};
this.dropGestureProcess = function(deltaTime) {
- var standardControllerValue = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
- var pose = Controller.getPoseValue(standardControllerValue);
- var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation);
-
+ var worldHandRotation = this.getControllerLocation(true).orientation;
var localHandUpAxis = this.hand === RIGHT_HAND ? {
x: 1,
y: 0,
@@ -1806,14 +1866,14 @@ function MyController(hand) {
this.nearGrabbingEnter = function() {
if (this.hand === 0) {
Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'left');
-
}
if (this.hand === 1) {
Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'right');
-
}
+ this.grabPointSphereOff();
this.lineOff();
this.overlayLineOff();
+ this.searchSphereOff();
this.dropGestureReset();
this.clearEquipHaptics();
@@ -1835,12 +1895,23 @@ function MyController(hand) {
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
this.activateEntity(this.grabbedEntity, grabbedProperties, false);
- // var handRotation = this.getHandRotation();
- var handRotation = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation();
- var handPosition = this.getHandPosition();
-
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
- this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false;
+ if (FORCE_IGNORE_IK) {
+ this.ignoreIK = true;
+ } else {
+ this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false;
+ }
+
+ var handRotation;
+ var handPosition;
+ if (this.ignoreIK) {
+ var controllerLocation = this.getControllerLocation(false);
+ handRotation = controllerLocation.orientation;
+ handPosition = controllerLocation.position;
+ } else {
+ handRotation = this.getHandRotation();
+ handPosition = this.getHandPosition();
+ }
var hasPresetPosition = false;
if (this.state == STATE_HOLD && this.grabbedHotspot) {
@@ -1879,12 +1950,21 @@ function MyController(hand) {
}
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'grab',
- grabbedEntity: this.grabbedEntity
+ grabbedEntity: this.grabbedEntity,
+ joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
} else {
// grab entity via parenting
this.actionID = null;
- var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
+ var handJointIndex;
+ if (this.ignoreIK) {
+ handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ?
+ "_CONTROLLER_RIGHTHAND" :
+ "_CONTROLLER_LEFTHAND");
+ } else {
+ handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
+ }
+
var reparentProps = {
parentID: MyAvatar.sessionUUID,
parentJointIndex: handJointIndex,
@@ -1892,14 +1972,15 @@ function MyController(hand) {
angularVelocity: {x: 0, y: 0, z: 0}
};
if (hasPresetPosition) {
- reparentProps["localPosition"] = this.offsetPosition;
- reparentProps["localRotation"] = this.offsetRotation;
+ reparentProps.localPosition = this.offsetPosition;
+ reparentProps.localRotation = this.offsetRotation;
}
Entities.editEntity(this.grabbedEntity, reparentProps);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'equip',
- grabbedEntity: this.grabbedEntity
+ grabbedEntity: this.grabbedEntity,
+ joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
}
@@ -1937,6 +2018,8 @@ function MyController(hand) {
this.nearGrabbing = function(deltaTime, timestamp) {
+ this.grabPointSphereOff();
+
if (this.state == STATE_NEAR_GRABBING && !this.triggerClicked) {
this.callEntityMethodOnGrabbed("releaseGrab");
this.setState(STATE_OFF, "trigger released");
@@ -1967,9 +2050,10 @@ function MyController(hand) {
// store the offset attach points into preferences.
if (USE_ATTACH_POINT_SETTINGS && this.grabbedHotspot && this.grabbedEntity) {
- var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]);
- if (props && props.localPosition && props.localRotation) {
- storeAttachPointForHotspotInSettings(this.grabbedHotspot, this.hand, props.localPosition, props.localRotation);
+ var prefprops = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]);
+ if (prefprops && prefprops.localPosition && prefprops.localRotation) {
+ storeAttachPointForHotspotInSettings(this.grabbedHotspot, this.hand,
+ prefprops.localPosition, prefprops.localRotation);
}
}
@@ -1988,22 +2072,34 @@ function MyController(hand) {
"position", "rotation", "dimensions",
"registrationPoint"]);
if (!props.position) {
- // server may have reset, taking our equipped entity with it. move back to "off" stte
+ // server may have reset, taking our equipped entity with it. move back to "off" state
this.callEntityMethodOnGrabbed("releaseGrab");
this.setState(STATE_OFF, "entity has no position property");
return;
}
var now = Date.now();
- if (now - this.lastUnequipCheckTime > MSECS_PER_SEC * CHECK_TOO_FAR_UNEQUIP_TIME) {
+ if (this.state == STATE_HOLD && now - this.lastUnequipCheckTime > MSECS_PER_SEC * CHECK_TOO_FAR_UNEQUIP_TIME) {
this.lastUnequipCheckTime = now;
if (props.parentID == MyAvatar.sessionUUID) {
- var handPosition = this.getHandPosition();
+ var handPosition;
+ if (this.ignoreIK) {
+ handPosition = this.getControllerLocation(false).position;
+ } else {
+ handPosition = this.getHandPosition();
+ }
var TEAR_AWAY_DISTANCE = 0.1;
var dist = distanceBetweenPointAndEntityBoundingBox(handPosition, props);
if (dist > TEAR_AWAY_DISTANCE) {
+ this.autoUnequipCounter += 1;
+ } else {
+ this.autoUnequipCounter = 0;
+ }
+
+ if (this.autoUnequipCounter > 1) {
+ // for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip.
print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." +
props.parentID + ", dist = " + dist);
@@ -2076,16 +2172,15 @@ function MyController(hand) {
};
this.nearTriggerEnter = function() {
-
this.clearEquipHaptics();
-
+ this.grabPointSphereOff();
Controller.triggerShortHapticPulse(1.0, this.hand);
this.callEntityMethodOnGrabbed("startNearTrigger");
};
this.farTriggerEnter = function() {
this.clearEquipHaptics();
-
+ this.grabPointSphereOff();
this.callEntityMethodOnGrabbed("startFarTrigger");
};
@@ -2105,10 +2200,9 @@ function MyController(hand) {
return;
}
- var handPosition = this.getHandPosition();
var pickRay = {
- origin: handPosition,
- direction: Quat.getUp(this.getHandRotation())
+ origin: this.getControllerLocation().position,
+ direction: Quat.getUp(this.getControllerLocation().orientation)
};
var now = Date.now();
@@ -2137,7 +2231,7 @@ function MyController(hand) {
this.entityTouchingEnter = function() {
// test for intersection between controller laser and web entity plane.
- var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand);
+ var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.getControllerLocation(true));
if (intersectInfo) {
var pointerEvent = {
type: "Press",
@@ -2162,7 +2256,7 @@ function MyController(hand) {
this.entityTouchingExit = function() {
// test for intersection between controller laser and web entity plane.
- var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand);
+ var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.getControllerLocation(true));
if (intersectInfo) {
var pointerEvent;
if (this.deadspotExpired) {
@@ -2200,7 +2294,7 @@ function MyController(hand) {
}
// test for intersection between controller laser and web entity plane.
- var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand);
+ var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.getControllerLocation(true));
if (intersectInfo) {
if (Entities.keyboardFocusEntity != this.grabbedEntity) {
@@ -2237,7 +2331,7 @@ function MyController(hand) {
};
this.release = function() {
- Messages.sendLocalMessage('Hifi-Teleport-Disabler','none');
+ Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none');
this.turnOffVisualizations();
var noVelocity = false;
@@ -2249,16 +2343,7 @@ function MyController(hand) {
// If this looks like the release after adjusting something still held in the other hand, print the position
// and rotation of the held thing to help content creators set the userData.
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
- if (grabData.refCount > 1) {
- var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]);
- if (grabbedProperties && grabbedProperties.localPosition && grabbedProperties.localRotation) {
- print((this.hand === RIGHT_HAND ? '"LeftHand"' : '"RightHand"') + ":" +
- '[{"x":' + grabbedProperties.localPosition.x + ', "y":' + grabbedProperties.localPosition.y +
- ', "z":' + grabbedProperties.localPosition.z + '}, {"x":' + grabbedProperties.localRotation.x +
- ', "y":' + grabbedProperties.localRotation.y + ', "z":' + grabbedProperties.localRotation.z +
- ', "w":' + grabbedProperties.localRotation.w + '}]');
- }
- }
+ this.printNewOffsets = (grabData.refCount > 1);
if (this.actionID !== null) {
Entities.deleteAction(this.grabbedEntity, this.actionID);
@@ -2272,17 +2357,17 @@ function MyController(hand) {
noVelocity = true;
}
}
+
+ this.deactivateEntity(this.grabbedEntity, noVelocity);
+
+ Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
+ action: 'release',
+ grabbedEntity: this.grabbedEntity,
+ joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
+ }));
}
- this.deactivateEntity(this.grabbedEntity, noVelocity);
this.actionID = null;
-
- Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
- action: 'release',
- grabbedEntity: this.grabbedEntity,
- joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
- }));
-
this.grabbedEntity = null;
this.grabbedHotspot = null;
@@ -2293,13 +2378,14 @@ function MyController(hand) {
this.cleanup = function() {
this.release();
+ this.grabPointSphereOff();
};
this.heartBeat = function(entityID) {
var now = Date.now();
if (now - this.lastHeartBeat > HEART_BEAT_INTERVAL) {
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
- data["heartBeat"] = now;
+ data.heartBeat = now;
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
this.lastHeartBeat = now;
}
@@ -2308,12 +2394,14 @@ function MyController(hand) {
this.resetAbandonedGrab = function(entityID) {
print("cleaning up abandoned grab on " + entityID);
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
- data["refCount"] = 1;
+ data.refCount = 1;
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
this.deactivateEntity(entityID, false);
};
this.activateEntity = function(entityID, grabbedProperties, wasLoaded) {
+ this.autoUnequipCounter = 0;
+
if (this.entityActivated) {
return;
}
@@ -2327,29 +2415,29 @@ function MyController(hand) {
// get re-instated after all the grabs have been released) be correct.
Script.clearTimeout(delayedDeactivateTimeout);
delayedDeactivateTimeout = null;
- grabbedProperties["collidesWith"] = delayedDeactivateFunc();
+ grabbedProperties.collidesWith = delayedDeactivateFunc();
}
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
var now = Date.now();
if (wasLoaded) {
- data["refCount"] = 1;
+ data.refCount = 1;
} else {
- data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1;
+ data.refCount = data.refCount ? data.refCount + 1 : 1;
// zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done
- if (data["refCount"] == 1) {
- data["heartBeat"] = now;
+ if (data.refCount == 1) {
+ data.heartBeat = now;
this.lastHeartBeat = now;
this.isInitialGrab = true;
- data["gravity"] = grabbedProperties.gravity;
- data["collidesWith"] = grabbedProperties.collidesWith;
- data["collisionless"] = grabbedProperties.collisionless;
- data["dynamic"] = grabbedProperties.dynamic;
- data["parentID"] = wasLoaded ? NULL_UUID : grabbedProperties.parentID;
- data["parentJointIndex"] = grabbedProperties.parentJointIndex;
+ data.gravity = grabbedProperties.gravity;
+ data.collidesWith = grabbedProperties.collidesWith;
+ data.collisionless = grabbedProperties.collisionless;
+ data.dynamic = grabbedProperties.dynamic;
+ data.parentID = wasLoaded ? NULL_UUID : grabbedProperties.parentID;
+ data.parentJointIndex = grabbedProperties.parentJointIndex;
var whileHeldProperties = {
gravity: {
@@ -2363,9 +2451,8 @@ function MyController(hand) {
"collidesWith": COLLIDES_WITH_WHILE_GRABBED
};
Entities.editEntity(entityID, whileHeldProperties);
- } else if (data["refCount"] > 1) {
- if (data["heartBeat"] === undefined ||
- now - data["heartBeat"] > HEART_BEAT_TIMEOUT) {
+ } else if (data.refCount > 1) {
+ if (this.heartBeatIsStale(data)) {
// this entity has userData suggesting it is grabbed, but nobody is updating the hearbeat.
// deactivate it before grabbing.
this.resetAbandonedGrab(entityID);
@@ -2380,7 +2467,8 @@ function MyController(hand) {
// bootstrap themselves with the held object. This happens because the meaning of "otherAvatar" in
// the collision mask hinges on who the physics simulation owner is.
Entities.editEntity(entityID, {
- "collidesWith": COLLIDES_WITH_WHILE_MULTI_GRABBED
+ // "collidesWith": removeAvatarsFromCollidesWith(grabbedProperties.collidesWith)
+ collisionless: true
});
}
}
@@ -2393,6 +2481,10 @@ function MyController(hand) {
// unhook them.
var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, handJointIndex);
+ var controllerJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ?
+ "_CONTROLLER_RIGHTHAND" :
+ "_CONTROLLER_LEFTHAND");
+ children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, controllerJointIndex));
children.forEach(function(childID) {
print("disconnecting stray child of hand: (" + _this.hand + ") " + childID);
Entities.editEntity(childID, {
@@ -2411,9 +2503,9 @@ function MyController(hand) {
collidesWith: collidesWith
});
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
- if (data && data["refCount"]) {
- data["refCount"] = data["refCount"] - 1;
- if (data["refCount"] < 1) {
+ if (data && data.refCount) {
+ data.refCount = data.refCount - 1;
+ if (data.refCount < 1) {
data = null;
}
} else {
@@ -2433,24 +2525,24 @@ function MyController(hand) {
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
var doDelayedDeactivate = false;
- if (data && data["refCount"]) {
- data["refCount"] = data["refCount"] - 1;
- if (data["refCount"] < 1) {
+ if (data && data.refCount) {
+ data.refCount = data.refCount - 1;
+ if (data.refCount < 1) {
deactiveProps = {
- gravity: data["gravity"],
+ gravity: data.gravity,
// don't set collidesWith myAvatar back right away, because thrown things tend to bounce off the
// avatar's capsule.
- collidesWith: removeMyAvatarFromCollidesWith(data["collidesWith"]),
- collisionless: data["collisionless"],
- dynamic: data["dynamic"],
- parentID: data["parentID"],
- parentJointIndex: data["parentJointIndex"]
+ collidesWith: removeMyAvatarFromCollidesWith(data.collidesWith),
+ collisionless: data.collisionless,
+ dynamic: data.dynamic,
+ parentID: data.parentID,
+ parentJointIndex: data.parentJointIndex
};
- doDelayedDeactivate = (data["collidesWith"].indexOf("myAvatar") >= 0);
+ doDelayedDeactivate = (data.collidesWith.indexOf("myAvatar") >= 0);
if (doDelayedDeactivate) {
- var delayedCollidesWith = data["collidesWith"];
+ var delayedCollidesWith = data.collidesWith;
var delayedEntityID = entityID;
delayedDeactivateFunc = function() {
// set collidesWith back to original value a bit later than the rest
@@ -2470,19 +2562,19 @@ function MyController(hand) {
if (!noVelocity &&
parentID == MyAvatar.sessionUUID &&
- Vec3.length(data["gravity"]) > 0.0 &&
- data["dynamic"] &&
- data["parentID"] == NULL_UUID &&
- !data["collisionless"]) {
- deactiveProps["velocity"] = this.currentVelocity;
+ Vec3.length(data.gravity) > 0.0 &&
+ data.dynamic &&
+ data.parentID == NULL_UUID &&
+ !data.collisionless) {
+ deactiveProps.velocity = this.currentVelocity;
}
if (noVelocity) {
- deactiveProps["velocity"] = {
+ deactiveProps.velocity = {
x: 0.0,
y: 0.0,
z: 0.0
};
- deactiveProps["angularVelocity"] = {
+ deactiveProps.angularVelocity = {
x: 0.0,
y: 0.0,
z: 0.0
@@ -2508,6 +2600,17 @@ function MyController(hand) {
}
};
Entities.editEntity(entityID, deactiveProps);
+
+ if (this.printNewOffsets) {
+ var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]);
+ if (grabbedProperties && grabbedProperties.localPosition && grabbedProperties.localRotation) {
+ print((this.hand === RIGHT_HAND ? '"LeftHand"' : '"RightHand"') + ":" +
+ '[{"x":' + grabbedProperties.localPosition.x + ', "y":' + grabbedProperties.localPosition.y +
+ ', "z":' + grabbedProperties.localPosition.z + '}, {"x":' + grabbedProperties.localRotation.x +
+ ', "y":' + grabbedProperties.localRotation.y + ', "z":' + grabbedProperties.localRotation.z +
+ ', "w":' + grabbedProperties.localRotation.w + '}]');
+ }
+ }
} else if (noVelocity) {
Entities.editEntity(entityID, {
velocity: {
@@ -2520,7 +2623,7 @@ function MyController(hand) {
y: 0.0,
z: 0.0
},
- dynamic: data["dynamic"]
+ dynamic: data.dynamic
});
}
} else {
@@ -2567,9 +2670,13 @@ function update(deltaTime) {
if (handToDisable !== LEFT_HAND && handToDisable !== 'both') {
leftController.update(deltaTime, timestamp);
+ } else {
+ leftController.release();
}
if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') {
rightController.update(deltaTime, timestamp);
+ } else {
+ rightController.release();
}
equipHotspotBuddy.update(deltaTime, timestamp);
entityPropertiesCache.update();
@@ -2620,7 +2727,7 @@ var handleHandMessages = function(channel, message, sender) {
selectedController.nearGrabbingEnter();
} catch (e) {
- print("WARNING: error parsing Hifi-Hand-Grab message");
+ print("WARNING: handControllerGrab.js -- error parsing Hifi-Hand-Grab message: " + message);
}
} else if (channel === 'Hifi-Hand-RayPick-Blacklist') {
@@ -2640,7 +2747,7 @@ var handleHandMessages = function(channel, message, sender) {
}
} catch (e) {
- print("WARNING: error parsing Hifi-Hand-RayPick-Blacklist message");
+ print("WARNING: handControllerGrab.js -- error parsing Hifi-Hand-RayPick-Blacklist message: " + message);
}
}
}
diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js
index f3e94d23de..b4a8eefcd2 100644
--- a/scripts/system/controllers/teleport.js
+++ b/scripts/system/controllers/teleport.js
@@ -94,7 +94,6 @@ function Teleporter() {
this.initialize = function() {
this.createMappings();
- this.disableGrab();
};
this.createMappings = function() {
@@ -218,10 +217,6 @@ function Teleporter() {
this.updateConnected = null;
this.inCoolIn = false;
inTeleportMode = false;
-
- Script.setTimeout(function() {
- _this.enableGrab();
- }, 200);
};
this.update = function() {
@@ -494,14 +489,6 @@ function Teleporter() {
});
};
- this.disableGrab = function() {
- Messages.sendLocalMessage('Hifi-Hand-Disabler', this.teleportHand);
- };
-
- this.enableGrab = function() {
- Messages.sendLocalMessage('Hifi-Hand-Disabler', 'none');
- };
-
this.triggerHaptics = function() {
var hand = this.teleportHand === 'left' ? 0 : 1;
var haptic = Controller.triggerShortHapticPulse(0.2, hand);
diff --git a/scripts/system/directory.js b/scripts/system/directory.js
index 2eae4fa9cf..ac65615a68 100644
--- a/scripts/system/directory.js
+++ b/scripts/system/directory.js
@@ -73,8 +73,7 @@ var toolBar = (function() {
width: toolWidth,
height: toolHeight,
alpha: 0.9,
- visible: true,
- showButtonDown: true
+ visible: true
});
toolBar.showTool(browseDirectoryButton, true);
diff --git a/scripts/system/libraries/toolBars.js b/scripts/system/libraries/toolBars.js
index 5a84bf9027..e49f8c4004 100644
--- a/scripts/system/libraries/toolBars.js
+++ b/scripts/system/libraries/toolBars.js
@@ -130,8 +130,6 @@ Tool = function(properties, selectable, selected) { // selectable and selected a
if (update) {
if (selectable) {
this.toggle();
- } else if (properties.showButtonDown) {
- this.buttonDown(true);
}
}
return true;
diff --git a/tests/gpu-test/src/TestFbx.cpp b/tests/gpu-test/src/TestFbx.cpp
index cea356e125..538bb0a973 100644
--- a/tests/gpu-test/src/TestFbx.cpp
+++ b/tests/gpu-test/src/TestFbx.cpp
@@ -56,6 +56,7 @@ public:
explicit FileDownloader(QUrl imageUrl, QObject *parent = 0) : QObject(parent) {
connect(&m_WebCtrl, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*)));
QNetworkRequest request(imageUrl);
+ request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
m_WebCtrl.get(request);
}