Merge remote-tracking branch 'upstream/master' into vive-ui

Conflicts:
	scripts/system/controllers/handControllerGrab.js
This commit is contained in:
Brad Davis 2016-06-10 10:43:09 -07:00
commit 472a3540d7
66 changed files with 994 additions and 398 deletions

View file

@ -1,5 +1,5 @@
{
"version": 1.2,
"version": 1.3,
"settings": [
{
"name": "metaverse",
@ -71,6 +71,76 @@
}
]
},
{
"name": "descriptors",
"label": "Description",
"help": "This data will be queryable from your server. It may be collected by High Fidelity and used to share your domain with others.",
"settings": [
{
"name": "description",
"label": "Description",
"help": "A description of your domain (256 character limit)."
},
{
"name": "maturity",
"label": "Maturity",
"help": "A maturity rating, available as a guideline for content on your domain.",
"default": "unrated",
"type": "select",
"options": [
{
"value": "unrated",
"label": "Unrated"
},
{
"value": "everyone",
"label": "Everyone"
},
{
"value": "teen",
"label": "Teen (13+)"
},
{
"value": "mature",
"label": "Mature (17+)"
},
{
"value": "adult",
"label": "Adult (18+)"
}
]
},
{
"name": "hosts",
"label": "Hosts",
"type": "table",
"help": "Usernames of hosts who can reliably show your domain to new visitors.",
"numbered": false,
"columns": [
{
"name": "host",
"label": "Username",
"can_set": true
}
]
},
{
"name": "tags",
"label": "Tags",
"type": "table",
"help": "Common categories under which your domain falls.",
"numbered": false,
"columns": [
{
"name": "tag",
"label": "Tag",
"can_set": true
}
]
}
]
},
{
"name": "security",
"label": "Security",

View file

@ -0,0 +1,132 @@
//
// DomainMetadata.cpp
// domain-server/src
//
// Created by Zach Pomerantz on 5/25/2016.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#include "DomainMetadata.h"
#include <HifiConfigVariantMap.h>
#include <DependencyManager.h>
#include <LimitedNodeList.h>
#include "DomainServerNodeData.h"
const QString DomainMetadata::USERS = "users";
const QString DomainMetadata::USERS_NUM_TOTAL = "num_users";
const QString DomainMetadata::USERS_NUM_ANON = "num_anon_users";
const QString DomainMetadata::USERS_HOSTNAMES = "user_hostnames";
// users metadata will appear as (JSON):
// { "num_users": Number,
// "num_anon_users": Number,
// "user_hostnames": { <HOSTNAME>: Number }
// }
const QString DomainMetadata::DESCRIPTORS = "descriptors";
const QString DomainMetadata::DESCRIPTORS_DESCRIPTION = "description";
const QString DomainMetadata::DESCRIPTORS_CAPACITY = "capacity"; // parsed from security
const QString DomainMetadata::DESCRIPTORS_RESTRICTION = "restriction"; // parsed from ACL
const QString DomainMetadata::DESCRIPTORS_MATURITY = "maturity";
const QString DomainMetadata::DESCRIPTORS_HOSTS = "hosts";
const QString DomainMetadata::DESCRIPTORS_TAGS = "tags";
// descriptors metadata will appear as (JSON):
// { "capacity": Number,
// TODO: "hours": String, // UTF-8 representation of the week, split into 15" segments
// "restriction": String, // enum of either open, hifi, or acl
// "maturity": String, // enum corresponding to ESRB ratings
// "hosts": [ String ], // capped list of usernames
// "description": String, // capped description
// TODO: "img": {
// "src": String,
// "type": String,
// "size": Number,
// "updated_at": Number,
// },
// "tags": [ String ], // capped list of tags
// }
// metadata will appear as (JSON):
// { users: <USERS>, descriptors: <DESCRIPTORS> }
//
// it is meant to be sent to and consumed by an external API
DomainMetadata::DomainMetadata() {
_metadata[USERS] = {};
_metadata[DESCRIPTORS] = {};
}
void DomainMetadata::setDescriptors(QVariantMap& settings) {
const QString CAPACITY = "security.maximum_user_capacity";
const QVariant* capacityVariant = valueForKeyPath(settings, CAPACITY);
unsigned int capacity = capacityVariant ? capacityVariant->toUInt() : 0;
// TODO: Keep parity with ACL development.
const QString RESTRICTION = "security.restricted_access";
const QString RESTRICTION_OPEN = "open";
// const QString RESTRICTION_HIFI = "hifi";
const QString RESTRICTION_ACL = "acl";
const QVariant* isRestrictedVariant = valueForKeyPath(settings, RESTRICTION);
bool isRestricted = isRestrictedVariant ? isRestrictedVariant->toBool() : false;
QString restriction = isRestricted ? RESTRICTION_ACL : RESTRICTION_OPEN;
QVariantMap descriptors = settings[DESCRIPTORS].toMap();
descriptors[DESCRIPTORS_CAPACITY] = capacity;
descriptors[DESCRIPTORS_RESTRICTION] = restriction;
_metadata[DESCRIPTORS] = descriptors;
#if DEV_BUILD || PR_BUILD
qDebug() << "Domain metadata descriptors set:" << descriptors;
#endif
}
void DomainMetadata::updateUsers() {
static const QString DEFAULT_HOSTNAME = "*";
auto nodeList = DependencyManager::get<LimitedNodeList>();
int numConnected = 0;
int numConnectedAnonymously = 0;
QVariantMap userHostnames;
// figure out the breakdown of currently connected interface clients
nodeList->eachNode([&numConnected, &numConnectedAnonymously, &userHostnames](const SharedNodePointer& node) {
auto linkedData = node->getLinkedData();
if (linkedData) {
auto nodeData = static_cast<DomainServerNodeData*>(linkedData);
if (!nodeData->wasAssigned()) {
++numConnected;
if (nodeData->getUsername().isEmpty()) {
++numConnectedAnonymously;
}
// increment the count for this hostname (or the default if we don't have one)
auto placeName = nodeData->getPlaceName();
auto hostname = placeName.isEmpty() ? DEFAULT_HOSTNAME : placeName;
userHostnames[hostname] = userHostnames[hostname].toInt() + 1;
}
}
});
QVariantMap users = {
{ USERS_NUM_TOTAL, numConnected },
{ USERS_NUM_ANON, numConnectedAnonymously },
{ USERS_HOSTNAMES, userHostnames }};
_metadata[USERS] = users;
#if DEV_BUILD || PR_BUILD
qDebug() << "Domain metadata users updated:" << users;
#endif
}
void DomainMetadata::usersChanged() {
++_tic;
#if DEV_BUILD || PR_BUILD
qDebug() << "Domain metadata users change detected";
#endif
}

View file

@ -0,0 +1,65 @@
//
// DomainMetadata.h
// domain-server/src
//
// Created by Zach Pomerantz on 5/25/2016.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#ifndef hifi_DomainMetadata_h
#define hifi_DomainMetadata_h
#include <stdint.h>
#include <QVariantMap>
#include <QJsonObject>
class DomainMetadata : public QObject {
Q_OBJECT
static const QString USERS;
static const QString USERS_NUM_TOTAL;
static const QString USERS_NUM_ANON;
static const QString USERS_HOSTNAMES;
static const QString DESCRIPTORS;
static const QString DESCRIPTORS_DESCRIPTION;
static const QString DESCRIPTORS_CAPACITY;
static const QString DESCRIPTORS_HOURS;
static const QString DESCRIPTORS_RESTRICTION;
static const QString DESCRIPTORS_MATURITY;
static const QString DESCRIPTORS_HOSTS;
static const QString DESCRIPTORS_TAGS;
static const QString DESCRIPTORS_IMG;
static const QString DESCRIPTORS_IMG_SRC;
static const QString DESCRIPTORS_IMG_TYPE;
static const QString DESCRIPTORS_IMG_SIZE;
static const QString DESCRIPTORS_IMG_UPDATED_AT;
public:
DomainMetadata();
// Returns the last set metadata
// If connected users have changed, metadata may need to be updated
// this should be checked by storing tic = getTic() between calls
// and testing it for equality before the next get (tic == getTic())
QJsonObject get() { return QJsonObject::fromVariantMap(_metadata); }
QJsonObject getUsers() { return QJsonObject::fromVariantMap(_metadata[USERS].toMap()); }
QJsonObject getDescriptors() { return QJsonObject::fromVariantMap(_metadata[DESCRIPTORS].toMap()); }
uint32_t getTic() { return _tic; }
void setDescriptors(QVariantMap& settings);
void updateUsers();
public slots:
void usersChanged();
protected:
QVariantMap _metadata;
uint32_t _tic{ 0 };
};
#endif // hifi_DomainMetadata_h

View file

@ -94,6 +94,10 @@ DomainServer::DomainServer(int argc, char* argv[]) :
qRegisterMetaType<DomainServerWebSessionData>("DomainServerWebSessionData");
qRegisterMetaTypeStreamOperators<DomainServerWebSessionData>("DomainServerWebSessionData");
// update the metadata when a user (dis)connects
connect(this, &DomainServer::userConnected, &_metadata, &DomainMetadata::usersChanged);
connect(this, &DomainServer::userDisconnected, &_metadata, &DomainMetadata::usersChanged);
// make sure we hear about newly connected nodes from our gatekeeper
connect(&_gatekeeper, &DomainGatekeeper::connectedNode, this, &DomainServer::handleConnectedNode);
@ -112,6 +116,9 @@ DomainServer::DomainServer(int argc, char* argv[]) :
optionallyGetTemporaryName(args);
}
// update the metadata with current descriptors
_metadata.setDescriptors(_settingsManager.getSettingsMap());
}
DomainServer::~DomainServer() {
@ -767,12 +774,16 @@ QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) {
}
void DomainServer::handleConnectedNode(SharedNodePointer newNode) {
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(newNode->getLinkedData());
// reply back to the user with a PacketType::DomainList
sendDomainListToNode(newNode, nodeData->getSendingSockAddr());
// if this node is a user (unassigned Agent), signal
if (newNode->getType() == NodeType::Agent && !nodeData->wasAssigned()) {
emit userConnected();
}
// send out this node to our other connected nodes
broadcastNewNode(newNode);
}
@ -1067,62 +1078,39 @@ void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr)
sendHeartbeatToMetaverse(newPublicSockAddr.getAddress().toString());
}
void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) {
const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
auto nodeList = DependencyManager::get<LimitedNodeList>();
const QUuid& domainID = nodeList->getSessionUUID();
// setup the domain object to send to the data server
const QString PUBLIC_NETWORK_ADDRESS_KEY = "network_address";
const QString AUTOMATIC_NETWORKING_KEY = "automatic_networking";
// Setup the domain object to send to the data server
QJsonObject domainObject;
if (!networkAddress.isEmpty()) {
static const QString PUBLIC_NETWORK_ADDRESS_KEY = "network_address";
domainObject[PUBLIC_NETWORK_ADDRESS_KEY] = networkAddress;
}
static const QString AUTOMATIC_NETWORKING_KEY = "automatic_networking";
domainObject[AUTOMATIC_NETWORKING_KEY] = _automaticNetworkingSetting;
// add a flag to indicate if this domain uses restricted access - for now that will exclude it from listings
const QString RESTRICTED_ACCESS_FLAG = "restricted";
// Add a flag to indicate if this domain uses restricted access -
// for now that will exclude it from listings
static const QString RESTRICTED_ACCESS_FLAG = "restricted";
domainObject[RESTRICTED_ACCESS_FLAG] =
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
// figure out the breakdown of currently connected interface clients
int numConnectedUnassigned = 0;
QJsonObject userHostnames;
static const QString DEFAULT_HOSTNAME = "*";
nodeList->eachNode([&numConnectedUnassigned, &userHostnames](const SharedNodePointer& node) {
if (node->getLinkedData()) {
auto nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
if (!nodeData->wasAssigned()) {
++numConnectedUnassigned;
// increment the count for this hostname (or the default if we don't have one)
auto hostname = nodeData->getPlaceName().isEmpty() ? DEFAULT_HOSTNAME : nodeData->getPlaceName();
userHostnames[hostname] = userHostnames[hostname].toInt() + 1;
}
}
});
// Add the metadata to the heartbeat
static const QString DOMAIN_HEARTBEAT_KEY = "heartbeat";
static const QString HEARTBEAT_NUM_USERS_KEY = "num_users";
static const QString HEARTBEAT_USER_HOSTNAMES_KEY = "user_hostnames";
auto tic = _metadata.getTic();
if (_metadataTic != tic) {
_metadataTic = tic;
_metadata.updateUsers();
}
domainObject[DOMAIN_HEARTBEAT_KEY] = _metadata.getUsers();
QJsonObject heartbeatObject;
heartbeatObject[HEARTBEAT_NUM_USERS_KEY] = numConnectedUnassigned;
heartbeatObject[HEARTBEAT_USER_HOSTNAMES_KEY] = userHostnames;
domainObject[DOMAIN_HEARTBEAT_KEY] = heartbeatObject;
QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson()));
QString domainUpdateJSON = QString("{\"domain\":%1}").arg(QString(QJsonDocument(domainObject).toJson(QJsonDocument::Compact)));
static const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
DependencyManager::get<AccountManager>()->sendRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)),
AccountManagerAuth::Required,
QNetworkAccessManager::PutOperation,
@ -1918,11 +1906,10 @@ void DomainServer::nodeAdded(SharedNodePointer node) {
}
void DomainServer::nodeKilled(SharedNodePointer node) {
// if this peer connected via ICE then remove them from our ICE peers hash
_gatekeeper.removeICEPeer(node->getUUID());
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
if (nodeData) {
// if this node's UUID matches a static assignment we need to throw it back in the assignment queue
@ -1934,15 +1921,22 @@ void DomainServer::nodeKilled(SharedNodePointer node) {
}
}
// If this node was an Agent ask DomainServerNodeData to potentially remove the interpolation we stored
nodeData->removeOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY,
uuidStringWithoutCurlyBraces(node->getUUID()));
// cleanup the connection secrets that we set up for this node (on the other nodes)
foreach (const QUuid& otherNodeSessionUUID, nodeData->getSessionSecretHash().keys()) {
SharedNodePointer otherNode = DependencyManager::get<LimitedNodeList>()->nodeWithUUID(otherNodeSessionUUID);
if (otherNode) {
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())->getSessionSecretHash().remove(node->getUUID());
static_cast<DomainServerNodeData*>(otherNode->getLinkedData())->getSessionSecretHash().remove(node->getUUID());
}
}
if (node->getType() == NodeType::Agent) {
// if this node was an Agent ask DomainServerNodeData to remove the interpolation we potentially stored
nodeData->removeOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY,
uuidStringWithoutCurlyBraces(node->getUUID()));
// if this node is a user (unassigned Agent), signal
if (!nodeData->wasAssigned()) {
emit userDisconnected();
}
}
}

View file

@ -26,6 +26,7 @@
#include <LimitedNodeList.h>
#include "DomainGatekeeper.h"
#include "DomainMetadata.h"
#include "DomainServerSettingsManager.h"
#include "DomainServerWebSessionData.h"
#include "WalletTransaction.h"
@ -91,6 +92,8 @@ private slots:
signals:
void iceServerChanged();
void userConnected();
void userDisconnected();
private:
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
@ -167,6 +170,9 @@ private:
DomainServerSettingsManager _settingsManager;
DomainMetadata _metadata;
uint32_t _metadataTic{ 0 };
HifiSockAddr _iceServerSocket;
std::unique_ptr<NLPacket> _iceServerHeartbeatPacket;

View file

@ -1,22 +1,40 @@
{
"name": "Oculus Touch to Standard",
"channels": [
{ "from": "OculusTouch.LY", "filters": "invert", "to": "Standard.LY" },
{ "from": "OculusTouch.LX", "to": "Standard.LX" },
{ "from": "OculusTouch.A", "to": "Standard.A" },
{ "from": "OculusTouch.B", "to": "Standard.B" },
{ "from": "OculusTouch.X", "to": "Standard.X" },
{ "from": "OculusTouch.Y", "to": "Standard.Y" },
{ "from": "OculusTouch.LY", "filters": "invert", "to": "Standard.LY" },
{ "from": "OculusTouch.LX", "to": "Standard.LX" },
{ "from": "OculusTouch.LT", "to": "Standard.LT" },
{ "from": "OculusTouch.LS", "to": "Standard.LS" },
{ "from": "OculusTouch.LeftGrip", "to": "Standard.LeftGrip" },
{ "from": "OculusTouch.LeftHand", "to": "Standard.LeftHand" },
{ "from": "OculusTouch.RY", "filters": "invert", "to": "Standard.RY" },
{ "from": "OculusTouch.RX", "to": "Standard.RX" },
{ "from": "OculusTouch.RY", "filters": "invert", "to": "Standard.RY" },
{ "from": "OculusTouch.RX", "to": "Standard.RX" },
{ "from": "OculusTouch.RT", "to": "Standard.RT" },
{ "from": "OculusTouch.RB", "to": "Standard.RB" },
{ "from": "OculusTouch.RS", "to": "Standard.RS" },
{ "from": "OculusTouch.RightGrip", "to": "Standard.RightGrip" },
{ "from": "OculusTouch.RightHand", "to": "Standard.RightHand" },
{ "from": "OculusTouch.LeftApplicationMenu", "to": "Standard.Back" },
{ "from": "OculusTouch.RightApplicationMenu", "to": "Standard.Start" },
{ "from": "OculusTouch.LeftHand", "to": "Standard.LeftHand" },
{ "from": "OculusTouch.RightHand", "to": "Standard.RightHand" }
{ "from": "OculusTouch.LeftPrimaryThumbTouch", "to": "Standard.LeftPrimaryThumbTouch" },
{ "from": "OculusTouch.LeftSecondaryThumbTouch", "to": "Standard.LeftSecondaryThumbTouch" },
{ "from": "OculusTouch.RightPrimaryThumbTouch", "to": "Standard.RightPrimaryThumbTouch" },
{ "from": "OculusTouch.RightSecondaryThumbTouch", "to": "Standard.RightSecondaryThumbTouch" },
{ "from": "OculusTouch.LeftPrimaryIndexTouch", "to": "Standard.LeftPrimaryIndexTouch" },
{ "from": "OculusTouch.RightPrimaryIndexTouch", "to": "Standard.RightPrimaryIndexTouch" },
{ "from": "OculusTouch.LSTouch", "to": "Standard.LSTouch" },
{ "from": "OculusTouch.RSTouch", "to": "Standard.RSTouch" },
{ "from": "OculusTouch.LeftThumbUp", "to": "Standard.LeftThumbUp" },
{ "from": "OculusTouch.RightThumbUp", "to": "Standard.RightThumbUp" },
{ "from": "OculusTouch.LeftIndexPoint", "to": "Standard.LeftIndexPoint" },
{ "from": "OculusTouch.RightIndexPoint", "to": "Standard.RightIndexPoint" }
]
}

View file

@ -5,7 +5,7 @@
{ "from": "Vive.LX", "when": "Vive.LSX", "to": "Standard.LX" },
{ "from": "Vive.LT", "to": "Standard.LT" },
{ "from": "Vive.LeftGrip", "to": "Standard.LB" },
{ "from": "Vive.LeftGrip", "to": "Standard.LeftGrip" },
{ "from": "Vive.LS", "to": "Standard.LS" },
{ "from": "Vive.LSTouch", "to": "Standard.LSTouch" },
@ -13,7 +13,7 @@
{ "from": "Vive.RX", "when": "Vive.RSX", "to": "Standard.RX" },
{ "from": "Vive.RT", "to": "Standard.RT" },
{ "from": "Vive.RightGrip", "to": "Standard.RB" },
{ "from": "Vive.RightGrip", "to": "Standard.RightGrip" },
{ "from": "Vive.RS", "to": "Standard.RS" },
{ "from": "Vive.RSTouch", "to": "Standard.RSTouch" },

View file

@ -1,14 +1,14 @@
{
"name": "XBox to Standard",
"channels": [
{ "from": "GamePad.LY", "to": "Standard.LY" },
{ "from": "GamePad.LX", "to": "Standard.LX" },
{ "from": "GamePad.LY", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Standard.LY" },
{ "from": "GamePad.LX", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Standard.LX" },
{ "from": "GamePad.LT", "to": "Standard.LT" },
{ "from": "GamePad.LB", "to": "Standard.LB" },
{ "from": "GamePad.LS", "to": "Standard.LS" },
{ "from": "GamePad.RY", "to": "Standard.RY" },
{ "from": "GamePad.RX", "to": "Standard.RX" },
{ "from": "GamePad.RY", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Standard.RY" },
{ "from": "GamePad.RX", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Standard.RX" },
{ "from": "GamePad.RT", "to": "Standard.RT" },
{ "from": "GamePad.RB", "to": "Standard.RB" },
{ "from": "GamePad.RS", "to": "Standard.RS" },

View file

@ -264,6 +264,7 @@ Window {
HifiControls.Button {
text: "Load Defaults"
color: hifi.buttons.black
height: 26
onClicked: loadDefaults()
}
}

View file

@ -10,16 +10,20 @@
//
#ifdef HAS_BUGSPLAT
#include "Application.h"
#include "CrashReporter.h"
#ifdef _WIN32
#include <new.h>
#include <Windows.h>
#include <DbgHelp.h>
#include <csignal>
#include <QDebug>
#pragma comment(lib, "Dbghelp.lib")
// SetUnhandledExceptionFilter can be overridden by the CRT at the point that an error occurs. More information
// can be found here: http://www.codeproject.com/Articles/154686/SetUnhandledExceptionFilter-and-the-C-C-Runtime-Li
// A fairly common approach is to patch the SetUnhandledExceptionFilter so that it cannot be overridden and so
@ -77,13 +81,37 @@ BOOL redirectLibraryFunctionToFunction(char* library, char* function, void* fn)
return bRet;
}
void printStackTrace(ULONG framesToSkip = 1) {
HANDLE process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
void* stack[100];
uint16_t frames = CaptureStackBackTrace(framesToSkip, 100, stack, NULL);
SYMBOL_INFO* symbol = (SYMBOL_INFO *)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
for (uint16_t i = 0; i < frames; ++i) {
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
qWarning() << QString("%1: %2 - 0x%0X").arg(QString::number(frames - i - 1), QString(symbol->Name), QString::number(symbol->Address, 16));
}
free(symbol);
// Try to force the log to sync to the filesystem
auto app = qApp;
if (app && app->getLogger()) {
app->getLogger()->sync();
}
}
void handleSignal(int signal) {
// Throw so BugSplat can handle
throw(signal);
}
void handlePureVirtualCall() {
void __cdecl handlePureVirtualCall() {
qWarning() << "Pure virtual function call detected";
printStackTrace(2);
// Throw so BugSplat can handle
throw("ERROR: Pure virtual call");
}
@ -107,6 +135,8 @@ _purecall_handler __cdecl noop_set_purecall_handler(_purecall_handler pNew) {
return nullptr;
}
#ifdef HAS_BUGSPLAT
static const DWORD BUG_SPLAT_FLAGS = MDSF_PREVENTHIJACKING | MDSF_USEGUARDMEMORY;
CrashReporter::CrashReporter(QString bugSplatDatabase, QString bugSplatApplicationName, QString version)
@ -133,3 +163,4 @@ CrashReporter::CrashReporter(QString bugSplatDatabase, QString bugSplatApplicati
}
}
#endif
#endif

View file

@ -115,3 +115,7 @@ QString FileLogger::getLogData() {
}
return result;
}
void FileLogger::sync() {
_persistThreadInstance->waitIdle();
}

View file

@ -28,6 +28,7 @@ public:
virtual void addMessage(const QString&) override;
virtual QString getLogData() override;
virtual void locateLog() override;
void sync();
signals:
void rollingLogFile(QString newFilename);

View file

@ -84,7 +84,6 @@ Avatar::Avatar(RigPointer rig) :
_acceleration(0.0f),
_lastAngularVelocity(0.0f),
_lastOrientation(),
_leanScale(0.5f),
_worldUpDirection(DEFAULT_UP_DIRECTION),
_moving(false),
_initialized(false),

View file

@ -210,7 +210,6 @@ protected:
glm::vec3 _angularAcceleration;
glm::quat _lastOrientation;
float _leanScale;
glm::vec3 _worldUpDirection;
float _stringLength;
bool _moving; ///< set when position is changing

View file

@ -54,8 +54,6 @@ Head::Head(Avatar* owningAvatar) :
_deltaPitch(0.0f),
_deltaYaw(0.0f),
_deltaRoll(0.0f),
_deltaLeanSideways(0.0f),
_deltaLeanForward(0.0f),
_isCameraMoving(false),
_isLookingAtMe(false),
_lookingAtMeStarted(0),
@ -70,7 +68,6 @@ void Head::init() {
void Head::reset() {
_baseYaw = _basePitch = _baseRoll = 0.0f;
_leanForward = _leanSideways = 0.0f;
}
void Head::simulate(float deltaTime, bool isMine, bool billboard) {
@ -118,13 +115,6 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
auto eyeTracker = DependencyManager::get<EyeTracker>();
_isEyeTrackerConnected = eyeTracker->isTracking();
}
// Twist the upper body to follow the rotation of the head, but only do this with my avatar,
// since everyone else will see the full joint rotations for other people.
const float BODY_FOLLOW_HEAD_YAW_RATE = 0.1f;
const float BODY_FOLLOW_HEAD_FACTOR = 0.66f;
float currentTwist = getTorsoTwist();
setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE);
}
if (!(_isFaceTrackerConnected || billboard)) {
@ -301,17 +291,13 @@ void Head::applyEyelidOffset(glm::quat headOrientation) {
}
}
void Head::relaxLean(float deltaTime) {
void Head::relax(float deltaTime) {
// restore rotation, lean to neutral positions
const float LEAN_RELAXATION_PERIOD = 0.25f; // seconds
float relaxationFactor = 1.0f - glm::min(deltaTime / LEAN_RELAXATION_PERIOD, 1.0f);
_deltaYaw *= relaxationFactor;
_deltaPitch *= relaxationFactor;
_deltaRoll *= relaxationFactor;
_leanSideways *= relaxationFactor;
_leanForward *= relaxationFactor;
_deltaLeanSideways *= relaxationFactor;
_deltaLeanForward *= relaxationFactor;
}
void Head::setScale (float scale) {
@ -419,8 +405,3 @@ float Head::getFinalPitch() const {
float Head::getFinalRoll() const {
return glm::clamp(_baseRoll + _deltaRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL);
}
void Head::addLeanDeltas(float sideways, float forward) {
_deltaLeanSideways += sideways;
_deltaLeanForward += forward;
}

View file

@ -59,8 +59,6 @@ public:
glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
glm::vec3 getUpDirection() const { return getOrientation() * IDENTITY_UP; }
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
float getFinalLeanSideways() const { return _leanSideways + _deltaLeanSideways; }
float getFinalLeanForward() const { return _leanForward + _deltaLeanForward; }
glm::quat getEyeRotation(const glm::vec3& eyePosition) const;
@ -91,8 +89,7 @@ public:
virtual float getFinalYaw() const;
virtual float getFinalRoll() const;
void relaxLean(float deltaTime);
void addLeanDeltas(float sideways, float forward);
void relax(float deltaTime);
float getTimeWithoutTalking() const { return _timeWithoutTalking; }
@ -132,10 +129,6 @@ private:
float _deltaYaw;
float _deltaRoll;
// delta lean angles for lean perturbations (driven by collisions)
float _deltaLeanSideways;
float _deltaLeanForward;
bool _isCameraMoving;
bool _isLookingAtMe;
quint64 _lookingAtMeStarted;

View file

@ -190,9 +190,6 @@ MyAvatar::MyAvatar(RigPointer rig) :
if (!headData->getBlendshapeCoefficients().isEmpty()) {
_headData->setBlendshapeCoefficients(headData->getBlendshapeCoefficients());
}
// head lean
_headData->setLeanForward(headData->getLeanForward());
_headData->setLeanSideways(headData->getLeanSideways());
// head orientation
_headData->setLookAtPosition(headData->getLookAtPosition());
}
@ -237,7 +234,7 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "reset", Q_ARG(bool, andRecenter));
QMetaObject::invokeMethod(this, "reset", Q_ARG(bool, andRecenter), Q_ARG(bool, andReload), Q_ARG(bool, andHead));
return;
}
@ -306,7 +303,7 @@ void MyAvatar::update(float deltaTime) {
}
Head* head = getHead();
head->relaxLean(deltaTime);
head->relax(deltaTime);
updateFromTrackers(deltaTime);
// Get audio loudness data from audio input device
@ -574,16 +571,6 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
head->setDeltaYaw(estimatedRotation.y * magnifyFieldOfView);
head->setDeltaRoll(estimatedRotation.z);
}
// Update torso lean distance based on accelerometer data
const float TORSO_LENGTH = 0.5f;
glm::vec3 relativePosition = estimatedPosition - glm::vec3(0.0f, -TORSO_LENGTH, 0.0f);
const float MAX_LEAN = 45.0f;
head->setLeanSideways(glm::clamp(glm::degrees(atanf(relativePosition.x * _leanScale / TORSO_LENGTH)),
-MAX_LEAN, MAX_LEAN));
head->setLeanForward(glm::clamp(glm::degrees(atanf(relativePosition.z * _leanScale / TORSO_LENGTH)),
-MAX_LEAN, MAX_LEAN));
}
glm::vec3 MyAvatar::getLeftHandPosition() const {
@ -692,7 +679,6 @@ void MyAvatar::saveData() {
settings.setValue("headPitch", getHead()->getBasePitch());
settings.setValue("leanScale", _leanScale);
settings.setValue("scale", _targetScale);
settings.setValue("fullAvatarURL",
@ -809,7 +795,6 @@ void MyAvatar::loadData() {
getHead()->setBasePitch(loadSetting(settings, "headPitch", 0.0f));
_leanScale = loadSetting(settings, "leanScale", 0.05f);
_targetScale = loadSetting(settings, "scale", 1.0f);
setScale(glm::vec3(_targetScale));
@ -1271,13 +1256,13 @@ void MyAvatar::prepareForPhysicsSimulation() {
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
glm::vec3 position = getPosition();
glm::quat orientation = getOrientation();
if (_characterController.isEnabled()) {
if (_characterController.isEnabledAndReady()) {
_characterController.getPositionAndOrientation(position, orientation);
}
nextAttitude(position, orientation);
_bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix);
if (_characterController.isEnabled()) {
if (_characterController.isEnabledAndReady()) {
setVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity());
} else {
setVelocity(getVelocity() + _characterController.getFollowVelocity());
@ -1660,7 +1645,7 @@ void MyAvatar::updatePosition(float deltaTime) {
vec3 velocity = getVelocity();
const float MOVING_SPEED_THRESHOLD_SQUARED = 0.0001f; // 0.01 m/s
if (!_characterController.isEnabled()) {
if (!_characterController.isEnabledAndReady()) {
// _characterController is not in physics simulation but it can still compute its target velocity
updateMotors();
_characterController.computeNewVelocity(deltaTime, velocity);
@ -1834,6 +1819,16 @@ void MyAvatar::updateMotionBehaviorFromMenu() {
_motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
}
setCharacterControllerEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController));
}
void MyAvatar::setCharacterControllerEnabled(bool enabled) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setCharacterControllerEnabled", Q_ARG(bool, enabled));
return;
}
bool ghostingAllowed = true;
EntityTreeRenderer* entityTreeRenderer = qApp->getEntities();
if (entityTreeRenderer) {
@ -1842,12 +1837,11 @@ void MyAvatar::updateMotionBehaviorFromMenu() {
ghostingAllowed = zone->getGhostingAllowed();
}
}
bool checked = menu->isOptionChecked(MenuOption::EnableCharacterController);
if (!ghostingAllowed) {
checked = true;
}
_characterController.setEnabled(ghostingAllowed ? enabled : true);
}
_characterController.setEnabled(checked);
bool MyAvatar::getCharacterControllerEnabled() {
return _characterController.isEnabled();
}
void MyAvatar::clearDriveKeys() {
@ -2055,14 +2049,17 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) {
_desiredBodyMatrix = desiredBodyMatrix;
if (!isActive(Rotation) && shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
activate(Rotation);
}
if (!isActive(Horizontal) && shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
activate(Horizontal);
}
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Vertical);
if (myAvatar.getHMDLeanRecenterEnabled()) {
if (!isActive(Rotation) && shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
activate(Rotation);
}
if (!isActive(Horizontal) && shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
activate(Horizontal);
}
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Vertical);
}
}
glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * _desiredBodyMatrix;

View file

@ -69,7 +69,6 @@ class MyAvatar : public Avatar {
Q_PROPERTY(AudioListenerMode audioListenerModeCustom READ getAudioListenerModeCustom)
//TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition)
Q_PROPERTY(glm::vec3 rightHandPosition READ getRightHandPosition)
Q_PROPERTY(glm::vec3 leftHandTipPosition READ getLeftHandTipPosition)
@ -84,6 +83,9 @@ class MyAvatar : public Avatar {
Q_PROPERTY(float energy READ getEnergy WRITE setEnergy)
Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled)
Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled)
public:
explicit MyAvatar(RigPointer rig);
~MyAvatar();
@ -123,9 +125,6 @@ public:
void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); }
void setLeanScale(float scale) { _leanScale = scale; }
float getLeanScale() const { return _leanScale; }
Q_INVOKABLE glm::vec3 getDefaultEyePosition() const;
float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); }
@ -163,6 +162,9 @@ public:
Q_INVOKABLE bool getClearOverlayWhenDriving() const { return _clearOverlayWhenDriving; }
Q_INVOKABLE void setClearOverlayWhenDriving(bool on) { _clearOverlayWhenDriving = on; }
Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; }
Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; }
// get/set avatar data
void saveData();
void loadData();
@ -264,6 +266,9 @@ public:
controller::Pose getLeftHandControllerPoseInAvatarFrame() const;
controller::Pose getRightHandControllerPoseInAvatarFrame() const;
Q_INVOKABLE void setCharacterControllerEnabled(bool enabled);
Q_INVOKABLE bool getCharacterControllerEnabled();
public slots:
void increaseSize();
void decreaseSize();
@ -470,6 +475,8 @@ private:
ThreadSafeValueCache<controller::Pose> _leftHandControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightHandControllerPoseInSensorFrameCache { controller::Pose() };
bool _hmdLeanRecenterEnabled = true;
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
float AUDIO_ENERGY_CONSTANT { 0.000001f };
float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f };

View file

@ -106,10 +106,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
Rig::HeadParameters headParams;
headParams.enableLean = qApp->isHMDMode();
headParams.leanSideways = head->getFinalLeanSideways();
headParams.leanForward = head->getFinalLeanForward();
headParams.torsoTwist = head->getTorsoTwist();
if (qApp->isHMDMode()) {
headParams.isInHMD = true;
@ -131,7 +127,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
headParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame();
}
headParams.leanJointIndex = geometry.leanJointIndex;
headParams.neckJointIndex = geometry.neckJointIndex;
headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f;

View file

@ -129,16 +129,6 @@ void setupPreferences() {
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = [=]()->float { return myAvatar->getLeanScale(); };
auto setter = [=](float value) { myAvatar->setLeanScale(value); };
auto preference = new SpinnerPreference(AVATAR_TUNING, "Lean scale (applies to Faceshift users)", getter, setter);
preference->setMin(0);
preference->setMax(99.9f);
preference->setDecimals(2);
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = [=]()->float { return myAvatar->getUniformScale(); };
auto setter = [=](float value) { myAvatar->setTargetScaleVerbose(value); }; // The hell?

View file

@ -931,11 +931,6 @@ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) {
}
void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) {
if (params.enableLean) {
updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist);
} else {
_animVars.unset("lean");
}
updateNeckJoint(params.neckJointIndex, params);
_animVars.set("isTalking", params.isTalking);
@ -953,15 +948,6 @@ static const glm::vec3 X_AXIS(1.0f, 0.0f, 0.0f);
static const glm::vec3 Y_AXIS(0.0f, 1.0f, 0.0f);
static const glm::vec3 Z_AXIS(0.0f, 0.0f, 1.0f);
void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) {
if (isIndexValid(index)) {
glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, Z_AXIS) *
glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, X_AXIS) *
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, Y_AXIS));
_animVars.set("lean", absRot);
}
}
void Rig::computeHeadNeckAnimVars(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut,
glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) const {

View file

@ -42,15 +42,10 @@ public:
};
struct HeadParameters {
float leanSideways = 0.0f; // degrees
float leanForward = 0.0f; // degrees
float torsoTwist = 0.0f; // degrees
bool enableLean = false;
glm::quat worldHeadOrientation = glm::quat(); // world space (-z forward)
glm::quat rigHeadOrientation = glm::quat(); // rig space (-z forward)
glm::vec3 rigHeadPosition = glm::vec3(); // rig space
bool isInHMD = false;
int leanJointIndex = -1;
int neckJointIndex = -1;
bool isTalking = false;
};
@ -222,7 +217,6 @@ protected:
void applyOverridePoses();
void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut);
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
void updateNeckJoint(int index, const HeadParameters& params);
void computeHeadNeckAnimVars(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut,
glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) const;

View file

@ -10,7 +10,6 @@
//
#include <math.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
@ -119,68 +118,18 @@ static void FIR_1x4_SSE(float* src, float* dst0, float* dst1, float* dst2, float
}
}
//
// Detect AVX/AVX2 support
//
#if defined(_MSC_VER)
#include <intrin.h>
static bool cpuSupportsAVX() {
int info[4];
int mask = (1 << 27) | (1 << 28); // OSXSAVE and AVX
__cpuidex(info, 0x1, 0);
bool result = false;
if ((info[2] & mask) == mask) {
if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
result = true;
}
}
return result;
}
#elif defined(__GNUC__)
#include <cpuid.h>
static bool cpuSupportsAVX() {
unsigned int eax, ebx, ecx, edx;
unsigned int mask = (1 << 27) | (1 << 28); // OSXSAVE and AVX
bool result = false;
if (__get_cpuid(0x1, &eax, &ebx, &ecx, &edx) && ((ecx & mask) == mask)) {
__asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0));
if ((eax & 0x6) == 0x6) {
result = true;
}
}
return result;
}
#else
static bool cpuSupportsAVX() {
return false;
}
#endif
//
// Runtime CPU dispatch
//
typedef void FIR_1x4_t(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
FIR_1x4_t FIR_1x4_AVX; // separate compilation with VEX-encoding enabled
#include "CPUDetect.h"
void FIR_1x4_AVX(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) {
static FIR_1x4_t* f = cpuSupportsAVX() ? FIR_1x4_AVX : FIR_1x4_SSE; // init on first call
(*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch
static auto f = cpuSupportsAVX() ? FIR_1x4_AVX : FIR_1x4_SSE;
(*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch
}
// 4 channel planar to interleaved

View file

@ -32,7 +32,7 @@ public:
// input: mono source
// output: interleaved stereo mix buffer (accumulates into existing output)
// index: HRTF subject index
// azimuth: clockwise panning angle [0, 360] in degrees
// azimuth: clockwise panning angle in radians
// gain: gain factor for distance attenuation
// numFrames: must be HRTF_BLOCK in this version
//

View file

@ -31,9 +31,6 @@ HeadData::HeadData(AvatarData* owningAvatar) :
_baseYaw(0.0f),
_basePitch(0.0f),
_baseRoll(0.0f),
_leanSideways(0.0f),
_leanForward(0.0f),
_torsoTwist(0.0f),
_lookAtPosition(0.0f, 0.0f, 0.0f),
_audioLoudness(0.0f),
_isFaceTrackerConnected(false),
@ -132,12 +129,6 @@ QJsonObject HeadData::toJson() const {
if (getRawOrientation() != quat()) {
headJson[JSON_AVATAR_HEAD_ROTATION] = toJsonValue(getRawOrientation());
}
if (getLeanForward() != 0.0f) {
headJson[JSON_AVATAR_HEAD_LEAN_FORWARD] = getLeanForward();
}
if (getLeanSideways() != 0.0f) {
headJson[JSON_AVATAR_HEAD_LEAN_SIDEWAYS] = getLeanSideways();
}
auto lookat = getLookAtPosition();
if (lookat != vec3()) {
vec3 relativeLookAt = glm::inverse(_owningAvatar->getOrientation()) *
@ -171,12 +162,6 @@ void HeadData::fromJson(const QJsonObject& json) {
if (json.contains(JSON_AVATAR_HEAD_ROTATION)) {
setOrientation(quatFromJsonValue(json[JSON_AVATAR_HEAD_ROTATION]));
}
if (json.contains(JSON_AVATAR_HEAD_LEAN_FORWARD)) {
setLeanForward((float)json[JSON_AVATAR_HEAD_LEAN_FORWARD].toDouble());
}
if (json.contains(JSON_AVATAR_HEAD_LEAN_SIDEWAYS)) {
setLeanSideways((float)json[JSON_AVATAR_HEAD_LEAN_SIDEWAYS].toDouble());
}
if (json.contains(JSON_AVATAR_HEAD_LOOKAT)) {
auto relativeLookAt = vec3FromJsonValue(json[JSON_AVATAR_HEAD_LOOKAT]);

View file

@ -68,17 +68,6 @@ public:
const glm::vec3& getLookAtPosition() const { return _lookAtPosition; }
void setLookAtPosition(const glm::vec3& lookAtPosition) { _lookAtPosition = lookAtPosition; }
float getLeanSideways() const { return _leanSideways; }
float getLeanForward() const { return _leanForward; }
float getTorsoTwist() const { return _torsoTwist; }
virtual float getFinalLeanSideways() const { return _leanSideways; }
virtual float getFinalLeanForward() const { return _leanForward; }
void setLeanSideways(float leanSideways) { _leanSideways = leanSideways; }
void setLeanForward(float leanForward) { _leanForward = leanForward; }
void setTorsoTwist(float torsoTwist) { _torsoTwist = torsoTwist; }
friend class AvatarData;
QJsonObject toJson() const;
@ -89,9 +78,6 @@ protected:
float _baseYaw;
float _basePitch;
float _baseRoll;
float _leanSideways;
float _leanForward;
float _torsoTwist;
glm::vec3 _lookAtPosition;
float _audioLoudness;

View file

@ -31,6 +31,12 @@ namespace controller {
class Endpoint;
using EndpointPointer = std::shared_ptr<Endpoint>;
enum Hand {
LEFT = 0,
RIGHT,
BOTH
};
// NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first.
// e.g. class Example : public InputPlugin, public InputDevice
// instead of class Example : public InputDevice, public InputPlugin
@ -55,6 +61,9 @@ public:
const QString& getName() const { return _name; }
// By default, Input Devices do not support haptics
virtual bool triggerHapticPulse(float strength, float duration, controller::Hand hand) { return false; }
// Update call MUST be called once per simulation loop
// It takes care of updating the action states and deltas
virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData) {};

View file

@ -140,6 +140,24 @@ namespace controller {
return DependencyManager::get<UserInputMapper>()->getActionNames();
}
bool ScriptingInterface::triggerHapticPulse(float strength, float duration, controller::Hand hand) const {
return DependencyManager::get<UserInputMapper>()->triggerHapticPulse(strength, duration, hand);
}
bool ScriptingInterface::triggerShortHapticPulse(float strength, controller::Hand hand) const {
const float SHORT_HAPTIC_DURATION_MS = 250.0f;
return DependencyManager::get<UserInputMapper>()->triggerHapticPulse(strength, SHORT_HAPTIC_DURATION_MS, hand);
}
bool ScriptingInterface::triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, controller::Hand hand) const {
return DependencyManager::get<UserInputMapper>()->triggerHapticPulseOnDevice(device, strength, duration, hand);
}
bool ScriptingInterface::triggerShortHapticPulseOnDevice(unsigned int device, float strength, controller::Hand hand) const {
const float SHORT_HAPTIC_DURATION_MS = 250.0f;
return DependencyManager::get<UserInputMapper>()->triggerHapticPulseOnDevice(device, strength, SHORT_HAPTIC_DURATION_MS, hand);
}
void ScriptingInterface::updateMaps() {
QVariantMap newHardware;
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();

View file

@ -84,6 +84,11 @@ namespace controller {
Q_INVOKABLE Pose getPoseValue(const int& source) const;
Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const;
Q_INVOKABLE bool triggerHapticPulse(float strength, float duration, controller::Hand hand = BOTH) const;
Q_INVOKABLE bool triggerShortHapticPulse(float strength, controller::Hand hand = BOTH) const;
Q_INVOKABLE bool triggerHapticPulseOnDevice(unsigned int device, float strength, float duration, controller::Hand hand = BOTH) const;
Q_INVOKABLE bool triggerShortHapticPulseOnDevice(unsigned int device, float strength, controller::Hand hand = BOTH) const;
Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString());
Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true);
Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); }

View file

@ -68,9 +68,7 @@ namespace controller {
RIGHT_SECONDARY_INDEX_TOUCH,
RIGHT_INDEX_POINT,
LEFT_GRIP,
LEFT_GRIP_TOUCH,
RIGHT_GRIP,
RIGHT_GRIP_TOUCH,
NUM_STANDARD_BUTTONS
@ -87,9 +85,9 @@ namespace controller {
// Triggers
LT,
RT,
// Grips (Oculus touch squeeze)
LG,
RG,
// Grips
LEFT_GRIP,
RIGHT_GRIP,
NUM_STANDARD_AXES,
LZ = LT,
RZ = RT

View file

@ -62,14 +62,6 @@ namespace controller {
UserInputMapper::~UserInputMapper() {
}
int UserInputMapper::recordDeviceOfType(const QString& deviceName) {
if (!_deviceCounts.contains(deviceName)) {
_deviceCounts[deviceName] = 0;
}
_deviceCounts[deviceName] += 1;
return _deviceCounts[deviceName];
}
void UserInputMapper::registerDevice(InputDevice::Pointer device) {
Locker locker(_lock);
if (device->_deviceID == Input::INVALID_DEVICE) {
@ -77,8 +69,6 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) {
}
const auto& deviceID = device->_deviceID;
recordDeviceOfType(device->getName());
qCDebug(controllers) << "Registered input device <" << device->getName() << "> deviceID = " << deviceID;
for (const auto& inputMapping : device->getAvailableInputs()) {
@ -134,6 +124,15 @@ void UserInputMapper::removeDevice(int deviceID) {
_mappingsByDevice.erase(mappingsEntry);
}
for (const auto& inputMapping : device->getAvailableInputs()) {
const auto& input = inputMapping.first;
auto endpoint = _endpointsByInput.find(input);
if (endpoint != _endpointsByInput.end()) {
_inputsByEndpoint.erase((*endpoint).second);
_endpointsByInput.erase(input);
}
}
_registeredDevices.erase(proxyEntry);
emit hardwareChanged();
@ -336,10 +335,28 @@ QVector<QString> UserInputMapper::getActionNames() const {
return result;
}
bool UserInputMapper::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
Locker locker(_lock);
bool toReturn = false;
for (auto device : _registeredDevices) {
toReturn = toReturn || device.second->triggerHapticPulse(strength, duration, hand);
}
return toReturn;
}
bool UserInputMapper::triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, controller::Hand hand) {
Locker locker(_lock);
if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
return _registeredDevices[deviceID]->triggerHapticPulse(strength, duration, hand);
}
return false;
}
int actionMetaTypeId = qRegisterMetaType<Action>();
int inputMetaTypeId = qRegisterMetaType<Input>();
int inputPairMetaTypeId = qRegisterMetaType<Input::NamedPair>();
int poseMetaTypeId = qRegisterMetaType<controller::Pose>("Pose");
int handMetaTypeId = qRegisterMetaType<controller::Hand>();
QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input);
void inputFromScriptValue(const QScriptValue& object, Input& input);
@ -347,6 +364,8 @@ QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action);
void actionFromScriptValue(const QScriptValue& object, Action& action);
QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair);
void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair);
QScriptValue handToScriptValue(QScriptEngine* engine, const controller::Hand& hand);
void handFromScriptValue(const QScriptValue& object, controller::Hand& hand);
QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input) {
QScriptValue obj = engine->newObject();
@ -385,12 +404,21 @@ void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inpu
inputPair.second = QString(object.property("inputName").toVariant().toString());
}
QScriptValue handToScriptValue(QScriptEngine* engine, const controller::Hand& hand) {
return engine->newVariant((int)hand);
}
void handFromScriptValue(const QScriptValue& object, controller::Hand& hand) {
hand = Hand(object.toVariant().toInt());
}
void UserInputMapper::registerControllerTypes(QScriptEngine* engine) {
qScriptRegisterSequenceMetaType<QVector<Action> >(engine);
qScriptRegisterSequenceMetaType<Input::NamedVector>(engine);
qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue);
qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue);
qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue);
qScriptRegisterMetaType(engine, handToScriptValue, handFromScriptValue);
qScriptRegisterMetaType(engine, Pose::toScriptValue, Pose::fromScriptValue);
}

View file

@ -89,6 +89,8 @@ namespace controller {
void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; }
void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; }
void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; }
bool triggerHapticPulse(float strength, float duration, controller::Hand hand);
bool triggerHapticPulseOnDevice(uint16 deviceID, float strength, float duration, controller::Hand hand);
static Input makeStandardInput(controller::StandardButtonChannel button);
static Input makeStandardInput(controller::StandardAxisChannel axis);
@ -141,9 +143,6 @@ namespace controller {
std::vector<Pose> _poseStates = std::vector<Pose>(toInt(Action::NUM_ACTIONS));
std::vector<float> _lastStandardStates = std::vector<float>();
int recordDeviceOfType(const QString& deviceName);
QHash<const QString&, int> _deviceCounts;
static float getValue(const EndpointPointer& endpoint, bool peek = false);
static Pose getPose(const EndpointPointer& endpoint, bool peek = false);
@ -199,6 +198,7 @@ Q_DECLARE_METATYPE(QVector<controller::Input::NamedPair>)
Q_DECLARE_METATYPE(controller::Input)
Q_DECLARE_METATYPE(controller::Action)
Q_DECLARE_METATYPE(QVector<controller::Action>)
Q_DECLARE_METATYPE(controller::Hand)
// Cheating.
using UserInputMapper = controller::UserInputMapper;

View file

@ -759,7 +759,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
model.preTransform = glm::translate(rotationOffset) * glm::translate(rotationPivot);
model.preRotation = glm::quat(glm::radians(preRotation));
model.rotation = glm::quat(glm::radians(rotation));
model.postRotation = glm::quat(glm::radians(postRotation));
model.postRotation = glm::inverse(glm::quat(glm::radians(postRotation)));
model.postTransform = glm::translate(-rotationPivot) * glm::translate(scaleOffset) *
glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot);
// NOTE: angles from the FBX file are in degrees
@ -927,6 +927,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
// material.emissiveColor = getVec3(property.properties, index);
// material.emissiveFactor = 1.0;
} else if (property.properties.at(0) == "AmbientFactor") {
material.ambientFactor = property.properties.at(index).value<double>();
// Detected just for BLender AO vs lightmap
} else if (property.properties.at(0) == "Shininess") {
material.shininess = property.properties.at(index).value<double>();
@ -1128,8 +1131,10 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
emissiveTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("tex_emissive_map")) {
emissiveTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("ambient")) {
} else if (type.contains("ambientcolor")) {
ambientTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("ambientfactor")) {
ambientFactorTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("tex_ao_map")) {
occlusionTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type == "lcl rotation") {

View file

@ -151,6 +151,7 @@ public:
float metallic{ 0.0f };
float roughness{ 1.0f };
float emissiveIntensity{ 1.0f };
float ambientFactor{ 1.0f };
QString materialID;
QString name;
@ -437,6 +438,7 @@ public:
QHash<QString, QString> shininessTextures;
QHash<QString, QString> emissiveTextures;
QHash<QString, QString> ambientTextures;
QHash<QString, QString> ambientFactorTextures;
QHash<QString, QString> occlusionTextures;
QHash<QString, FBXMaterial> _fbxMaterials;

View file

@ -186,6 +186,14 @@ void FBXReader::consolidateFBXMaterials() {
FBXTexture occlusionTexture;
QString occlusionTextureID = occlusionTextures.value(material.materialID);
if (occlusionTextureID.isNull()) {
// 2nd chance
// For blender we use the ambient factor texture as AOMap ONLY if the ambientFactor value is > 0.0
if (material.ambientFactor > 0.0f) {
occlusionTextureID = ambientFactorTextures.value(material.materialID);
}
}
if (!occlusionTextureID.isNull()) {
occlusionTexture = getTexture(occlusionTextureID);
detectDifferentUVs |= (occlusionTexture.texcoordSet != 0) || (!emissiveTexture.transform.isIdentity());
@ -198,6 +206,14 @@ void FBXReader::consolidateFBXMaterials() {
FBXTexture ambientTexture;
QString ambientTextureID = ambientTextures.value(material.materialID);
if (ambientTextureID.isNull()) {
// 2nd chance
// For blender we use the ambient factor texture as Lightmap ONLY if the ambientFactor value is set to 0
if (material.ambientFactor == 0.0f) {
ambientTextureID = ambientFactorTextures.value(material.materialID);
}
}
if (_loadLightmaps && !ambientTextureID.isNull()) {
ambientTexture = getTexture(ambientTextureID);
detectDifferentUVs |= (ambientTexture.texcoordSet != 0) || (!ambientTexture.transform.isIdentity());

View file

@ -418,6 +418,8 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, c
auto map = std::make_shared<model::TextureMap>();
map->setTextureSource(texture->_textureSource);
map->setTextureTransform(fbxTexture.transform);
return map;
}
@ -427,6 +429,7 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, Textu
auto map = std::make_shared<model::TextureMap>();
map->setTextureSource(texture->_textureSource);
return map;
}
@ -475,6 +478,7 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur
if (!material.occlusionTexture.filename.isEmpty()) {
auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, NetworkTexture::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP);
map->setTextureTransform(material.occlusionTexture.transform);
setTextureMap(MapChannel::OCCLUSION_MAP, map);
}

View file

@ -122,6 +122,10 @@ void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textur
_texMapArrayBuffer.edit<TexMapArraySchema>()._texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
}
if (channel == MaterialKey::OCCLUSION_MAP) {
_texMapArrayBuffer.edit<TexMapArraySchema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
}
if (channel == MaterialKey::LIGHTMAP_MAP) {
// update the texcoord1 with lightmap
_texMapArrayBuffer.edit<TexMapArraySchema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());

View file

@ -14,6 +14,7 @@
#include <assert.h>
#include <stdint.h>
#include <atomic>
#include <btBulletDynamicsCommon.h>
#include <BulletDynamics/Character/btCharacterControllerInterface.h>
@ -105,8 +106,9 @@ public:
void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale);
bool isEnabled() const { return _enabled; } // thread-safe
void setEnabled(bool enabled);
bool isEnabled() const { return _enabled && _dynamicsWorld; }
bool isEnabledAndReady() const { return _enabled && _dynamicsWorld; }
bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
@ -167,7 +169,7 @@ protected:
btQuaternion _followAngularDisplacement;
btVector3 _linearAcceleration;
bool _enabled;
std::atomic_bool _enabled;
State _state;
bool _isPushingUp;

View file

@ -83,7 +83,7 @@ static const std::string DEFAULT_NORMAL_SHADER {
static const std::string DEFAULT_OCCLUSION_SHADER{
"vec4 getFragmentColor() {"
" DeferredFragment frag = unpackDeferredFragmentNoPosition(uv);"
" return vec4(vec3(frag.obscurance), 1.0);"
" return vec4(vec3(pow(frag.obscurance, 1.0 / 2.2)), 1.0);"
" }"
};

View file

@ -90,7 +90,7 @@ float fetchOcclusionMap(vec2 uv) {
<@endfunc@>
<@func fetchMaterialTextures(matKey, texcoord0, albedo, roughness, normal, metallic, emissive, occlusion)@>
<@func fetchMaterialTexturesCoord0(matKey, texcoord0, albedo, roughness, normal, metallic, emissive)@>
<@if albedo@>
vec4 <$albedo$> = (((<$matKey$> & (ALBEDO_MAP_BIT | OPACITY_MASK_MAP_BIT | OPACITY_TRANSLUCENT_MAP_BIT)) != 0) ? fetchAlbedoMap(<$texcoord0$>) : vec4(1.0));
<@endif@>
@ -106,12 +106,19 @@ float fetchOcclusionMap(vec2 uv) {
<@if emissive@>
vec3 <$emissive$> = (((<$matKey$> & EMISSIVE_MAP_BIT) != 0) ? fetchEmissiveMap(<$texcoord0$>) : vec3(0.0));
<@endif@>
<@endfunc@>
<@func fetchMaterialTexturesCoord1(matKey, texcoord1, occlusion, lightmapVal)@>
<@if occlusion@>
float <$occlusion$> = (((<$matKey$> & OCCLUSION_MAP_BIT) != 0) ? fetchOcclusionMap(<$texcoord0$>) : 1.0);
float <$occlusion$> = (((<$matKey$> & OCCLUSION_MAP_BIT) != 0) ? fetchOcclusionMap(<$texcoord1$>) : 1.0);
<@endif@>
<@if lightmapVal@>
vec3 <$lightmapVal$> = fetchLightmapMap(<$texcoord1$>);
<@endif@>
<@endfunc@>
<@func declareMaterialLightmap()@>
<$declareMaterialTexMapArrayBuffer()$>
@ -123,11 +130,6 @@ vec3 fetchLightmapMap(vec2 uv) {
}
<@endfunc@>
<@func fetchMaterialLightmap(texcoord1, lightmapVal)@>
vec3 <$lightmapVal$> = fetchLightmapMap(<$texcoord1$>);
<@endfunc@>
<@func tangentToViewSpace(fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@>
{
vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz);

View file

@ -22,12 +22,14 @@ in vec4 _position;
in vec3 _normal;
in vec3 _color;
in vec2 _texCoord0;
in vec2 _texCoord1;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$>
float opacity = 1.0;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;

View file

@ -22,6 +22,7 @@
out vec3 _color;
out float _alpha;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec4 _position;
out vec3 _normal;
@ -31,6 +32,7 @@ void main(void) {
TexMapArray texMapArray = getTexMapArray();
<$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$>
<$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$>
// standard transform
TransformCamera cam = getTransformCamera();

View file

@ -29,8 +29,8 @@ in vec3 _color;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedo, roughness)$>
<$fetchMaterialLightmap(_texCoord1, lightmapVal)$>
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$>
packDeferredFragmentLightmap(

View file

@ -30,8 +30,8 @@ in vec3 _color;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedo, roughness, normalTexel)$>
<$fetchMaterialLightmap(_texCoord1, lightmapVal)$>
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness, normalTexel)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$>
vec3 viewNormal;
<$tangentToViewSpace(normalTexel, _normal, _tangent, viewNormal)$>

View file

@ -30,8 +30,8 @@ in vec3 _color;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedo, roughness, normalTexel, metallicTex)$>
<$fetchMaterialLightmap(_texCoord1, lightmapVal)$>
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness, normalTexel, metallicTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$>
vec3 viewNormal;
<$tangentToViewSpace(normalTexel, _normal, _tangent, viewNormal)$>

View file

@ -29,8 +29,8 @@ in vec3 _color;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedo, roughness, _SCRIBE_NULL, metallicTex)$>
<$fetchMaterialLightmap(_texCoord1, lightmapVal)$>
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness, _SCRIBE_NULL, metallicTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$>
packDeferredFragmentLightmap(
normalize(_normal),

View file

@ -21,6 +21,7 @@
in vec4 _position;
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec3 _normal;
in vec3 _tangent;
in vec3 _color;
@ -28,7 +29,8 @@ in vec3 _color;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$>
float opacity = 1.0;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;

View file

@ -22,6 +22,7 @@
out vec4 _position;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _tangent;
out vec3 _color;
@ -34,6 +35,7 @@ void main(void) {
TexMapArray texMapArray = getTexMapArray();
<$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$>
<$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$>
// standard transform
TransformCamera cam = getTransformCamera();

View file

@ -21,6 +21,7 @@
in vec4 _position;
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec3 _normal;
in vec3 _tangent;
in vec3 _color;
@ -28,7 +29,8 @@ in vec3 _color;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex, emissiveTex, occlusionTex)$>
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex, emissiveTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$>
float opacity = 1.0;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;

View file

@ -21,6 +21,7 @@
in vec4 _position;
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec3 _normal;
in vec3 _color;
@ -28,7 +29,8 @@ in vec3 _color;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex, occlusionTex)$>
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$>
float opacity = 1.0;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;

View file

@ -25,6 +25,7 @@
<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$>
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec4 _position;
in vec3 _normal;
in vec3 _color;
@ -35,7 +36,8 @@ out vec4 _fragColor;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$>
float opacity = getMaterialOpacity(mat) * _alpha;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
@ -61,7 +63,7 @@ void main(void) {
_fragColor = vec4(evalGlobalLightingAlphaBlended(
cam._viewInverse,
1.0,
1.0,
occlusionTex,
fragPosition,
fragNormal,
albedo,

View file

@ -26,7 +26,7 @@ out vec4 _fragColor;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL)$>
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$>
float opacity = getMaterialOpacity(mat) * _alpha;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;

View file

@ -16,7 +16,7 @@
<@include model/Material.slh@>
<@include MaterialTextures.slh@>
<$declareMaterialTextures(ALBEDO, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL)$>
<$declareMaterialTextures(ALBEDO)$>
in vec2 _texCoord0;
in vec3 _normal;
@ -27,7 +27,7 @@ void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL)$>
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$>
float opacity = 1.0;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;

View file

@ -24,6 +24,7 @@
out vec4 _position;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _color;
out float _alpha;
@ -40,6 +41,7 @@ void main(void) {
TexMapArray texMapArray = getTexMapArray();
<$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$>
<$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$>
// standard transform
TransformCamera cam = getTransformCamera();

View file

@ -24,6 +24,7 @@
out vec4 _position;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _tangent;
out vec3 _color;
@ -42,6 +43,7 @@ void main(void) {
TexMapArray texMapArray = getTexMapArray();
<$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$>
<$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$>
interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0);
interpolatedTangent = vec4(normalize(interpolatedTangent.xyz), 0.0);

View file

@ -12,9 +12,10 @@
#include <stdint.h>
#include <QQueue>
#include <QMutex>
#include <QWaitCondition>
#include <QtCore/QElapsedTimer>
#include <QtCore/QMutex>
#include <QtCore/QQueue>
#include <QtCore/QWaitCondition>
#include "GenericThread.h"
#include "NumericalConstants.h"
@ -35,6 +36,25 @@ public:
_hasItems.wakeAll();
}
void waitIdle(uint32_t maxWaitMs = UINT32_MAX) {
QElapsedTimer timer;
timer.start();
// FIXME this will work as long as the thread doing the wait
// is the only thread which can add work to the queue.
// It would be better if instead we had a queue empty condition to wait on
// that would ensure that we get woken as soon as we're idle the very
// first time the queue was empty.
while (timer.elapsed() < maxWaitMs) {
lock();
if (!_items.size()) {
unlock();
return;
}
unlock();
}
}
protected:
virtual void queueItemInternal(const T& t) {
_items.push_back(t);
@ -44,6 +64,7 @@ protected:
return MSECS_PER_SECOND;
}
virtual bool process() {
lock();
if (!_items.size()) {

View file

@ -21,9 +21,16 @@ Joystick::Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameControl
InputDevice("GamePad"),
_sdlGameController(sdlGameController),
_sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),
_sdlHaptic(SDL_HapticOpenFromJoystick(_sdlJoystick)),
_instanceId(instanceId)
{
if (!_sdlHaptic) {
qDebug() << "SDL Haptic Open Failure: " << QString(SDL_GetError());
} else {
if (SDL_HapticRumbleInit(_sdlHaptic) != 0) {
qDebug() << "SDL Haptic Rumble Init Failure: " << QString(SDL_GetError());
}
}
}
Joystick::~Joystick() {
@ -31,6 +38,9 @@ Joystick::~Joystick() {
}
void Joystick::closeJoystick() {
if (_sdlHaptic) {
SDL_HapticClose(_sdlHaptic);
}
SDL_GameControllerClose(_sdlGameController);
}
@ -62,55 +72,64 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {
}
}
bool Joystick::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
if (SDL_HapticRumblePlay(_sdlHaptic, strength, duration) != 0) {
return false;
}
return true;
}
controller::Input::NamedVector Joystick::getAvailableInputs() const {
using namespace controller;
static const Input::NamedVector availableInputs{
makePair(A, "A"),
makePair(B, "B"),
makePair(X, "X"),
makePair(Y, "Y"),
// DPad
makePair(DU, "DU"),
makePair(DD, "DD"),
makePair(DL, "DL"),
makePair(DR, "DR"),
// Bumpers
makePair(LB, "LB"),
makePair(RB, "RB"),
// Stick press
makePair(LS, "LS"),
makePair(RS, "RS"),
// Center buttons
makePair(START, "Start"),
makePair(BACK, "Back"),
// Analog sticks
makePair(LX, "LX"),
makePair(LY, "LY"),
makePair(RX, "RX"),
makePair(RY, "RY"),
// Triggers
makePair(LT, "LT"),
makePair(RT, "RT"),
if (_availableInputs.length() == 0) {
_availableInputs = {
makePair(A, "A"),
makePair(B, "B"),
makePair(X, "X"),
makePair(Y, "Y"),
// DPad
makePair(DU, "DU"),
makePair(DD, "DD"),
makePair(DL, "DL"),
makePair(DR, "DR"),
// Bumpers
makePair(LB, "LB"),
makePair(RB, "RB"),
// Stick press
makePair(LS, "LS"),
makePair(RS, "RS"),
// Center buttons
makePair(START, "Start"),
makePair(BACK, "Back"),
// Analog sticks
makePair(LX, "LX"),
makePair(LY, "LY"),
makePair(RX, "RX"),
makePair(RY, "RY"),
// Aliases, PlayStation style names
makePair(LB, "L1"),
makePair(RB, "R1"),
makePair(LT, "L2"),
makePair(RT, "R2"),
makePair(LS, "L3"),
makePair(RS, "R3"),
makePair(BACK, "Select"),
makePair(A, "Cross"),
makePair(B, "Circle"),
makePair(X, "Square"),
makePair(Y, "Triangle"),
makePair(DU, "Up"),
makePair(DD, "Down"),
makePair(DL, "Left"),
makePair(DR, "Right"),
};
return availableInputs;
// Triggers
makePair(LT, "LT"),
makePair(RT, "RT"),
// Aliases, PlayStation style names
makePair(LB, "L1"),
makePair(RB, "R1"),
makePair(LT, "L2"),
makePair(RT, "R2"),
makePair(LS, "L3"),
makePair(RS, "R3"),
makePair(BACK, "Select"),
makePair(A, "Cross"),
makePair(B, "Circle"),
makePair(X, "Square"),
makePair(Y, "Triangle"),
makePair(DU, "Up"),
makePair(DD, "Down"),
makePair(DL, "Left"),
makePair(DR, "Right"),
};
}
return _availableInputs;
}
QString Joystick::getDefaultMappingConfig() const {

View file

@ -36,6 +36,8 @@ public:
virtual QString getDefaultMappingConfig() const override;
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
virtual void focusOutEvent() override;
bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override;
Joystick() : InputDevice("GamePad") {}
~Joystick();
@ -52,7 +54,10 @@ public:
private:
SDL_GameController* _sdlGameController;
SDL_Joystick* _sdlJoystick;
SDL_Haptic* _sdlHaptic;
SDL_JoystickID _instanceId;
mutable controller::Input::NamedVector _availableInputs;
};
#endif // hifi_Joystick_h

View file

@ -49,7 +49,7 @@ SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) {
}
void SDL2Manager::init() {
bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER) == 0);
bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == 0);
if (initSuccess) {
int joystickCount = SDL_NumJoysticks();

View file

@ -105,6 +105,12 @@ void OculusControllerManager::pluginFocusOutEvent() {
}
}
void OculusControllerManager::stopHapticPulse(bool leftHand) {
if (_touch) {
_touch->stopHapticPulse(leftHand);
}
}
using namespace controller;
static const std::vector<std::pair<ovrButton, StandardButtonChannel>> BUTTON_MAP { {
@ -120,14 +126,14 @@ static const std::vector<std::pair<ovrButton, StandardButtonChannel>> BUTTON_MAP
{ ovrButton_B, B },
{ ovrButton_LThumb, LS },
{ ovrButton_RThumb, RS },
{ ovrButton_LShoulder, LB },
{ ovrButton_RShoulder, RB },
//{ ovrButton_LShoulder, LB },
//{ ovrButton_RShoulder, RB },
} };
static const std::vector<std::pair<ovrTouch, StandardButtonChannel>> TOUCH_MAP { {
{ ovrTouch_X, LEFT_SECONDARY_THUMB_TOUCH },
{ ovrTouch_X, LEFT_PRIMARY_THUMB_TOUCH },
{ ovrTouch_Y, LEFT_SECONDARY_THUMB_TOUCH },
{ ovrTouch_A, RIGHT_SECONDARY_THUMB_TOUCH },
{ ovrTouch_A, RIGHT_PRIMARY_THUMB_TOUCH },
{ ovrTouch_B, RIGHT_SECONDARY_THUMB_TOUCH },
{ ovrTouch_LIndexTrigger, LEFT_PRIMARY_INDEX_TOUCH },
{ ovrTouch_RIndexTrigger, RIGHT_PRIMARY_INDEX_TOUCH },
@ -183,6 +189,8 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, const control
++numTrackedControllers;
if (REQUIRED_HAND_STATUS == (tracking.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) {
handlePose(deltaTime, inputCalibrationData, hand, tracking.HandPoses[hand]);
} else {
_poseStateMap[hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND].valid = false;
}
});
using namespace controller;
@ -191,12 +199,12 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, const control
_axisStateMap[LX] = inputState.Thumbstick[ovrHand_Left].x;
_axisStateMap[LY] = inputState.Thumbstick[ovrHand_Left].y;
_axisStateMap[LT] = inputState.IndexTrigger[ovrHand_Left];
_axisStateMap[LG] = inputState.HandTrigger[ovrHand_Left];
_axisStateMap[LEFT_GRIP] = inputState.HandTrigger[ovrHand_Left];
_axisStateMap[RX] = inputState.Thumbstick[ovrHand_Right].x;
_axisStateMap[RY] = inputState.Thumbstick[ovrHand_Right].y;
_axisStateMap[RT] = inputState.IndexTrigger[ovrHand_Right];
_axisStateMap[RG] = inputState.HandTrigger[ovrHand_Right];
_axisStateMap[RIGHT_GRIP] = inputState.HandTrigger[ovrHand_Right];
// Buttons
for (const auto& pair : BUTTON_MAP) {
@ -210,6 +218,21 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, const control
_buttonPressedMap.insert(pair.second);
}
}
// Haptics
{
Locker locker(_lock);
if (_leftHapticDuration > 0.0f) {
_leftHapticDuration -= deltaTime * 1000.0f; // milliseconds
} else {
stopHapticPulse(true);
}
if (_rightHapticDuration > 0.0f) {
_rightHapticDuration -= deltaTime * 1000.0f; // milliseconds
} else {
stopHapticPulse(false);
}
}
}
void OculusControllerManager::TouchDevice::focusOutEvent() {
@ -220,38 +243,177 @@ void OculusControllerManager::TouchDevice::focusOutEvent() {
void OculusControllerManager::TouchDevice::handlePose(float deltaTime,
const controller::InputCalibrationData& inputCalibrationData, ovrHandType hand,
const ovrPoseStatef& handPose) {
// When the sensor-to-world rotation is identity the coordinate axes look like this:
//
// user
// forward
// -z
// |
// y| user
// y o----x right
// o-----x user
// | up
// |
// z
//
// Rift
// From ABOVE the hand canonical axes looks like this:
//
// | | | | y | | | |
// | | | | | | | | |
// | | | | |
// |left | / x---- + \ |right|
// | _/ z \_ |
// | | | |
// | | | |
//
// So when the user is in Rift space facing the -zAxis with hands outstretched and palms down
// the rotation to align the Touch axes with those of the hands is:
//
// touchToHand = halfTurnAboutY * quaterTurnAboutX
// Due to how the Touch controllers fit into the palm there is an offset that is different for each hand.
// You can think of this offset as the inverse of the measured rotation when the hands are posed, such that
// the combination (measurement * offset) is identity at this orientation.
//
// Qoffset = glm::inverse(deltaRotation when hand is posed fingers forward, palm down)
//
// An approximate offset for the Touch can be obtained by inspection:
//
// Qoffset = glm::inverse(glm::angleAxis(sign * PI/2.0f, zAxis) * glm::angleAxis(PI/4.0f, xAxis))
//
// So the full equation is:
//
// Q = combinedMeasurement * touchToHand
//
// Q = (deltaQ * QOffset) * (yFlip * quarterTurnAboutX)
//
// Q = (deltaQ * inverse(deltaQForAlignedHand)) * (yFlip * quarterTurnAboutX)
auto poseId = hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND;
auto& pose = _poseStateMap[poseId];
static const glm::quat yFlip = glm::angleAxis(PI, Vectors::UNIT_Y);
static const glm::quat quarterX = glm::angleAxis(PI_OVER_TWO, Vectors::UNIT_X);
static const glm::quat touchToHand = yFlip * quarterX;
static const glm::quat leftQuarterZ = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_Z);
static const glm::quat rightQuarterZ = glm::angleAxis(PI_OVER_TWO, Vectors::UNIT_Z);
static const glm::quat eighthX = glm::angleAxis(PI / 4.0f, Vectors::UNIT_X);
static const glm::quat leftRotationOffset = glm::inverse(leftQuarterZ * eighthX) * touchToHand;
static const glm::quat rightRotationOffset = glm::inverse(rightQuarterZ * eighthX) * touchToHand;
static const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches
static const glm::vec3 CONTROLLER_OFFSET = glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f,
CONTROLLER_LENGTH_OFFSET / 2.0f,
CONTROLLER_LENGTH_OFFSET * 2.0f);
static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET;
static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET;
auto translationOffset = (hand == ovrHand_Left ? leftTranslationOffset : rightTranslationOffset);
auto rotationOffset = (hand == ovrHand_Left ? leftRotationOffset : rightRotationOffset);
glm::quat rotation = toGlm(handPose.ThePose.Orientation);
pose.translation = toGlm(handPose.ThePose.Position);
pose.rotation = toGlm(handPose.ThePose.Orientation);
pose.translation += rotation * translationOffset;
pose.rotation = rotation * rotationOffset;
pose.angularVelocity = toGlm(handPose.AngularVelocity);
pose.velocity = toGlm(handPose.LinearVelocity);
pose.valid = true;
// transform into avatar frame
glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
pose = pose.transform(controllerToAvatar);
}
bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
Locker locker(_lock);
bool toReturn = true;
if (hand == controller::BOTH || hand == controller::LEFT) {
if (strength == 0.0f) {
_leftHapticStrength = 0.0f;
_leftHapticDuration = 0.0f;
} else {
_leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength;
if (ovr_SetControllerVibration(_parent._session, ovrControllerType_LTouch, 1.0f, _leftHapticStrength) != ovrSuccess) {
toReturn = false;
}
_leftHapticDuration = std::max(duration, _leftHapticDuration);
}
}
if (hand == controller::BOTH || hand == controller::RIGHT) {
if (strength == 0.0f) {
_rightHapticStrength = 0.0f;
_rightHapticDuration = 0.0f;
} else {
_rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength;
if (ovr_SetControllerVibration(_parent._session, ovrControllerType_RTouch, 1.0f, _rightHapticStrength) != ovrSuccess) {
toReturn = false;
}
_rightHapticDuration = std::max(duration, _rightHapticDuration);
}
}
return toReturn;
}
void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) {
auto handType = (leftHand ? ovrControllerType_LTouch : ovrControllerType_RTouch);
ovr_SetControllerVibration(_parent._session, handType, 0.0f, 0.0f);
}
controller::Input::NamedVector OculusControllerManager::TouchDevice::getAvailableInputs() const {
using namespace controller;
QVector<Input::NamedPair> availableInputs{
// Trackpad analogs
// buttons
makePair(A, "A"),
makePair(B, "B"),
makePair(X, "X"),
makePair(Y, "Y"),
// trackpad analogs
makePair(LX, "LX"),
makePair(LY, "LY"),
makePair(RX, "RX"),
makePair(RY, "RY"),
// trigger analogs
// triggers
makePair(LT, "LT"),
makePair(RT, "RT"),
makePair(LB, "LB"),
makePair(RB, "RB"),
// trigger buttons
//makePair(LB, "LB"),
//makePair(RB, "RB"),
// side grip triggers
makePair(LEFT_GRIP, "LeftGrip"),
makePair(RIGHT_GRIP, "RightGrip"),
// joystick buttons
makePair(LS, "LS"),
makePair(RS, "RS"),
makePair(LEFT_HAND, "LeftHand"),
makePair(RIGHT_HAND, "RightHand"),
makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"),
makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"),
makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"),
makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"),
makePair(LEFT_PRIMARY_THUMB_TOUCH, "LeftPrimaryThumbTouch"),
makePair(LEFT_SECONDARY_THUMB_TOUCH, "LeftSecondaryThumbTouch"),
makePair(RIGHT_PRIMARY_THUMB_TOUCH, "RightPrimaryThumbTouch"),
makePair(RIGHT_SECONDARY_THUMB_TOUCH, "RightSecondaryThumbTouch"),
makePair(LEFT_PRIMARY_INDEX_TOUCH, "LeftPrimaryIndexTouch"),
makePair(RIGHT_PRIMARY_INDEX_TOUCH, "RightPrimaryIndexTouch"),
makePair(LS_TOUCH, "LSTouch"),
makePair(RS_TOUCH, "RSTouch"),
makePair(LEFT_THUMB_UP, "LeftThumbUp"),
makePair(RIGHT_THUMB_UP, "RightThumbUp"),
makePair(LEFT_INDEX_POINT, "LeftIndexPoint"),
makePair(RIGHT_INDEX_POINT, "RightIndexPoint"),
makePair(BACK, "LeftApplicationMenu"),
makePair(START, "RightApplicationMenu"),
};
return availableInputs;
}

View file

@ -32,6 +32,9 @@ public:
void pluginFocusOutEvent() override;
void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
private slots:
void stopHapticPulse(bool leftHand);
private:
class OculusInputDevice : public controller::InputDevice {
public:
@ -64,9 +67,24 @@ private:
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
void focusOutEvent() override;
bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override;
private:
void stopHapticPulse(bool leftHand);
void handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, ovrHandType hand, const ovrPoseStatef& handPose);
int _trackedControllers { 0 };
// perform an action when the TouchDevice mutex is acquired.
using Locker = std::unique_lock<std::recursive_mutex>;
template <typename F>
void withLock(F&& f) { Locker locker(_lock); f(); }
float _leftHapticDuration { 0.0f };
float _leftHapticStrength { 0.0f };
float _rightHapticDuration { 0.0f };
float _rightHapticStrength { 0.0f };
mutable std::recursive_mutex _lock;
friend class OculusControllerManager;
};

View file

@ -126,7 +126,6 @@ bool ViveControllerManager::activate() {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->registerDevice(_inputDevice);
_registeredWithInputMapper = true;
return true;
}
@ -253,6 +252,17 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle
handleHandController(deltaTime, leftHandDeviceIndex, inputCalibrationData, true);
handleHandController(deltaTime, rightHandDeviceIndex, inputCalibrationData, false);
// handle haptics
{
Locker locker(_lock);
if (_leftHapticDuration > 0.0f) {
hapticsHelper(deltaTime, true);
}
if (_rightHapticDuration > 0.0f) {
hapticsHelper(deltaTime, false);
}
}
int numTrackedControllers = 0;
if (leftHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) {
numTrackedControllers++;
@ -354,7 +364,7 @@ void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint
if (button == vr::k_EButton_ApplicationMenu) {
_buttonPressedMap.insert(isLeftHand ? LEFT_APP_MENU : RIGHT_APP_MENU);
} else if (button == vr::k_EButton_Grip) {
_buttonPressedMap.insert(isLeftHand ? LEFT_GRIP : RIGHT_GRIP);
_axisStateMap[isLeftHand ? LEFT_GRIP : RIGHT_GRIP] = 1.0f;
} else if (button == vr::k_EButton_SteamVR_Trigger) {
_buttonPressedMap.insert(isLeftHand ? LT : RT);
} else if (button == vr::k_EButton_SteamVR_Touchpad) {
@ -454,6 +464,55 @@ void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const
_poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = avatarPose.transform(controllerToAvatar);
}
bool ViveControllerManager::InputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
Locker locker(_lock);
if (hand == controller::BOTH || hand == controller::LEFT) {
if (strength == 0.0f) {
_leftHapticStrength = 0.0f;
_leftHapticDuration = 0.0f;
} else {
_leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength;
_leftHapticDuration = std::max(duration, _leftHapticDuration);
}
}
if (hand == controller::BOTH || hand == controller::RIGHT) {
if (strength == 0.0f) {
_rightHapticStrength = 0.0f;
_rightHapticDuration = 0.0f;
} else {
_rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength;
_rightHapticDuration = std::max(duration, _rightHapticDuration);
}
}
return true;
}
void ViveControllerManager::InputDevice::hapticsHelper(float deltaTime, bool leftHand) {
auto handRole = leftHand ? vr::TrackedControllerRole_LeftHand : vr::TrackedControllerRole_RightHand;
auto deviceIndex = _system->GetTrackedDeviceIndexForControllerRole(handRole);
if (_system->IsTrackedDeviceConnected(deviceIndex) &&
_system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller &&
_trackedDevicePose[deviceIndex].bPoseIsValid) {
float strength = leftHand ? _leftHapticStrength : _rightHapticStrength;
float duration = leftHand ? _leftHapticDuration : _rightHapticDuration;
// Vive Controllers only support duration up to 4 ms, which is short enough that any variation feels more like strength
const float MAX_HAPTIC_TIME = 3999.0f; // in microseconds
float hapticTime = strength * MAX_HAPTIC_TIME;
if (hapticTime < duration * 1000.0f) {
_system->TriggerHapticPulse(deviceIndex, 0, hapticTime);
}
float remainingHapticTime = duration - (hapticTime / 1000.0f + deltaTime * 1000.0f); // in milliseconds
if (leftHand) {
_leftHapticDuration = remainingHapticTime;
} else {
_rightHapticDuration = remainingHapticTime;
}
}
}
controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const {
using namespace controller;
QVector<Input::NamedPair> availableInputs{

View file

@ -56,6 +56,9 @@ private:
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
void focusOutEvent() override;
bool triggerHapticPulse(float strength, float duration, controller::Hand hand) override;
void hapticsHelper(float deltaTime, bool leftHand);
void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand);
void handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool touched, bool isLeftHand);
void handleAxisEvent(float deltaTime, uint32_t axis, float x, float y, bool isLeftHand);
@ -90,8 +93,19 @@ private:
FilteredStick _filteredLeftStick;
FilteredStick _filteredRightStick;
// perform an action when the InputDevice mutex is acquired.
using Locker = std::unique_lock<std::recursive_mutex>;
template <typename F>
void withLock(F&& f) { Locker locker(_lock); f(); }
int _trackedControllers { 0 };
vr::IVRSystem*& _system;
float _leftHapticStrength { 0.0f };
float _leftHapticDuration { 0.0f };
float _rightHapticStrength { 0.0f };
float _rightHapticDuration { 0.0f };
mutable std::recursive_mutex _lock;
friend class ViveControllerManager;
};

View file

@ -33,7 +33,7 @@ Column {
"Roughness",
"Metallic",
"Emissive",
"Shaded/Lightmapped/Unlit",
"Unlit",
"Occlusion",
"Lightmap",
"Lighting",

View file

@ -265,9 +265,11 @@ eventMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.LT).peek().to(goActive);
eventMapping.from(Controller.Standard.LB).peek().to(goActive);
eventMapping.from(Controller.Standard.LS).peek().to(goActive);
eventMapping.from(Controller.Standard.LeftGrip).peek().to(goActive);
eventMapping.from(Controller.Standard.RT).peek().to(goActive);
eventMapping.from(Controller.Standard.RB).peek().to(goActive);
eventMapping.from(Controller.Standard.RS).peek().to(goActive);
eventMapping.from(Controller.Standard.RightGrip).peek().to(goActive);
eventMapping.from(Controller.Standard.Back).peek().to(goActive);
eventMapping.from(Controller.Standard.Start).peek().to(goActive);
Controller.enableMapping(eventMappingName);

View file

@ -330,7 +330,7 @@ function MyController(hand) {
this.triggerValue = 0; // rolling average of trigger value
this.rawTriggerValue = 0;
this.rawBumperValue = 0;
this.rawSecondaryValue = 0;
this.rawThumbValue = 0;
//for visualizations
@ -568,10 +568,10 @@ 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.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
(this.triggerSmoothedGrab() || this.secondarySqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
if ((USE_OVERLAY_LINES_FOR_SEARCHING === true) && PICK_WITH_HAND_RAY) {
this.overlayLineOn(handPosition, searchSphereLocation,
(this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
(this.triggerSmoothedGrab() || this.secondarySqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
}
}
@ -818,8 +818,8 @@ function MyController(hand) {
_this.rawTriggerValue = value;
};
this.bumperPress = function(value) {
_this.rawBumperValue = value;
this.secondaryPress = function(value) {
_this.rawSecondaryValue = value;
};
this.updateSmoothedTrigger = function() {
@ -841,20 +841,20 @@ function MyController(hand) {
return this.triggerValue < TRIGGER_OFF_VALUE;
};
this.bumperSqueezed = function() {
return _this.rawBumperValue > BUMPER_ON_VALUE;
this.secondarySqueezed = function() {
return _this.rawSecondaryValue > BUMPER_ON_VALUE;
};
this.bumperReleased = function() {
return _this.rawBumperValue < BUMPER_ON_VALUE;
this.secondaryReleased = function() {
return _this.rawSecondaryValue < BUMPER_ON_VALUE;
};
// this.triggerOrBumperSqueezed = function() {
// return triggerSmoothedSqueezed() || bumperSqueezed();
// this.triggerOrsecondarySqueezed = function() {
// return triggerSmoothedSqueezed() || secondarySqueezed();
// }
// this.triggerAndBumperReleased = function() {
// return triggerSmoothedReleased() && bumperReleased();
// this.triggerAndSecondaryReleased = function() {
// return triggerSmoothedReleased() && secondaryReleased();
// }
this.thumbPress = function(value) {
@ -870,13 +870,13 @@ function MyController(hand) {
};
this.off = function() {
if (this.triggerSmoothedSqueezed() || this.bumperSqueezed()) {
if (this.triggerSmoothedSqueezed() || this.secondarySqueezed()) {
this.lastPickTime = 0;
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
if (this.triggerSmoothedSqueezed()) {
this.setState(STATE_SEARCHING);
} else if (this.bumperSqueezed()) {
} else if (this.secondarySqueezed()) {
this.setState(STATE_HOLD_SEARCHING);
}
}
@ -944,7 +944,7 @@ function MyController(hand) {
return;
}
if (this.state == STATE_HOLD_SEARCHING && this.bumperReleased()) {
if (this.state == STATE_HOLD_SEARCHING && this.secondaryReleased()) {
this.setState(STATE_RELEASE);
return;
}
@ -1108,7 +1108,7 @@ function MyController(hand) {
grabbableData = grabbableDataForCandidate;
}
}
if ((this.grabbedEntity !== null) && (this.triggerSmoothedGrab() || this.bumperSqueezed())) {
if ((this.grabbedEntity !== null) && (this.triggerSmoothedGrab() || this.secondarySqueezed())) {
// We are squeezing enough to grab, and we've found an entity that we'll try to do something with.
var near = (nearPickedCandidateEntities.indexOf(this.grabbedEntity) >= 0) || minDistance <= NEAR_PICK_MAX_DISTANCE;
var isPhysical = propsArePhysical(props);
@ -1274,7 +1274,7 @@ function MyController(hand) {
};
this.continueDistanceHolding = function() {
if (this.triggerSmoothedReleased() && this.bumperReleased()) {
if (this.triggerSmoothedReleased() && this.secondaryReleased()) {
this.setState(STATE_RELEASE);
this.callEntityMethodOnGrabbed("releaseGrab");
return;
@ -1498,7 +1498,7 @@ function MyController(hand) {
this.callEntityMethodOnGrabbed("releaseGrab");
return;
}
if (this.state == STATE_HOLD && this.bumperReleased()) {
if (this.state == STATE_HOLD && this.secondaryReleased()) {
this.setState(STATE_RELEASE);
this.callEntityMethodOnGrabbed("releaseGrab");
return;
@ -1612,7 +1612,7 @@ function MyController(hand) {
this.callEntityMethodOnGrabbed("releaseGrab");
return;
}
if (this.state == STATE_CONTINUE_HOLD && this.bumperReleased()) {
if (this.state == STATE_CONTINUE_HOLD && this.secondaryReleased()) {
this.setState(STATE_RELEASE);
this.callEntityMethodOnGrabbed("releaseEquip");
return;
@ -1741,7 +1741,7 @@ function MyController(hand) {
};
this.nearTrigger = function() {
if (this.triggerSmoothedReleased() && this.bumperReleased()) {
if (this.triggerSmoothedReleased() && this.secondaryReleased()) {
this.setState(STATE_RELEASE);
this.callEntityMethodOnGrabbed("stopNearTrigger");
return;
@ -1751,7 +1751,7 @@ function MyController(hand) {
};
this.farTrigger = function() {
if (this.triggerSmoothedReleased() && this.bumperReleased()) {
if (this.triggerSmoothedReleased() && this.secondaryReleased()) {
this.setState(STATE_RELEASE);
this.callEntityMethodOnGrabbed("stopFarTrigger");
return;
@ -1761,7 +1761,7 @@ function MyController(hand) {
};
this.continueNearTrigger = function() {
if (this.triggerSmoothedReleased() && this.bumperReleased()) {
if (this.triggerSmoothedReleased() && this.secondaryReleased()) {
this.setState(STATE_RELEASE);
this.callEntityMethodOnGrabbed("stopNearTrigger");
return;
@ -1770,7 +1770,7 @@ function MyController(hand) {
};
this.continueFarTrigger = function() {
if (this.triggerSmoothedReleased() && this.bumperReleased()) {
if (this.triggerSmoothedReleased() && this.secondaryReleased()) {
this.setState(STATE_RELEASE);
this.callEntityMethodOnGrabbed("stopFarTrigger");
return;
@ -2050,8 +2050,10 @@ var mapping = Controller.newMapping(MAPPING_NAME);
mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress);
mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress);
mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress);
mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress);
mapping.from([Controller.Standard.RB]).peek().to(rightController.secondaryPress);
mapping.from([Controller.Standard.LB]).peek().to(leftController.secondaryPress);
mapping.from([Controller.Standard.LeftGrip]).peek().to(leftController.secondaryPress);
mapping.from([Controller.Standard.RightGrip]).peek().to(rightController.secondaryPress);
mapping.from([Controller.Standard.LeftPrimaryThumb]).peek().to(leftController.thumbPress);
mapping.from([Controller.Standard.RightPrimaryThumb]).peek().to(rightController.thumbPress);