Merge branch 'master' of https://github.com/highfidelity/hifi into hdr

This commit is contained in:
sam 2016-09-06 23:37:16 -07:00
commit 248f172832
61 changed files with 881 additions and 655 deletions

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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}
)

View file

@ -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}
)

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
</dict>
</plist>

View file

@ -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.

View file

@ -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);
}

View file

@ -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;

View file

@ -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)

View file

@ -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
} // dialog

View file

@ -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
}

View file

@ -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;

View file

@ -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;

View file

@ -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
}
}

View file

@ -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<PluginContainer*>(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<AddressManager>().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);
}

View file

@ -286,6 +286,7 @@ public slots:
bool exportEntities(const QString& filename, const QVector<EntityItemID>& 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();

View file

@ -14,6 +14,7 @@
#include <avatar/AvatarActionHold.h>
#include <ObjectActionOffset.h>
#include <ObjectActionSpring.h>
#include <ObjectActionTravelOriented.h>
#include <LogHandler.h>
#include "InterfaceActionFactory.h"
@ -29,6 +30,8 @@ EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& i
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
case ACTION_TYPE_HOLD:
return std::make_shared<AvatarActionHold>(id, ownerEntity);
case ACTION_TYPE_TRAVEL_ORIENTED:
return std::make_shared<ObjectActionTravelOriented>(id, ownerEntity);
}
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity action type");

View file

@ -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;
}

View file

@ -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<AvatarManager>()->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());
}
}

View file

@ -532,11 +532,21 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
_hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation);
}
void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeValueCache<glm::mat4>& matrixCache) {
assert(QThread::currentThread() == thread());
auto userInputMapper = DependencyManager::get<UserInputMapper>();
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);
}
}
}

View file

@ -19,6 +19,7 @@
#include <Sound.h>
#include <controllers/Pose.h>
#include <controllers/Actions.h>
#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<glm::mat4>& 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.

View file

@ -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 <QVBoxLayout>
#include <QApplication>
#include <QMainWindow>
#include <QDockWidget>
#include <QWebFrame>
#include <QWebView>
#include <QListWidget>
#include <QStyleFactory>
#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<WindowScriptingInterface>().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);
}

View file

@ -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 <QScriptContext>
#include <QScriptEngine>
#include <QWebView>
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

View file

@ -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();
}

View file

@ -16,9 +16,6 @@
#include <QtCore/QString>
#include <QtScript/QScriptValue>
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);

View file

@ -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 <qnetworkrequest.h>
#include "Application.h"
#include <AddressManager.h>
#include <OAuthNetworkAccessManager.h>
#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;
}

View file

@ -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 <qwebpage.h>
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

View file

@ -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()));

View file

@ -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;

View file

@ -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);

View file

@ -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<glm::mat4>& 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);

View file

@ -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<glm::mat4> _sensorToWorldMatrixCache { glm::mat4() };
ThreadSafeValueCache<glm::mat4> _controllerLeftHandMatrixCache { glm::mat4() };
ThreadSafeValueCache<glm::mat4> _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

View file

@ -325,7 +325,6 @@ QString UserInputMapper::getActionName(Action action) const {
return QString();
}
QVector<QString> UserInputMapper::getActionNames() const {
Locker locker(_lock);
QVector<QString> result;
@ -335,6 +334,12 @@ QVector<QString> 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;

View file

@ -81,7 +81,7 @@ namespace controller {
QVector<Action> 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<QString> getActionNames() const;
Input inputFromAction(Action action) const { return getActionInputs()[toInt(action)].first; }

View file

@ -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;

View file

@ -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);
}
});
});
}

View file

@ -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";

View file

@ -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
};

View file

@ -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;

View file

@ -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();

View file

@ -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());

View file

@ -836,6 +836,7 @@ void AddressManager::ifLocalSandboxRunningElse(std::function<void()> 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);

View file

@ -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) {

View file

@ -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());

View file

@ -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<PacketVersion>(AvatarMixerPacketVersion::SensorToWorldMat);
return static_cast<PacketVersion>(AvatarMixerPacketVersion::HandControllerJoints);
case PacketType::ICEServerHeartbeat:
return 18; // ICE Server Heartbeat signing
case PacketType::AssetGetInfo:

View file

@ -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 {

View file

@ -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 <glm/gtc/quaternion.hpp>
#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<ObjectMotionState*>(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());
});
}

View file

@ -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

View file

@ -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

View file

@ -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()));

View file

@ -45,6 +45,7 @@ XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) :
_timer(this),
_numRedirects(0) {
_request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
_timer.setSingleShot(true);
}

View file

@ -18,6 +18,7 @@
#include <QtGui/QVector3D>
#include <QtGui/QQuaternion>
#include <glm/gtc/quaternion.hpp>
#include <QAbstractSocket>
#include "RegisteredMetaTypes.h"
@ -32,6 +33,7 @@ int xColorMetaTypeId = qRegisterMetaType<xColor>();
int pickRayMetaTypeId = qRegisterMetaType<PickRay>();
int collisionMetaTypeId = qRegisterMetaType<Collision>();
int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>();
int socketErrorMetaTypeId = qRegisterMetaType<QAbstractSocket::SocketError>();
void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue);

View file

@ -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<QObject*>(returnedValue); // returnedValue.value<QObject*>();

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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);

View file

@ -73,8 +73,7 @@ var toolBar = (function() {
width: toolWidth,
height: toolHeight,
alpha: 0.9,
visible: true,
showButtonDown: true
visible: true
});
toolBar.showTool(browseDirectoryButton, true);

View file

@ -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;

View file

@ -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);
}