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

This commit is contained in:
samcake 2016-02-15 09:29:00 -08:00
commit 545f276fdf
65 changed files with 732 additions and 274 deletions

View file

@ -45,25 +45,67 @@ AssetServer::AssetServer(ReceivedMessage& message) :
}
void AssetServer::run() {
qDebug() << "Waiting for connection to domain to request settings from domain-server.";
// wait until we have the domain-server settings, otherwise we bail
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
connect(&domainHandler, &DomainHandler::settingsReceived, this, &AssetServer::completeSetup);
connect(&domainHandler, &DomainHandler::settingsReceiveFail, this, &AssetServer::domainSettingsRequestFailed);
ThreadedAssignment::commonInit(ASSET_SERVER_LOGGING_TARGET_NAME, NodeType::AssetServer);
}
void AssetServer::completeSetup() {
auto nodeList = DependencyManager::get<NodeList>();
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
const QString RESOURCES_PATH = "assets";
auto& domainHandler = nodeList->getDomainHandler();
const QJsonObject& settingsObject = domainHandler.getSettingsObject();
_resourcesDirectory = QDir(ServerPathUtils::getDataDirectory()).filePath(RESOURCES_PATH);
static const QString ASSET_SERVER_SETTINGS_KEY = "asset_server";
if (!settingsObject.contains(ASSET_SERVER_SETTINGS_KEY)) {
qCritical() << "Received settings from the domain-server with no asset-server section. Stopping assignment.";
setFinished(true);
return;
}
auto assetServerObject = settingsObject[ASSET_SERVER_SETTINGS_KEY].toObject();
// get the path to the asset folder from the domain server settings
static const QString ASSETS_PATH_OPTION = "assets_path";
auto assetsJSONValue = assetServerObject[ASSETS_PATH_OPTION];
if (!assetsJSONValue.isString()) {
qCritical() << "Received an assets path from the domain-server that could not be parsed. Stopping assignment.";
setFinished(true);
return;
}
auto assetsPathString = assetsJSONValue.toString();
QDir assetsPath { assetsPathString };
QString absoluteFilePath = assetsPath.absolutePath();
if (assetsPath.isRelative()) {
// if the domain settings passed us a relative path, make an absolute path that is relative to the
// default data directory
absoluteFilePath = ServerPathUtils::getDataFilePath("assets/" + assetsPathString);
}
_resourcesDirectory = QDir(absoluteFilePath);
qDebug() << "Creating resources directory";
_resourcesDirectory.mkpath(".");
bool noExistingAssets = !_resourcesDirectory.exists() \
|| _resourcesDirectory.entryList(QDir::Files).size() == 0;
bool noExistingAssets = !_resourcesDirectory.exists() || _resourcesDirectory.entryList(QDir::Files).size() == 0;
if (noExistingAssets) {
qDebug() << "Asset resources directory not found, searching for existing asset resources";
qDebug() << "Asset resources directory empty, searching for existing asset resources to migrate";
QString oldDataDirectory = QCoreApplication::applicationDirPath();
auto oldResourcesDirectory = QDir(oldDataDirectory).filePath("resources/" + RESOURCES_PATH);
const QString OLD_RESOURCES_PATH = "assets";
auto oldResourcesDirectory = QDir(oldDataDirectory).filePath("resources/" + OLD_RESOURCES_PATH);
if (QDir(oldResourcesDirectory).exists()) {
@ -111,10 +153,12 @@ void AssetServer::run() {
auto hexHash = hash.toHex();
qDebug() << "\tMoving " << filename << " to " << hexHash;
file.rename(_resourcesDirectory.absoluteFilePath(hexHash) + "." + fileInfo.suffix());
}
}
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
}
void AssetServer::handleAssetGetInfo(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {

View file

@ -29,6 +29,8 @@ public slots:
void run();
private slots:
void completeSetup();
void handleAssetGetInfo(QSharedPointer<ReceivedMessage> packet, SharedNodePointer senderNode);
void handleAssetGet(QSharedPointer<ReceivedMessage> packet, SharedNodePointer senderNode);
void handleAssetUpload(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer senderNode);

View file

@ -157,12 +157,16 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend() {
while (it != _audioStreams.end()) {
SharedStreamPointer stream = it->second;
static const int INJECTOR_INACTIVITY_USECS = 5 * USECS_PER_SECOND;
if (stream->popFrames(1, true) > 0) {
stream->updateLastPopOutputLoudnessAndTrailingLoudness();
}
// if we don't have new data for an injected stream in the last INJECTOR_INACTIVITY_MSECS then
static const int INJECTOR_MAX_INACTIVE_BLOCKS = 500;
// if we don't have new data for an injected stream in the last INJECTOR_MAX_INACTIVE_BLOCKS then
// we remove the injector from our streams
if (stream->getType() == PositionalAudioStream::Injector
&& stream->usecsSinceLastPacket() > INJECTOR_INACTIVITY_USECS) {
&& stream->getConsecutiveNotMixedCount() > INJECTOR_MAX_INACTIVE_BLOCKS) {
// this is an inactive injector, pull it from our streams
// first emit that it is finished so that the HRTF objects for this source can be cleaned up
@ -170,12 +174,7 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend() {
// erase the stream to drop our ref to the shared pointer and remove it
it = _audioStreams.erase(it);
} else {
if (stream->popFrames(1, true) > 0) {
stream->updateLastPopOutputLoudnessAndTrailingLoudness();
}
++it;
}
}

View file

@ -168,7 +168,7 @@
},
{
"name": "asset_server",
"label": "Asset Server",
"label": "Asset Server (ATP)",
"assignment-types": [3],
"settings": [
{
@ -178,6 +178,14 @@
"help": "Assigns an asset-server in your domain to serve files to clients via the ATP protocol (over UDP)",
"default": true,
"advanced": true
},
{
"name": "assets_path",
"type": "string",
"label": "Assets Path",
"help": "The path to the directory assets are stored in.<br/>If this path is relative, it will be relative to the application data directory.<br/>If you change this path you will need to manually copy any existing assets from the previous directory.",
"default": "",
"advanced": true
}
]
},
@ -377,7 +385,7 @@
{
"name": "persistFilePath",
"label": "Entities File Path",
"help": "The path to the file entities are stored in. If this path is relative it will be relative to the application data directory. The filename must end in .json.gz.",
"help": "The path to the file entities are stored in.<br/>If this path is relative it will be relative to the application data directory.<br/>The filename must end in .json.gz.",
"placeholder": "models.json.gz",
"default": "models.json.gz",
"advanced": true

View file

@ -120,6 +120,14 @@ DomainServer::~DomainServer() {
DependencyManager::destroy<LimitedNodeList>();
}
void DomainServer::queuedQuit(QString quitMessage, int exitCode) {
if (!quitMessage.isEmpty()) {
qCritical() << qPrintable(quitMessage);
}
QCoreApplication::exit(exitCode);
}
void DomainServer::aboutToQuit() {
// clear the log handler so that Qt doesn't call the destructor on LogHandler
@ -164,8 +172,11 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() {
qDebug() << "TCP server listening for HTTPS connections on" << DOMAIN_SERVER_HTTPS_PORT;
} else if (!certPath.isEmpty() || !keyPath.isEmpty()) {
qDebug() << "Missing certificate or private key. domain-server will now quit.";
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
static const QString MISSING_CERT_ERROR_MSG = "Missing certificate or private key. domain-server will now quit.";
static const int MISSING_CERT_ERROR_CODE = 3;
QMetaObject::invokeMethod(this, "queuedQuit", Qt::QueuedConnection,
Q_ARG(QString, MISSING_CERT_ERROR_MSG), Q_ARG(int, MISSING_CERT_ERROR_CODE));
return false;
}
@ -199,8 +210,10 @@ bool DomainServer::optionallySetupOAuth() {
|| _hostname.isEmpty()
|| _oauthClientID.isEmpty()
|| _oauthClientSecret.isEmpty()) {
qDebug() << "Missing OAuth provider URL, hostname, client ID, or client secret. domain-server will now quit.";
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
static const QString MISSING_OAUTH_INFO_MSG = "Missing OAuth provider URL, hostname, client ID, or client secret. domain-server will now quit.";
static const int MISSING_OAUTH_INFO_ERROR_CODE = 4;
QMetaObject::invokeMethod(this, "queuedQuit", Qt::QueuedConnection,
Q_ARG(QString, MISSING_OAUTH_INFO_MSG), Q_ARG(int, MISSING_OAUTH_INFO_ERROR_CODE));
return false;
} else {
qDebug() << "OAuth will be used to identify clients using provider at" << _oauthProviderURL.toString();
@ -404,9 +417,13 @@ bool DomainServer::resetAccountManagerAccessToken() {
return true;
} else {
qDebug() << "Missing OAuth provider URL, but a domain-server feature was required that requires authentication." <<
"domain-server will now quit.";
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
static const QString MISSING_OAUTH_PROVIDER_MSG =
QString("Missing OAuth provider URL, but a domain-server feature was required that requires authentication.") +
QString("domain-server will now quit.");
static const int MISSING_OAUTH_PROVIDER_ERROR_CODE = 5;
QMetaObject::invokeMethod(this, "queuedQuit", Qt::QueuedConnection,
Q_ARG(QString, MISSING_OAUTH_PROVIDER_MSG),
Q_ARG(int, MISSING_OAUTH_PROVIDER_ERROR_CODE));
return false;
}
@ -517,11 +534,6 @@ void DomainServer::setupICEHeartbeatForFullNetworking() {
}
}
void DomainServer::loginFailed() {
qDebug() << "Login to data server has failed. domain-server will now quit";
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
}
void DomainServer::parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes) {
// check for configs from the command line, these take precedence
const QString ASSIGNMENT_CONFIG_REGEX_STRING = "config-([\\d]+)";

View file

@ -65,7 +65,6 @@ public slots:
private slots:
void aboutToQuit();
void loginFailed();
void setupPendingAssignmentCredits();
void sendPendingTransactionsToServer();
@ -77,6 +76,8 @@ private slots:
void handleTempDomainSuccess(QNetworkReply& requestReply);
void handleTempDomainError(QNetworkReply& requestReply);
void queuedQuit(QString quitMessage, int exitCode);
private:
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());

View file

@ -62,9 +62,14 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
}
}
qCritical() << "Did not find settings decription in JSON at" << SETTINGS_DESCRIPTION_RELATIVE_PATH
<< "- Unable to continue. domain-server will quit.";
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
static const QString MISSING_SETTINGS_DESC_MSG =
QString("Did not find settings decription in JSON at %1 - Unable to continue. domain-server will quit.")
.arg(SETTINGS_DESCRIPTION_RELATIVE_PATH);
static const int MISSING_SETTINGS_DESC_ERROR_CODE = 6;
QMetaObject::invokeMethod(QCoreApplication::instance(), "queuedQuit", Qt::QueuedConnection,
Q_ARG(QString, MISSING_SETTINGS_DESC_MSG),
Q_ARG(int, MISSING_SETTINGS_DESC_ERROR_CODE));
}
void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<ReceivedMessage> message) {

View file

@ -90,6 +90,9 @@ hideOverlay();
// MAIN CONTROL
var wasMuted, isAway;
var eventMappingName = "io.highfidelity.away"; // goActive on hand controller button events, too.
var eventMapping = Controller.newMapping(eventMappingName);
function goAway() {
if (isAway) {
return;
@ -117,8 +120,8 @@ function goActive() {
stopAwayAnimation();
hideOverlay();
}
Script.scriptEnding.connect(goActive);
Controller.keyPressEvent.connect(function (event) {
function maybeGoActive(event) {
if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it)
return;
}
@ -127,13 +130,31 @@ Controller.keyPressEvent.connect(function (event) {
} else {
goActive();
}
});
}
var wasHmdActive = false;
Script.update.connect(function () {
function maybeGoAway() {
if (HMD.active !== wasHmdActive) {
wasHmdActive = !wasHmdActive;
if (wasHmdActive) {
goAway();
}
}
}
Script.update.connect(maybeGoAway);
Controller.mousePressEvent.connect(goActive);
Controller.keyPressEvent.connect(maybeGoActive);
// Note peek() so as to not interfere with other mappings.
eventMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.RightPrimaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(goActive);
Controller.enableMapping(eventMappingName);
Script.scriptEnding.connect(function () {
Script.update.disconnect(maybeGoAway);
goActive();
Controller.disableMapping(eventMappingName);
Controller.mousePressEvent.disconnect(goActive);
Controller.keyPressEvent.disconnect(maybeGoActive);
});

View file

@ -19,18 +19,20 @@ var cubeSize = 0.03;
var cube = Overlays.addOverlay("cube", {
position: cubePosition,
size: cubeSize,
color: { red: 255, green: 0, blue: 0},
color: { red: 0, green: 255, blue: 0},
alpha: 1,
solid: false
});
var square = Overlays.addOverlay("text", {
var SQUARE_SIZE = 20;
var square = Overlays.addOverlay("rectangle", {
x: 0,
y: 0,
width: 20,
height: 20,
color: { red: 255, green: 255, blue: 0},
backgroundColor: { red: 255, green: 255, blue: 0},
width: SQUARE_SIZE,
height: SQUARE_SIZE,
color: { red: 0, green: 255, blue: 0},
backgroundColor: { red: 0, green: 255, blue: 0},
alpha: 1
});
@ -43,7 +45,7 @@ Script.update.connect(function(deltaTime) {
Overlays.editOverlay(cube, { position: lookAt3D });
var lookAt2D = HMD.getHUDLookAtPosition2D();
Overlays.editOverlay(square, { x: lookAt2D.x, y: lookAt2D.y });
Overlays.editOverlay(square, { x: lookAt2D.x - (SQUARE_SIZE/2), y: lookAt2D.y - (SQUARE_SIZE/2)});
});
Script.scriptEnding.connect(function(){

View file

@ -14,6 +14,7 @@
Script.include("../libraries/utils.js");
//
// add lines where the hand ray picking is happening
//
@ -722,6 +723,9 @@ function MyController(hand) {
this.particleBeamOff();
}
this.searchSphereOff();
Controller.setReticleVisible(true);
};
this.triggerPress = function(value) {
@ -1018,6 +1022,9 @@ function MyController(hand) {
this.overlayLineOn(handPosition, searchSphereLocation,
(this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
}
Controller.setReticleVisible(false);
};
this.distanceGrabTimescale = function(mass, distance) {
@ -1879,7 +1886,7 @@ function cleanup() {
rightController.cleanup();
leftController.cleanup();
Controller.disableMapping(MAPPING_NAME);
Controller.setReticleVisible(true);
}
Script.scriptEnding.connect(cleanup);
Script.update.connect(update);

View file

@ -31,8 +31,8 @@ function moveReticleAbsolute(x, y) {
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation";
var mapping = Controller.newMapping(MAPPING_NAME);
mapping.from(Controller.Standard.LT).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
mapping.from(Controller.Standard.RT).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
mapping.from(Controller.Hardware.Hydra.L3).peek().to(Controller.Actions.ReticleClick);
mapping.from(Controller.Hardware.Hydra.R4).peek().to(Controller.Actions.ReticleClick);
mapping.enable();

View file

@ -308,6 +308,11 @@
allSections.push(elWebSections);
var elWebSourceURL = document.getElementById("property-web-source-url");
var elHyperlinkHref = document.getElementById("property-hyperlink-href");
var elHyperlinkDescription = document.getElementById("property-hyperlink-description");
var elHyperlinkSections = document.querySelectorAll(".hyperlink-section");
var elParticleSections = document.querySelectorAll(".particle-section");
allSections.push(elParticleSections);
var elParticleIsEmitting = document.getElementById("property-particle-is-emitting");
@ -370,9 +375,6 @@
var elYTextureURL = document.getElementById("property-y-texture-url");
var elZTextureURL = document.getElementById("property-z-texture-url");
var elHyperlinkHref = document.getElementById("property-hyperlink-href");
var elHyperlinkDescription = document.getElementById("property-hyperlink-description");
var elPreviewCameraButton = document.getElementById("preview-camera-button");
if (window.EventBridge !== undefined) {
@ -492,8 +494,12 @@
for (var j = 0; j < allSections[i].length; j++) {
allSections[i][j].style.display = 'none';
}
}
for (var i = 0; i < elHyperlinkSections.length; i++) {
elHyperlinkSections[i].style.display = 'block';
}
if (properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") {
elColorSection.style.display = 'block';
elColorRed.value = properties.color.red;
@ -526,6 +532,9 @@
for (var i = 0; i < elWebSections.length; i++) {
elWebSections[i].style.display = 'block';
}
for (var i = 0; i < elHyperlinkSections.length; i++) {
elHyperlinkSections[i].style.display = 'none';
}
elWebSourceURL.value = properties.sourceUrl;
} else if (properties.type == "Text") {
@ -1158,17 +1167,17 @@
</div>
<div class="section-header">
<div class="section-header hyperlink-section">
<label>Hyperlink</label>
</div>
<div class="property">
<div class="hyperlink-section property">
<div class="label">Href - Hifi://address</div>
<div class="value">
<input id="property-hyperlink-href" class="url">
</div>
</div>
<div class="property">
<div class="hyperlink-section property">
<div class="label">Description</div>
<div class="value">
<input id="property-hyperlink-description" class="url">

View file

@ -0,0 +1,161 @@
// entityEditStressTest.js
//
// Created by Seiji Emery on 8/31/15
// Copyright 2015 High Fidelity, Inc.
//
// Stress tests the client + server-side entity trees by spawning huge numbers of entities in
// close proximity to your avatar and updating them continuously (ie. applying position edits),
// with the intent of discovering crashes and other bugs related to the entity, scripting,
// rendering, networking, and/or physics subsystems.
//
// This script was originally created to find + diagnose an a clientside crash caused by improper
// locking of the entity tree, but can be reused for other purposes.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var NUM_ENTITIES = 20; // number of entities to spawn
var ENTITY_SPAWN_INTERVAL = 0.01;
var ENTITY_SPAWN_LIMIT = 1000;
var Y_OFFSET = 1.5;
var ENTITY_LIFETIME = 600; // Entity timeout (when/if we crash, we need the entities to delete themselves)
var KEEPALIVE_INTERVAL = 15; // Refreshes the timeout every X seconds
var RADIUS = 0.5; // Spawn within this radius (square)
var TEST_ENTITY_NAME = "EntitySpawnTest";
var UPDATE_INTERVAL = 0.1;
(function () {
this.makeEntity = function (properties) {
var entity = Entities.addEntity(properties);
return {
update: function (properties) {
Entities.editEntity(entity, properties);
},
destroy: function () {
Entities.deleteEntity(entity)
},
getAge: function () {
return Entities.getEntityProperties(entity).age;
}
};
}
this.randomPositionXZ = function (center, radius) {
return {
x: center.x + (Math.random() * radius * 2.0) - radius,
y: center.y,
z: center.z + (Math.random() * radius * 2.0) - radius
};
}
this.randomDimensions = function () {
return {
x: 0.1 + Math.random() * 0.1,
y: 0.1 + Math.random() * 0.05,
z: 0.1 + Math.random() * 0.1
};
}
})();
(function () {
var entities = [];
var entitiesToCreate = 0;
var entitiesSpawned = 0;
function clear () {
var ids = Entities.findEntities(MyAvatar.position, 50);
var that = this;
ids.forEach(function(id) {
var properties = Entities.getEntityProperties(id);
if (properties.name == TEST_ENTITY_NAME) {
Entities.deleteEntity(id);
}
}, this);
}
function createEntities () {
print("Creating " + NUM_ENTITIES + " entities (UPDATE_INTERVAL = " + UPDATE_INTERVAL + ", KEEPALIVE_INTERVAL = " + KEEPALIVE_INTERVAL + ")");
entitiesToCreate = NUM_ENTITIES;
Script.update.connect(spawnEntities);
}
var spawnTimer = 0.0;
function spawnEntities (dt) {
if (entitiesToCreate <= 0) {
Script.update.disconnect(spawnEntities);
print("Finished spawning entities");
}
else if ((spawnTimer -= dt) < 0.0){
spawnTimer = ENTITY_SPAWN_INTERVAL;
var n = Math.min(entitiesToCreate, ENTITY_SPAWN_LIMIT);
print("Spawning " + n + " entities (" + (entitiesSpawned += n) + ")");
entitiesToCreate -= n;
var center = { x: 0, y: 0, z: 0 } //MyAvatar.position;
// center.y += 1.0;
for (; n > 0; --n) {
entities.push(makeEntity({
type: "Model",
name: TEST_ENTITY_NAME,
position: randomPositionXZ(center, RADIUS),
dimensions: randomDimensions(),
modelURL: "https://s3.amazonaws.com/DreamingContent/models/console.fbx",
lifetime: ENTITY_LIFETIME
}));
}
}
}
function despawnEntities () {
print("despawning entities");
entities.forEach(function (entity) {
entity.destroy();
});
entities = [];
}
var keepAliveTimer = 0.0;
var updateTimer = 0.0;
// Runs the following entity updates:
// a) refreshes the timeout interval every KEEPALIVE_INTERVAL seconds, and
// b) re-randomizes its position every UPDATE_INTERVAL seconds.
// This should be sufficient to crash the client until the entity tree bug is fixed (and thereafter if it shows up again).
function updateEntities (dt) {
var updateLifetime = ((keepAliveTimer -= dt) < 0.0) ? ((keepAliveTimer = KEEPALIVE_INTERVAL), true) : false;
var updateProperties = ((updateTimer -= dt) < 0.0) ? ((updateTimer = UPDATE_INTERVAL), true) : false;
if (updateLifetime || updateProperties) {
var center = MyAvatar.position;
center.y -= Y_OFFSET;
entities.forEach((updateLifetime && updateProperties && function (entity) {
entity.update({
lifetime: entity.getAge() + ENTITY_LIFETIME,
position: randomPositionXZ(center, RADIUS)
});
}) || (updateLifetime && function (entity) {
entity.update({
lifetime: entity.getAge() + ENTITY_LIFETIME
});
}) || (updateProperties && function (entity) {
entity.update({
position: randomPositionXZ(center, RADIUS)
});
}) || null, this);
}
}
function init () {
Script.update.disconnect(init);
clear();
createEntities();
//Script.update.connect(updateEntities);
Script.scriptEnding.connect(despawnEntities);
}
Script.update.connect(init);
})();

View file

@ -17,7 +17,7 @@ var SPEED = 1.0;
var currentPosition = { x: 0, y: 0, z: 0 };
var trailingLoudness = 0.0;
var soundClip = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Tabla+Loops/Tabla1.wav");
var soundClip = SoundCache.getSound("http://public.highfidelity.io/sounds/Rats_Squeaks_Active.wav");
var properties = {
type: "Box",
@ -27,18 +27,26 @@ var properties = {
};
var objectId = Entities.addEntity(properties);
var sound = Audio.playSound(soundClip, { position: orbitCenter, loop: true, volume: 0.5 });
var soundOptions = { position: orbitCenter, loop: true, volume: 0.5 };
var sound;
function update(deltaTime) {
function update(deltaTime) {
if (!soundClip.downloaded) {
return;
}
if (!sound) {
sound = Audio.playSound(soundClip, soundOptions); // Not until downloaded
}
time += deltaTime;
currentPosition = { x: orbitCenter.x + Math.cos(time * SPEED) * RADIUS, y: orbitCenter.y, z: orbitCenter.z + Math.sin(time * SPEED) * RADIUS };
trailingLoudness = 0.9 * trailingLoudness + 0.1 * sound.loudness;
Entities.editEntity( objectId, { position: currentPosition, color: { red: Math.min(trailingLoudness * 2000, 255), green: 0, blue: 0 } } );
sound.options = { position: currentPosition };
soundOptions.position = currentPosition;
sound.setOptions(soundOptions);
}
Script.scriptEnding.connect(function() {
Entities.deleteEntity(objectId);
});
Script.update.connect(update);
Script.update.connect(update);

View file

@ -210,6 +210,10 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html";
static const unsigned int THROTTLED_SIM_FRAMERATE = 15;
static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE;
static const unsigned int CAPPED_SIM_FRAMERATE = 60;
static const int CAPPED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / CAPPED_SIM_FRAMERATE;
static const uint32_t INVALID_FRAME = UINT32_MAX;
static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation
@ -273,7 +277,8 @@ public:
#endif
enum CustomEventTypes {
Lambda = QEvent::User + 1
Lambda = QEvent::User + 1,
Paint = Lambda + 1,
};
class LambdaEvent : public QEvent {
@ -984,6 +989,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);
_idleTimer = new QTimer(this);
connect(_idleTimer, &QTimer::timeout, [=] {
idle(usecTimestampNow());
});
connect(this, &Application::beforeAboutToQuit, [=] {
disconnect(_idleTimer);
});
// Setting the interval to zero forces this to get called whenever there are no messages
// in the queue, which can be pretty damn frequent. Hence the idle function has a bunch
// of logic to abort early if it's being called too often.
_idleTimer->start(0);
}
void Application::aboutToQuit() {
@ -1297,9 +1313,6 @@ void Application::initializeUi() {
}
void Application::paintGL() {
// paintGL uses a queued connection, so we can get messages from the queue even after we've quit
// and the plugins have shutdown
if (_aboutToQuit) {
@ -1629,6 +1642,7 @@ void Application::paintGL() {
// Store both values now for use by next cycle.
_lastInstantaneousFps = instantaneousFps;
_lastUnsynchronizedFps = 1.0f / (((usecTimestampNow() - now) / (float)USECS_PER_SECOND) + paintWaitAndQTTimerAllowance);
_pendingPaint = false;
}
void Application::runTests() {
@ -1717,6 +1731,10 @@ bool Application::event(QEvent* event) {
return true;
}
if ((int)event->type() == (int)Paint) {
paintGL();
}
if (!_keyboardFocusedItem.isInvalidID()) {
switch (event->type()) {
case QEvent::KeyPress:
@ -2401,26 +2419,62 @@ bool Application::acceptSnapshot(const QString& urlString) {
return true;
}
static uint32_t _renderedFrameIndex { INVALID_FRAME };
void Application::idle(uint64_t now) {
if (_aboutToQuit) {
return; // bail early, nothing to do here.
}
auto displayPlugin = getActiveDisplayPlugin();
// depending on whether we're throttling or not.
// Once rendering is off on another thread we should be able to have Application::idle run at start(0) in
// perpetuity and not expect events to get backed up.
bool isThrottled = getActiveDisplayPlugin()->isThrottled();
bool isThrottled = displayPlugin->isThrottled();
// Only run simulation code if more than the targetFramePeriod have passed since last time we ran
// This attempts to lock the simulation at 60 updates per second, regardless of framerate
float timeSinceLastUpdateUs = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_USEC;
float secondsSinceLastUpdate = timeSinceLastUpdateUs / USECS_PER_SECOND;
if (isThrottled && (timeSinceLastUpdateUs / USECS_PER_MSEC) < THROTTLED_SIM_FRAME_PERIOD_MS) {
// Throttling both rendering and idle
return; // bail early, we're throttled and not enough time has elapsed
}
_lastTimeUpdated.start();
auto presentCount = displayPlugin->presentCount();
if (presentCount < _renderedFrameIndex) {
_renderedFrameIndex = INVALID_FRAME;
}
// Nested ifs are for clarity in the logic. Don't collapse them into a giant single if.
// Don't saturate the main thread with rendering, no paint calls until the last one is complete
if (!_pendingPaint) {
// Also no paint calls until the display plugin has increased by at least one frame
// (don't render at 90fps if the display plugin only goes at 60)
if (_renderedFrameIndex == INVALID_FRAME || presentCount > _renderedFrameIndex) {
// Record what present frame we're on
_renderedFrameIndex = presentCount;
// Don't allow paint requests to stack up in the event queue
_pendingPaint = true;
// But when we DO request a paint, get to it as soon as possible: high priority
postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
}
}
// For the rest of idle, we want to cap at the max sim rate, so we might not call
// the remaining idle work every paint frame, or vice versa
// In theory this means we could call idle processing more often than painting,
// but in practice, when the paintGL calls aren't keeping up, there's no room left
// in the main thread to call idle more often than paint.
// This check is mostly to keep idle from burning up CPU cycles by running at
// hundreds of idles per second when the rendering is that fast
if ((timeSinceLastUpdateUs / USECS_PER_MSEC) < CAPPED_SIM_FRAME_PERIOD_MS) {
// No paint this round, but might be time for a new idle, otherwise return
return;
}
// We're going to execute idle processing, so restart the last idle timer
_lastTimeUpdated.start();
{
PROFILE_RANGE(__FUNCTION__);
@ -3071,13 +3125,18 @@ void Application::update(float deltaTime) {
auto myAvatar = getMyAvatar();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix());
userInputMapper->update(deltaTime);
controller::InputCalibrationData calibrationData = {
myAvatar->getSensorToWorldMatrix(),
createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()),
myAvatar->getHMDSensorMatrix()
};
bool jointsCaptured = false;
for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
if (inputPlugin->isActive()) {
inputPlugin->pluginUpdate(deltaTime, jointsCaptured);
inputPlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured);
if (inputPlugin->isJointController()) {
jointsCaptured = true;
}
@ -4777,17 +4836,9 @@ void Application::updateDisplayMode() {
foreach(auto displayPlugin, standard) {
addDisplayPluginToMenu(displayPlugin, first);
// This must be a queued connection to avoid a deadlock
QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender, [=] {
postEvent(this, new LambdaEvent([=] {
paintGL();
}), Qt::HighEventPriority);
});
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) {
resizeGL();
});
first = false;
}
@ -4979,17 +5030,11 @@ void Application::setPalmData(Hand* hand, const controller::Pose& pose, float de
// of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about
// controller::Pose. More work is needed to clean this up.
hand->modifyPalm(whichHand, [&](PalmData& palm) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
palm.setActive(pose.isValid());
// transform from sensor space, to world space, to avatar model space.
glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation() * myAvatar->getScale());
glm::mat4 sensorToWorldMat = myAvatar->getSensorToWorldMatrix();
glm::mat4 modelMat = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat;
glm::vec3 position = extractTranslation(objectPose);
glm::quat rotation = glm::quat_cast(objectPose);
// controller pose is in Avatar frame.
glm::vec3 position = pose.getTranslation();
glm::quat rotation = pose.getRotation();
// Compute current velocity from position change
glm::vec3 rawVelocity;

View file

@ -508,6 +508,8 @@ private:
int _avatarAttachmentRequest = 0;
bool _settingsLoaded { false };
bool _pendingPaint { false };
QTimer* _idleTimer { nullptr };
};
#endif // hifi_Application_h

View file

@ -1392,7 +1392,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
desiredFacing.y = 0.0f;
// This is our reference frame, it is captured when the user begins to move.
glm::vec3 referenceFacing = transformVector(_sensorToWorldMatrix, _hoverReferenceCameraFacing);
glm::vec3 referenceFacing = transformVectorFast(_sensorToWorldMatrix, _hoverReferenceCameraFacing);
referenceFacing.y = 0.0f;
referenceFacing = glm::normalize(referenceFacing);
glm::vec3 referenceRight(referenceFacing.z, 0.0f, -referenceFacing.x);
@ -1597,7 +1597,7 @@ void MyAvatar::updatePosition(float deltaTime) {
if (!_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) > 0.1f || fabs(_driveKeys[TRANSLATE_X]) > 0.1f)) {
_hoverReferenceCameraFacingIsCaptured = true;
// transform the camera facing vector into sensor space.
_hoverReferenceCameraFacing = transformVector(glm::inverse(_sensorToWorldMatrix), getHead()->getCameraOrientation() * Vectors::UNIT_Z);
_hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getHead()->getCameraOrientation() * Vectors::UNIT_Z);
} else if (_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) <= 0.1f && fabs(_driveKeys[TRANSLATE_X]) <= 0.1f)) {
_hoverReferenceCameraFacingIsCaptured = false;
}

View file

@ -20,15 +20,39 @@
HMDScriptingInterface::HMDScriptingInterface() {
}
glm::vec3 HMDScriptingInterface::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const {
glm::vec3 result;
qApp->getApplicationCompositor().calculateRayUICollisionPoint(position, direction, result);
return result;
}
glm::vec2 HMDScriptingInterface::overlayFromWorldPoint(const glm::vec3& position) const {
return qApp->getApplicationCompositor().overlayFromSphereSurface(position);
}
glm::vec2 HMDScriptingInterface::sphericalToOverlay(const glm::vec2 & position) const {
return qApp->getApplicationCompositor().sphericalToOverlay(position);
}
glm::vec2 HMDScriptingInterface::overlayToSpherical(const glm::vec2 & position) const {
return qApp->getApplicationCompositor().overlayToSpherical(position);
}
glm::vec2 HMDScriptingInterface::screenToOverlay(const glm::vec2 & position) const {
return qApp->getApplicationCompositor().screenToOverlay(position);
}
glm::vec2 HMDScriptingInterface::overlayToScreen(const glm::vec2 & position) const {
return qApp->getApplicationCompositor().overlayToScreen(position);
}
QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) {
glm::vec3 hudIntersection;
auto instance = DependencyManager::get<HMDScriptingInterface>();
if (instance->getHUDLookAtPosition3D(hudIntersection)) {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::vec3 sphereCenter = myAvatar->getDefaultEyePosition();
glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * (hudIntersection - sphereCenter);
glm::vec2 polar = glm::vec2(glm::atan(direction.x, -direction.z), glm::asin(direction.y)) * -1.0f;
auto overlayPos = qApp->getApplicationCompositor().sphericalToOverlay(polar);
glm::vec2 overlayPos = qApp->getApplicationCompositor().overlayFromSphereSurface(hudIntersection);
return qScriptValueFromValue<glm::vec2>(engine, overlayPos);
}
return QScriptValue::NullValue;

View file

@ -25,6 +25,16 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
Q_OBJECT
Q_PROPERTY(glm::vec3 position READ getPosition)
Q_PROPERTY(glm::quat orientation READ getOrientation)
public:
Q_INVOKABLE glm::vec3 calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const;
Q_INVOKABLE glm::vec2 overlayFromWorldPoint(const glm::vec3& position) const;
Q_INVOKABLE glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
Q_INVOKABLE glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
Q_INVOKABLE glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const;
Q_INVOKABLE glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const;
public:
HMDScriptingInterface();
static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine);

View file

@ -262,7 +262,6 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
camMat = (headPose * eyeToHead) * camMat;
batch.setViewportTransform(renderArgs->_viewport);
batch.setViewTransform(camMat);
batch.setProjectionTransform(qApp->getEyeProjection(eye));
#ifdef DEBUG_OVERLAY
@ -282,21 +281,28 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
bindCursorTexture(batch);
//Controller Pointers
glm::mat4 overlayXfm;
_modelTransform.getMatrix(overlayXfm);
//Mouse Pointer
glm::vec2 projection = screenToSpherical(qApp->getTrueMouse());
mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
mat4 reticleXfm = overlayXfm * pointerXfm;
reticleXfm = glm::scale(reticleXfm, reticleScale);
batch.setModelTransform(reticleXfm);
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
auto controllerScriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
bool reticleVisible = controllerScriptingInterface->getReticleVisible();
if (reticleVisible) {
glm::mat4 overlayXfm;
_modelTransform.getMatrix(overlayXfm);
glm::vec2 projection = screenToSpherical(qApp->getTrueMouse());
float cursorDepth = controllerScriptingInterface->getReticleDepth();
mat4 pointerXfm = glm::scale(mat4(), vec3(cursorDepth)) * glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
mat4 reticleXfm = overlayXfm * pointerXfm;
reticleXfm = glm::scale(reticleXfm, reticleScale);
batch.setModelTransform(reticleXfm);
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
}
});
}
// FIXME - this probably is hella buggy and probably doesn't work correctly
// we should kill it asap.
void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {
const glm::vec2 projection = overlayToSpherical(cursorPos);
// The overlay space orientation of the mouse coordinates
@ -326,16 +332,22 @@ void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& or
//Finds the collision point of a world space ray
bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation());
auto displayPlugin = qApp->getActiveDisplayPlugin();
auto headPose = displayPlugin->getHeadPose(qApp->getFrameCount());
glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition());
glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction);
auto myCamera = qApp->getCamera();
mat4 cameraMat = myCamera->getTransform();
auto UITransform = cameraMat * glm::inverse(headPose);
auto relativePosition4 = glm::inverse(UITransform) * vec4(position, 1);
auto relativePosition = vec3(relativePosition4) / relativePosition4.w;
auto relativeDirection = glm::inverse(glm::quat_cast(UITransform)) * direction;
float t;
if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getUniformScale(), &t)){
result = position + direction * t;
float uiRadius = _oculusUIRadius; // * myAvatar->getUniformScale(); // FIXME - how do we want to handle avatar scale
float instersectionDistance;
if (raySphereIntersect(relativeDirection, relativePosition, uiRadius, &instersectionDistance)){
result = position + glm::normalize(direction) * instersectionDistance;
return true;
}
@ -494,6 +506,23 @@ glm::vec2 ApplicationCompositor::overlayToScreen(const glm::vec2& overlayPos) co
return sphericalToScreen(overlayToSpherical(overlayPos));
}
glm::vec2 ApplicationCompositor::overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const {
auto displayPlugin = qApp->getActiveDisplayPlugin();
auto headPose = displayPlugin->getHeadPose(qApp->getFrameCount());
auto myCamera = qApp->getCamera();
mat4 cameraMat = myCamera->getTransform();
auto UITransform = cameraMat * glm::inverse(headPose);
auto relativePosition4 = glm::inverse(UITransform) * vec4(sphereSurfacePoint, 1);
auto relativePosition = vec3(relativePosition4) / relativePosition4.w;
auto center = vec3(0); // center of HUD in HUD space
auto direction = relativePosition - center; // direction to relative position in HUD space
glm::vec2 polar = glm::vec2(glm::atan(direction.x, -direction.z), glm::asin(direction.y)) * -1.0f;
auto overlayPos = sphericalToOverlay(polar);
return overlayPos;
}
void ApplicationCompositor::updateTooltips() {
if (_hoverItemId != _noItemId) {
quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs;

View file

@ -62,6 +62,8 @@ public:
void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const;
uint32_t getOverlayTexture() const;
glm::vec2 overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const;
void setCameraBaseTransform(const Transform& transform) { _cameraBaseTransform = transform; }
const Transform& getCameraBaseTransform() const { return _cameraBaseTransform; }

View file

@ -143,10 +143,23 @@ static bool cpuSupportsAVX() {
return result;
}
#elif defined(__GNU__)
#elif defined(__GNUC__)
#include <cpuid.h>
static bool cpuSupportsAVX() {
return __builtin_cpu_supports("avx");
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

View file

@ -120,7 +120,7 @@ namespace controller {
return availableInputs;
}
void ActionsDevice::update(float deltaTime, bool jointsCaptured) {
void ActionsDevice::update(float deltaTime, const InputCalibrationData& inpuCalibrationData, bool jointsCaptured) {
}
void ActionsDevice::focusOutEvent() {

View file

@ -110,7 +110,7 @@ class ActionsDevice : public QObject, public InputDevice {
public:
virtual EndpointPointer createEndpoint(const Input& input) const override;
virtual Input::NamedVector getAvailableInputs() const override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
virtual void focusOutEvent() override;
ActionsDevice();

View file

@ -15,6 +15,12 @@
namespace controller {
struct InputCalibrationData {
glm::mat4 sensorToWorldMat;
glm::mat4 avatarMat;
glm::mat4 hmdSensorMat;
};
enum class ChannelType {
UNKNOWN = 0,
BUTTON,

View file

@ -52,12 +52,12 @@ public:
float getValue(const Input& input) const;
float getValue(ChannelType channelType, uint16_t channel) const;
Pose getPoseValue(uint16_t channel) const;
const QString& getName() const { return _name; }
// Update call MUST be called once per simulation loop
// It takes care of updating the action states and deltas
virtual void update(float deltaTime, bool jointsCaptured) = 0;
virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) = 0;
virtual void focusOutEvent() = 0;
@ -101,4 +101,4 @@ private:
static float _reticleMoveSpeed;
};
}
}

View file

@ -60,5 +60,14 @@ namespace controller {
}
}
Pose Pose::transform(const glm::mat4& mat) const {
auto rot = glmExtractRotation(mat);
Pose pose(transformPoint(mat, translation),
rot * rotation,
transformVectorFast(mat, velocity),
rot * angularVelocity);
pose.valid = valid;
return pose;
}
}

View file

@ -40,6 +40,8 @@ namespace controller {
vec3 getVelocity() const { return velocity; }
vec3 getAngularVelocity() const { return angularVelocity; }
Pose transform(const glm::mat4& mat) const;
static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event);
static void fromScriptValue(const QScriptValue& object, Pose& event);
};

View file

@ -13,10 +13,11 @@
#ifndef hifi_AbstractControllerScriptingInterface_h
#define hifi_AbstractControllerScriptingInterface_h
#include <unordered_map>
#include <unordered_set>
#include <atomic>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -89,6 +90,12 @@ namespace controller {
Q_INVOKABLE QObject* parseMapping(const QString& json);
Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl);
Q_INVOKABLE bool getReticleVisible() { return _reticleVisible; }
Q_INVOKABLE void setReticleVisible(bool visible) { _reticleVisible = visible; }
Q_INVOKABLE float getReticleDepth() { return _reticleDepth; }
Q_INVOKABLE void setReticleDepth(float depth) { _reticleDepth = depth; }
Q_INVOKABLE glm::vec2 getReticlePosition() {
return toGlm(QCursor::pos());
}
@ -163,6 +170,9 @@ namespace controller {
bool _touchCaptured{ false };
bool _wheelCaptured{ false };
bool _actionsCaptured{ false };
bool _reticleVisible{ true };
float _reticleDepth { 1.0f };
};

View file

@ -25,7 +25,7 @@ StandardController::StandardController() : InputDevice("Standard") {
StandardController::~StandardController() {
}
void StandardController::update(float deltaTime, bool jointsCaptured) {
void StandardController::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
}
void StandardController::focusOutEvent() {

View file

@ -28,7 +28,7 @@ public:
virtual EndpointPointer createEndpoint(const Input& input) const override;
virtual Input::NamedVector getAvailableInputs() const override;
virtual QStringList getDefaultMappingConfigs() const override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
virtual void focusOutEvent() override;
StandardController();

View file

@ -25,7 +25,7 @@ StateController::StateController() : InputDevice("Application") {
StateController::~StateController() {
}
void StateController::update(float deltaTime, bool jointsCaptured) {}
void StateController::update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) {}
void StateController::focusOutEvent() {}

View file

@ -28,7 +28,7 @@ public:
// Device functions
virtual Input::NamedVector getAvailableInputs() const override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
virtual void focusOutEvent() override;
StateController();

View file

@ -97,9 +97,6 @@ namespace controller {
// Update means go grab all the device input channels and update the output channel values
void update(float deltaTime);
void setSensorToWorldMat(glm::mat4 sensorToWorldMat) { _sensorToWorldMat = sensorToWorldMat; }
glm::mat4 getSensorToWorldMat() { return _sensorToWorldMat; }
const DevicesMap& getDevices() { return _registeredDevices; }
uint16 getStandardDeviceID() const { return STANDARD_DEVICE; }
InputDevice::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; }
@ -131,8 +128,6 @@ namespace controller {
std::vector<Pose> _poseStates = std::vector<Pose>(toInt(Action::NUM_ACTIONS));
std::vector<float> _lastStandardStates = std::vector<float>();
glm::mat4 _sensorToWorldMat;
int recordDeviceOfType(const QString& deviceName);
QHash<const QString&, int> _deviceCounts;

View file

@ -46,35 +46,19 @@ void Basic2DWindowOpenGLDisplayPlugin::internalPresent() {
}
WindowOpenGLDisplayPlugin::internalPresent();
}
const uint32_t THROTTLED_FRAMERATE = 15;
int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const {
static const int ULIMIITED_PAINT_TIMER_DELAY_MS = 1;
int result = ULIMIITED_PAINT_TIMER_DELAY_MS;
if (_isThrottled) {
// This test wouldn't be necessary if we could depend on updateFramerate setting _framerateTarget.
// Alas, that gets complicated: isThrottled() is const and other stuff depends on it.
result = MSECS_PER_SECOND / THROTTLED_FRAMERATE;
}
return result;
}
static const uint32_t MIN_THROTTLE_CHECK_FRAMES = 60;
bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const {
static const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Menu.h
bool shouldThrottle = (!_container->isForeground() && _container->isOptionChecked(ThrottleFPSIfNotFocus));
if (_isThrottled != shouldThrottle) {
_isThrottled = shouldThrottle;
_timer.start(getDesiredInterval());
static auto lastCheck = presentCount();
// Don't access the menu API every single frame
if ((presentCount() - lastCheck) > MIN_THROTTLE_CHECK_FRAMES) {
static const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Menu.h
_isThrottled = (!_container->isForeground() && _container->isOptionChecked(ThrottleFPSIfNotFocus));
lastCheck = presentCount();
}
return shouldThrottle;
}
void Basic2DWindowOpenGLDisplayPlugin::updateFramerate() {
int newInterval = getDesiredInterval();
_timer.start(newInterval);
return _isThrottled;
}
// FIXME target the screen the window is currently on

View file

@ -31,11 +31,9 @@ public:
virtual bool isThrottled() const override;
protected:
int getDesiredInterval() const;
mutable bool _isThrottled = false;
private:
void updateFramerate();
static const QString NAME;
QScreen* getFullscreenTarget();
std::vector<QAction*> _framerateActions;

View file

@ -188,19 +188,6 @@ OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
cleanupForSceneTexture(texture);
_container->releaseSceneTexture(texture);
});
connect(&_timer, &QTimer::timeout, this, [&] {
#ifdef Q_OS_MAC
// On Mac, QT thread timing is such that we can miss one or even two cycles quite often, giving a render rate (including update/simulate)
// far lower than what we want. This hack keeps that rate more natural, at the expense of some wasted rendering.
// This is likely to be mooted by further planned changes.
if (_active && _sceneTextureEscrow.depth() <= 1) {
#else
if (_active && _sceneTextureEscrow.depth() < 1) {
#endif
emit requestRender();
}
});
}
void OpenGLDisplayPlugin::cleanupForSceneTexture(uint32_t sceneTexture) {
@ -214,7 +201,6 @@ void OpenGLDisplayPlugin::activate() {
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
#if THREADED_PRESENT
_timer.start(1);
// Start the present thread if necessary
auto presentThread = DependencyManager::get<PresentThread>();
if (!presentThread) {
@ -236,12 +222,9 @@ void OpenGLDisplayPlugin::activate() {
_container->makeRenderingContextCurrent();
#endif
DisplayPlugin::activate();
}
void OpenGLDisplayPlugin::stop() {
_timer.stop();
}
void OpenGLDisplayPlugin::deactivate() {
@ -250,7 +233,6 @@ void OpenGLDisplayPlugin::deactivate() {
Lock lock(_mutex);
_deactivateWait.wait(lock, [&]{ return _uncustomized; });
}
_timer.stop();
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
@ -376,16 +358,12 @@ void OpenGLDisplayPlugin::internalPresent() {
}
void OpenGLDisplayPlugin::present() {
incrementPresentCount();
updateTextures();
if (_currentSceneTexture) {
internalPresent();
updateFramerate();
}
#if THREADED_PRESENT
#else
emit requestRender();
#endif
}
float OpenGLDisplayPlugin::presentRate() {

View file

@ -73,7 +73,6 @@ protected:
// Plugin specific functionality to composite the scene and overlay and present the result
virtual void internalPresent();
mutable QTimer _timer;
ProgramPtr _program;
ShapeWrapperPtr _plane;

View file

@ -405,18 +405,16 @@ public:
virtual void loader() {} // called indirectly when urls for geometry are updated
/// Has the entity been added to an entity tree? This is useful in some state decisions
/// for example whether or not to send out a scriptChanging signal.
void markAsAddedToTree() { _isAddedToTree = true; }
bool isAddedToTree() const { return _isAddedToTree; }
/// Should the external entity script mechanism call a preload for this entity.
/// Due to the asyncronous nature of signals for add entity and script changing
/// it's possible for two similar signals to cross paths. This method allows the
/// entity to definitively state if the preload signal should be sent.
bool shouldPreloadScript() const { return !_script.isEmpty() &&
_loadedScript != _script && _loadedScriptTimestamp != _scriptTimestamp; }
///
/// We only want to preload if:
/// there is some script, and either the script value or the scriptTimestamp
/// value have changed since our last preload
bool shouldPreloadScript() const { return !_script.isEmpty() &&
((_loadedScript != _script) || (_loadedScriptTimestamp != _scriptTimestamp)); }
void scriptHasPreloaded() { _loadedScript = _script; _loadedScriptTimestamp = _scriptTimestamp; }
protected:
@ -461,8 +459,15 @@ protected:
float _restitution;
float _friction;
float _lifetime;
QString _script;
quint64 _scriptTimestamp;
QString _script; /// the value of the script property
QString _loadedScript; /// the value of _script when the last preload signal was sent
quint64 _scriptTimestamp{ ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP }; /// the script loaded property used for forced reload
/// the value of _scriptTimestamp when the last preload signal was sent
// NOTE: on construction we want this to be different from _scriptTimestamp so we intentionally bump it
quint64 _loadedScriptTimestamp{ ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP + 1 };
QString _collisionSoundURL;
glm::vec3 _registrationPoint;
float _angularDamping;
@ -523,12 +528,6 @@ protected:
mutable QHash<QUuid, quint64> _previouslyDeletedActions;
QUuid _sourceUUID; /// the server node UUID we came from
bool _isAddedToTree { false }; /// this is false until the entity has been added to an entity tree
QString _loadedScript; /// the value of _script when the last preload signal was sent
quint64 _loadedScriptTimestamp { 0 }; /// the value of _scriptTimestamp when the last preload signal was sent
};
#endif // hifi_EntityItem_h

View file

@ -92,9 +92,6 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
// find and hook up any entities with this entity as a (previously) missing parent
fixupMissingParents();
// indicate that the entity has been added to the tree
entity->markAsAddedToTree();
}
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {

View file

@ -927,11 +927,8 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter;
// If the script value has changed on us, or it's timestamp has changed to force
// a reload then we want to send out a script changing signal... UNLESS this is the
// first time we've read the entity information, in which case, the entity has
// not yet been added to the tree, and we want to wait for the addEntity. In other
// words it's not a "change" in the script, if it's a first time load.
if (entityItem->isAddedToTree() && (entityScriptBefore != entityScriptAfter || reload)) {
// a reload then we want to send out a script changing signal...
if (entityScriptBefore != entityScriptAfter || reload) {
_myTree->emitEntityScriptChanging(entityItemID, reload); // the entity script has changed
}

View file

@ -20,8 +20,8 @@
const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";
void KeyboardMouseDevice::pluginUpdate(float deltaTime, bool jointsCaptured) {
_inputDevice->update(deltaTime, jointsCaptured);
void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
_inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
// For touch event, we need to check that the last event is not too long ago
// Maybe it's a Qt issue, but the touch event sequence (begin, update, end) is not always called properly
@ -36,7 +36,7 @@ void KeyboardMouseDevice::pluginUpdate(float deltaTime, bool jointsCaptured) {
}
}
void KeyboardMouseDevice::InputDevice::update(float deltaTime, bool jointsCaptured) {
void KeyboardMouseDevice::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
_axisStateMap.clear();
}

View file

@ -70,7 +70,7 @@ public:
virtual const QString& getName() const override { return NAME; }
virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override;
virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
void keyPressEvent(QKeyEvent* event);
void keyReleaseEvent(QKeyEvent* event);
@ -97,7 +97,7 @@ protected:
// Device functions
virtual controller::Input::NamedVector getAvailableInputs() const override;
virtual QString getDefaultMappingConfig() const override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
virtual void focusOutEvent() override;
// Let's make it easy for Qt because we assume we love Qt forever

View file

@ -111,7 +111,7 @@ controller::Input::NamedPair SpacemouseDevice::makePair(SpacemouseDevice::Positi
return controller::Input::NamedPair(makeInput(axis), name);
}
void SpacemouseDevice::update(float deltaTime, bool jointsCaptured) {
void SpacemouseDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
// the update is done in the SpacemouseManager class.
// for windows in the nativeEventFilter the inputmapper is connected or registed or removed when an 3Dconnnexion device is attached or detached
// for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached

View file

@ -214,7 +214,7 @@ public:
virtual controller::Input::NamedVector getAvailableInputs() const override;
virtual QString getDefaultMappingConfig() const override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
virtual void focusOutEvent() override;
glm::vec3 cc_position;

View file

@ -149,23 +149,50 @@ bool NetworkGeometry::isLoadedWithTextures() const {
}
void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& url) {
if (_meshes.size() > 0) {
auto textureCache = DependencyManager::get<TextureCache>();
for (auto&& material : _materials) {
auto networkMaterial = material->_material;
auto oldTextureMaps = networkMaterial->getTextureMaps();
if (material->diffuseTextureName == name) {
material->diffuseTexture = textureCache->getTexture(url, DEFAULT_TEXTURE);
auto diffuseMap = model::TextureMapPointer(new model::TextureMap());
diffuseMap->setTextureSource(material->diffuseTexture->_textureSource);
diffuseMap->setTextureTransform(
oldTextureMaps[model::MaterialKey::DIFFUSE_MAP]->getTextureTransform());
networkMaterial->setTextureMap(model::MaterialKey::DIFFUSE_MAP, diffuseMap);
} else if (material->normalTextureName == name) {
material->normalTexture = textureCache->getTexture(url);
auto normalMap = model::TextureMapPointer(new model::TextureMap());
normalMap->setTextureSource(material->normalTexture->_textureSource);
networkMaterial->setTextureMap(model::MaterialKey::NORMAL_MAP, normalMap);
} else if (material->specularTextureName == name) {
material->specularTexture = textureCache->getTexture(url);
auto glossMap = model::TextureMapPointer(new model::TextureMap());
glossMap->setTextureSource(material->specularTexture->_textureSource);
networkMaterial->setTextureMap(model::MaterialKey::GLOSS_MAP, glossMap);
} else if (material->emissiveTextureName == name) {
material->emissiveTexture = textureCache->getTexture(url);
auto lightmapMap = model::TextureMapPointer(new model::TextureMap());
lightmapMap->setTextureSource(material->emissiveTexture->_textureSource);
lightmapMap->setTextureTransform(
oldTextureMaps[model::MaterialKey::LIGHTMAP_MAP]->getTextureTransform());
glm::vec2 oldOffsetScale =
oldTextureMaps[model::MaterialKey::LIGHTMAP_MAP]->getLightmapOffsetScale();
lightmapMap->setLightmapOffsetScale(oldOffsetScale.x, oldOffsetScale.y);
networkMaterial->setTextureMap(model::MaterialKey::LIGHTMAP_MAP, lightmapMap);
}
}
} else {
qCWarning(modelnetworking) << "Ignoring setTextureWirthNameToURL() geometry not ready." << name << url;
qCWarning(modelnetworking) << "Ignoring setTextureWithNameToURL() geometry not ready." << name << url;
}
_isLoadedWithTextures = false;
}
@ -374,7 +401,6 @@ void NetworkGeometry::modelParseError(int error, QString str) {
_resource = nullptr;
}
const NetworkMaterial* NetworkGeometry::getShapeMaterial(int shapeID) {
if ((shapeID >= 0) && (shapeID < (int)_shapes.size())) {
int materialID = _shapes[shapeID]->_materialID;

View file

@ -8,6 +8,7 @@
#pragma once
#include <functional>
#include <atomic>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -122,10 +123,17 @@ public:
virtual void resetSensors() {}
virtual float devicePixelRatio() { return 1.0f; }
virtual float presentRate() { return -1.0f; }
uint32_t presentCount() const { return _presentedFrameIndex; }
static const QString& MENU_PATH();
signals:
void recommendedFramebufferSizeChanged(const QSize & size);
void requestRender();
protected:
void incrementPresentCount() { ++_presentedFrameIndex; }
private:
std::atomic<uint32_t> _presentedFrameIndex;
};

View file

@ -12,12 +12,16 @@
#include "Plugin.h"
namespace controller {
struct InputCalibrationData;
}
class InputPlugin : public Plugin {
public:
virtual bool isJointController() const = 0;
virtual void pluginFocusOutEvent() = 0;
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) = 0;
virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) = 0;
};

View file

@ -288,6 +288,11 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
RenderArgs* args = renderContext->args;
// FIXME: Different render modes should have different tasks
if (args->_renderMode != RenderArgs::DEFAULT_RENDER_MODE) {
return;
}
auto framebufferCache = DependencyManager::get<FramebufferCache>();
auto depthBuffer = framebufferCache->getPrimaryDepthTexture();
auto normalBuffer = framebufferCache->getDeferredNormalTexture();

View file

@ -194,10 +194,11 @@ void DeferredLightingEffect::render(const render::RenderContextPointer& renderCo
batch.setResourceTexture(DEFERRED_BUFFER_EMISSIVE_UNIT, framebufferCache->getDeferredSpecularTexture());
batch.setResourceTexture(DEFERRED_BUFFER_DEPTH_UNIT, framebufferCache->getPrimaryDepthTexture());
// need to assign the white texture if ao is off
if (_ambientOcclusionEnabled) {
// FIXME: Different render modes should have different tasks
if (args->_renderMode == RenderArgs::DEFAULT_RENDER_MODE && _ambientOcclusionEnabled) {
batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, framebufferCache->getOcclusionTexture());
} else {
// need to assign the white texture if ao is off
batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, textureCache->getWhiteTexture());
}

View file

@ -31,7 +31,7 @@ glm::quat Quat::rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
}
glm::quat Quat::lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up) {
return glm::quat_cast(glm::lookAt(eye, center, up));
return glm::inverse(glm::quat_cast(glm::lookAt(eye, center, up))); // OpenGL view matrix is inverse of our camera matrix.
}
glm::quat Quat::lookAtSimple(const glm::vec3& eye, const glm::vec3& center) {

View file

@ -407,7 +407,14 @@ glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& p) {
return glm::vec3(temp.x / temp.w, temp.y / temp.w, temp.z / temp.w);
}
glm::vec3 transformVector(const glm::mat4& m, const glm::vec3& v) {
// does not handle non-uniform scale correctly, but it's faster then transformVectorFull
glm::vec3 transformVectorFast(const glm::mat4& m, const glm::vec3& v) {
glm::mat3 rot(m);
return rot * v;
}
// handles non-uniform scale.
glm::vec3 transformVectorFull(const glm::mat4& m, const glm::vec3& v) {
glm::mat3 rot(m);
return glm::inverse(glm::transpose(rot)) * v;
}
@ -437,7 +444,7 @@ glm::vec2 getFacingDir2D(const glm::quat& rot) {
}
glm::vec2 getFacingDir2D(const glm::mat4& m) {
glm::vec3 facing3D = transformVector(m, Vectors::UNIT_NEG_Z);
glm::vec3 facing3D = transformVectorFast(m, Vectors::UNIT_NEG_Z);
glm::vec2 facing2D(facing3D.x, facing3D.z);
const float ALMOST_ZERO = 0.0001f;
if (glm::length(facing2D) < ALMOST_ZERO) {

View file

@ -215,7 +215,8 @@ glm::mat4 createMatFromQuatAndPos(const glm::quat& q, const glm::vec3& p);
glm::quat cancelOutRollAndPitch(const glm::quat& q);
glm::mat4 cancelOutRollAndPitch(const glm::mat4& m);
glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& p);
glm::vec3 transformVector(const glm::mat4& m, const glm::vec3& v);
glm::vec3 transformVectorFast(const glm::mat4& m, const glm::vec3& v);
glm::vec3 transformVectorFull(const glm::mat4& m, const glm::vec3& v);
// Calculate an orthogonal basis from a primary and secondary axis.
// The uAxis, vAxis & wAxis will form an orthognal basis.

View file

@ -28,6 +28,11 @@ public:
init(menu, qmlObject);
}
~MenuUserData() {
_widget->setUserData(USER_DATA_ID, nullptr);
_qml->setUserData(USER_DATA_ID, nullptr);
}
const QUuid uuid{ QUuid::createUuid() };
static MenuUserData* forObject(QObject* object) {
@ -37,12 +42,17 @@ public:
private:
MenuUserData(const MenuUserData&);
void init(QObject* widgetObject, QObject* qmlObject) {
_widget = widgetObject;
_qml = qmlObject;
widgetObject->setUserData(USER_DATA_ID, this);
qmlObject->setUserData(USER_DATA_ID, this);
qmlObject->setObjectName(uuid.toString());
// Make sure we can find it again in the future
Q_ASSERT(VrMenu::_instance->findMenuObject(uuid.toString()));
}
QObject* _widget { nullptr };
QObject* _qml { nullptr };
};
const int MenuUserData::USER_DATA_ID = QObject::registerUserData();

View file

@ -498,14 +498,14 @@ void NeuronPlugin::deactivate() {
#endif
}
void NeuronPlugin::pluginUpdate(float deltaTime, bool jointsCaptured) {
void NeuronPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
std::vector<NeuronJoint> joints;
{
// lock and copy
std::lock_guard<std::mutex> guard(_jointsMutex);
joints = _joints;
}
_inputDevice->update(deltaTime, joints, _prevJoints);
_inputDevice->update(deltaTime, inputCalibrationData, joints, _prevJoints);
_prevJoints = joints;
}
@ -537,7 +537,7 @@ QString NeuronPlugin::InputDevice::getDefaultMappingConfig() const {
return MAPPING_JSON;
}
void NeuronPlugin::InputDevice::update(float deltaTime, const std::vector<NeuronPlugin::NeuronJoint>& joints, const std::vector<NeuronPlugin::NeuronJoint>& prevJoints) {
void NeuronPlugin::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const std::vector<NeuronPlugin::NeuronJoint>& joints, const std::vector<NeuronPlugin::NeuronJoint>& prevJoints) {
for (size_t i = 0; i < joints.size(); i++) {
glm::vec3 linearVel, angularVel;
glm::vec3 pos = joints[i].pos;

View file

@ -35,7 +35,7 @@ public:
virtual void deactivate() override;
virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override;
virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
virtual void saveSettings() const override;
virtual void loadSettings() override;
@ -56,10 +56,10 @@ protected:
// Device functions
virtual controller::Input::NamedVector getAvailableInputs() const override;
virtual QString getDefaultMappingConfig() const override;
virtual void update(float deltaTime, bool jointsCaptured) override {};
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override {};
virtual void focusOutEvent() override {};
void update(float deltaTime, const std::vector<NeuronPlugin::NeuronJoint>& joints, const std::vector<NeuronPlugin::NeuronJoint>& prevJoints);
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const std::vector<NeuronPlugin::NeuronJoint>& joints, const std::vector<NeuronPlugin::NeuronJoint>& prevJoints);
};
std::shared_ptr<InputDevice> _inputDevice { std::make_shared<InputDevice>() };

View file

@ -39,7 +39,7 @@ void Joystick::closeJoystick() {
#endif
}
void Joystick::update(float deltaTime, bool jointsCaptured) {
void Joystick::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
for (auto axisState : _axisStateMap) {
if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) {
_axisStateMap[axisState.first] = 0.0f;

View file

@ -39,7 +39,7 @@ public:
// Device functions
virtual controller::Input::NamedVector getAvailableInputs() const override;
virtual QString getDefaultMappingConfig() const override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
virtual void focusOutEvent() override;
Joystick() : InputDevice("GamePad") {}

View file

@ -138,12 +138,12 @@ void SDL2Manager::pluginFocusOutEvent() {
#endif
}
void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) {
void SDL2Manager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
#ifdef HAVE_SDL2
if (_isInitialized) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
for (auto joystick : _openJoysticks) {
joystick->update(deltaTime, jointsCaptured);
joystick->update(deltaTime, inputCalibrationData, jointsCaptured);
}
PerformanceTimer perfTimer("SDL2Manager::update");

View file

@ -40,7 +40,7 @@ public:
virtual void deactivate() override;
virtual void pluginFocusOutEvent() override;
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override;
virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
signals:
void joystickAdded(Joystick* joystick);

View file

@ -132,16 +132,16 @@ void SixenseManager::setSixenseFilter(bool filter) {
#endif
}
void SixenseManager::pluginUpdate(float deltaTime, bool jointsCaptured) {
void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
BAIL_IF_NOT_LOADED
_inputDevice->update(deltaTime, jointsCaptured);
_inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
if (_inputDevice->_requestReset) {
_container->requestReset();
_inputDevice->_requestReset = false;
}
}
void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) {
void SixenseManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
BAIL_IF_NOT_LOADED
#ifdef HAVE_SIXENSE
_buttonPressedMap.clear();
@ -205,7 +205,7 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) {
if (!jointsCaptured) {
// Rotation of Palm
glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]);
handlePoseEvent(deltaTime, position, rotation, left);
handlePoseEvent(deltaTime, inputCalibrationData, position, rotation, left);
rawPoses[i] = controller::Pose(position, rotation, Vectors::ZERO, Vectors::ZERO);
} else {
_poseStateMap.clear();
@ -415,7 +415,7 @@ void SixenseManager::InputDevice::handleButtonEvent(unsigned int buttons, bool l
}
}
void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) {
void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const glm::vec3& position, const glm::quat& rotation, bool left) {
BAIL_IF_NOT_LOADED
#ifdef HAVE_SIXENSE
auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND;
@ -437,7 +437,7 @@ void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 pos
auto prevPose = _poseStateMap[hand];
// Transform the measured position into body frame.
position = _avatarRotation * (position + _avatarPosition);
vec3 pos = _avatarRotation * (position + _avatarPosition);
// From ABOVE the hand canonical axes look like this:
//
@ -476,7 +476,7 @@ void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 pos
// rotation = postOffset * Qsh^ * (measuredRotation * preOffset) * Qsh
//
// TODO: find a shortcut with fewer rotations.
rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
glm::quat rot = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
glm::vec3 velocity(0.0f);
glm::vec3 angularVelocity(0.0f);
@ -484,11 +484,11 @@ void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 pos
if (prevPose.isValid() && deltaTime > std::numeric_limits<float>::epsilon()) {
auto& samples = _collectedSamples[hand];
velocity = (position - prevPose.getTranslation()) / deltaTime;
velocity = (pos - prevPose.getTranslation()) / deltaTime;
samples.first.addSample(velocity);
velocity = samples.first.average;
auto deltaRot = glm::normalize(rotation * glm::conjugate(prevPose.getRotation()));
auto deltaRot = glm::normalize(rot * glm::conjugate(prevPose.getRotation()));
auto axis = glm::axis(deltaRot);
auto speed = glm::angle(deltaRot) / deltaTime;
assert(!glm::isnan(speed));
@ -500,7 +500,10 @@ void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 pos
_collectedSamples[hand].second.clear();
}
_poseStateMap[hand] = controller::Pose(position, rotation, velocity, angularVelocity);
// transform pose into avatar frame.
glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
auto avatarPose = controller::Pose(pos, rot, velocity, angularVelocity).transform(controllerToAvatar);
_poseStateMap[hand] = avatarPose;
#endif // HAVE_SIXENSE
}

View file

@ -36,7 +36,7 @@ public:
virtual void deactivate() override;
virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override;
virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
virtual void saveSettings() const override;
virtual void loadSettings() override;
@ -66,11 +66,11 @@ private:
// Device functions
virtual controller::Input::NamedVector getAvailableInputs() const override;
virtual QString getDefaultMappingConfig() const override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
virtual void focusOutEvent() override;
void handleButtonEvent(unsigned int buttons, bool left);
void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left);
void handlePoseEvent(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const glm::vec3& position, const glm::quat& rotation, bool left);
void updateCalibration(SixenseControllerData* controllers);
friend class SixenseManager;

View file

@ -102,7 +102,7 @@ void ViveControllerManager::activate() {
// vertexBufferPtr->getSize() - sizeof(float) * 2,
// sizeof(vr::RenderModel_Vertex_t),
// gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::RAW)));
gpu::Element formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
gpu::Element formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
_texture = gpu::TexturePointer(
@ -205,8 +205,8 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch&
}
void ViveControllerManager::pluginUpdate(float deltaTime, bool jointsCaptured) {
_inputDevice->update(deltaTime, jointsCaptured);
void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
_inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
if (_inputDevice->_trackedControllers == 0 && _registeredWithInputMapper) {
@ -222,7 +222,7 @@ void ViveControllerManager::pluginUpdate(float deltaTime, bool jointsCaptured) {
}
}
void ViveControllerManager::InputDevice::update(float deltaTime, bool jointsCaptured) {
void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
_poseStateMap.clear();
_buttonPressedMap.clear();
@ -232,29 +232,29 @@ void ViveControllerManager::InputDevice::update(float deltaTime, bool jointsCapt
int numTrackedControllers = 0;
for (vr::TrackedDeviceIndex_t device = vr::k_unTrackedDeviceIndex_Hmd + 1;
device < vr::k_unMaxTrackedDeviceCount && numTrackedControllers < 2; ++device) {
device < vr::k_unMaxTrackedDeviceCount && numTrackedControllers < 2; ++device) {
if (!_hmd->IsTrackedDeviceConnected(device)) {
continue;
}
if(_hmd->GetTrackedDeviceClass(device) != vr::TrackedDeviceClass_Controller) {
if (_hmd->GetTrackedDeviceClass(device) != vr::TrackedDeviceClass_Controller) {
continue;
}
if (!_trackedDevicePose[device].bPoseIsValid) {
continue;
}
numTrackedControllers++;
bool left = numTrackedControllers == 2;
const mat4& mat = _trackedDevicePoseMat4[device];
if (!jointsCaptured) {
handlePoseEvent(mat, numTrackedControllers - 1);
handlePoseEvent(inputCalibrationData, mat, numTrackedControllers - 1);
}
// handle inputs
vr::VRControllerState_t controllerState = vr::VRControllerState_t();
if (_hmd->GetControllerState(device, &controllerState)) {
@ -271,7 +271,7 @@ void ViveControllerManager::InputDevice::update(float deltaTime, bool jointsCapt
}
}
}
_trackedControllers = numTrackedControllers;
}
@ -314,7 +314,7 @@ void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool
}
}
void ViveControllerManager::InputDevice::handlePoseEvent(const mat4& mat, bool left) {
void ViveControllerManager::InputDevice::handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, bool left) {
// When the sensor-to-world rotation is identity the coordinate axes look like this:
//
// user
@ -342,11 +342,11 @@ void ViveControllerManager::InputDevice::handlePoseEvent(const mat4& mat, bool l
// | | | |
//
// So when the user is standing in Vive space facing the -zAxis with hands outstretched and palms down
// So when the user is standing in Vive space facing the -zAxis with hands outstretched and palms down
// the rotation to align the Vive axes with those of the hands is:
//
// QviveToHand = halfTurnAboutY * quaterTurnAboutX
// Due to how the Vive 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.
@ -384,8 +384,11 @@ void ViveControllerManager::InputDevice::handlePoseEvent(const mat4& mat, bool l
position += rotation * (left ? leftTranslationOffset : rightTranslationOffset);
rotation = rotation * (left ? leftRotationOffset : rightRotationOffset);
_poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = controller::Pose(position, rotation);
// transform into avatar frame
glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
auto avatarPose = controller::Pose(position, rotation).transform(controllerToAvatar);
_poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = avatarPose;
}
controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const {
@ -436,7 +439,7 @@ QString ViveControllerManager::InputDevice::getDefaultMappingConfig() const {
//void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) {
// const float JOYSTICK_MOVE_SPEED = 1.0f;
//
//
// // Left Trackpad: Movement, strafing
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
@ -446,17 +449,17 @@ QString ViveControllerManager::InputDevice::getDefaultMappingConfig() const {
// // Right Trackpad: Vertical movement, zooming
// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(AXIS_Y_POS, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(AXIS_Y_NEG, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
//
//
// // Buttons
// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 0));
// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 1));
//
//
// mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(GRIP_BUTTON, 0));
// mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(GRIP_BUTTON, 1));
//
// mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));
// mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));
//
//
// // Hands
// mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
// mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));

View file

@ -41,7 +41,7 @@ public:
virtual void deactivate() override;
virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override;
virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
void updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges);
@ -55,12 +55,12 @@ private:
// Device functions
virtual controller::Input::NamedVector getAvailableInputs() const override;
virtual QString getDefaultMappingConfig() const override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
virtual void focusOutEvent() override;
void handleButtonEvent(uint32_t button, bool pressed, bool left);
void handleAxisEvent(uint32_t axis, float x, float y, bool left);
void handlePoseEvent(const mat4& mat, bool left);
void handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, bool left);
int _trackedControllers { 0 };
vr::IVRSystem*& _hmd;

View file

@ -123,8 +123,14 @@ int main(int argc, char** argv) {
float delta = now - last;
last = now;
InputCalibrationData calibrationData = {
glm::mat4(),
glm::mat4(),
glm::mat4()
};
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
inputPlugin->pluginUpdate(delta, false);
inputPlugin->pluginUpdate(delta, calibrationData, false);
}
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
@ -133,6 +139,12 @@ int main(int argc, char** argv) {
timer.start(50);
{
InputCalibrationData calibrationData = {
glm::mat4(),
glm::mat4(),
glm::mat4()
};
DependencyManager::set<controller::UserInputMapper>();
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
QString name = inputPlugin->getName();
@ -141,7 +153,7 @@ int main(int argc, char** argv) {
if (name == KeyboardMouseDevice::NAME) {
userInputMapper->registerDevice(std::dynamic_pointer_cast<KeyboardMouseDevice>(inputPlugin)->getInputDevice());
}
inputPlugin->pluginUpdate(0, false);
inputPlugin->pluginUpdate(0, calibrationData, false);
}
rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface());
}