mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-30 14:09:30 +02:00
Merge branch 'master' of git://github.com/highfidelity/hifi into 19644
Conflicts: interface/src/Menu.cpp interface/ui/preferencesDialog.ui
This commit is contained in:
commit
7a08ac76d2
72 changed files with 4426 additions and 1769 deletions
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include <AccountManager.h>
|
#include <AccountManager.h>
|
||||||
#include <Assignment.h>
|
#include <Assignment.h>
|
||||||
|
#include <HifiConfigVariantMap.h>
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
|
@ -41,73 +42,65 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
||||||
setOrganizationDomain("highfidelity.io");
|
setOrganizationDomain("highfidelity.io");
|
||||||
setApplicationName("assignment-client");
|
setApplicationName("assignment-client");
|
||||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||||
|
|
||||||
QStringList argumentList = arguments();
|
|
||||||
|
|
||||||
// register meta type is required for queued invoke method on Assignment subclasses
|
|
||||||
|
|
||||||
// set the logging target to the the CHILD_TARGET_NAME
|
// set the logging target to the the CHILD_TARGET_NAME
|
||||||
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||||
|
|
||||||
const QString ASSIGNMENT_TYPE_OVVERIDE_OPTION = "-t";
|
const QVariantMap argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments());
|
||||||
int argumentIndex = argumentList.indexOf(ASSIGNMENT_TYPE_OVVERIDE_OPTION);
|
|
||||||
|
const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "t";
|
||||||
|
const QString ASSIGNMENT_POOL_OPTION = "pool";
|
||||||
|
const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet";
|
||||||
|
const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "a";
|
||||||
|
|
||||||
Assignment::Type requestAssignmentType = Assignment::AllTypes;
|
Assignment::Type requestAssignmentType = Assignment::AllTypes;
|
||||||
|
|
||||||
if (argumentIndex != -1) {
|
// check for an assignment type passed on the command line or in the config
|
||||||
requestAssignmentType = (Assignment::Type) argumentList[argumentIndex + 1].toInt();
|
if (argumentVariantMap.contains(ASSIGNMENT_TYPE_OVERRIDE_OPTION)) {
|
||||||
|
requestAssignmentType = (Assignment::Type) argumentVariantMap.value(ASSIGNMENT_TYPE_OVERRIDE_OPTION).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString ASSIGNMENT_POOL_OPTION = "--pool";
|
|
||||||
|
|
||||||
argumentIndex = argumentList.indexOf(ASSIGNMENT_POOL_OPTION);
|
|
||||||
|
|
||||||
QString assignmentPool;
|
QString assignmentPool;
|
||||||
|
|
||||||
if (argumentIndex != -1) {
|
// check for an assignment pool passed on the command line or in the config
|
||||||
assignmentPool = argumentList[argumentIndex + 1];
|
if (argumentVariantMap.contains(ASSIGNMENT_POOL_OPTION)) {
|
||||||
|
assignmentPool = argumentVariantMap.value(ASSIGNMENT_POOL_OPTION).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup our _requestAssignment member variable from the passed arguments
|
// setup our _requestAssignment member variable from the passed arguments
|
||||||
_requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool);
|
_requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool);
|
||||||
|
|
||||||
// check if we were passed a wallet UUID on the command line
|
// check for a wallet UUID on the command line or in the config
|
||||||
// this would represent where the user running AC wants funds sent to
|
// this would represent where the user running AC wants funds sent to
|
||||||
|
if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) {
|
||||||
const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet";
|
QUuid walletUUID = argumentVariantMap.value(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION).toString();
|
||||||
if ((argumentIndex = argumentList.indexOf(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) != -1) {
|
|
||||||
QUuid walletUUID = QString(argumentList[argumentIndex + 1]);
|
|
||||||
qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID);
|
qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID);
|
||||||
_requestAssignment.setWalletUUID(walletUUID);
|
_requestAssignment.setWalletUUID(walletUUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a NodeList as an unassigned client
|
// create a NodeList as an unassigned client
|
||||||
NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned);
|
NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned);
|
||||||
|
|
||||||
// check for an overriden assignment server hostname
|
// check for an overriden assignment server hostname
|
||||||
const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a";
|
if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION)) {
|
||||||
|
_assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString();
|
||||||
argumentIndex = argumentList.indexOf(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION);
|
|
||||||
|
|
||||||
if (argumentIndex != -1) {
|
|
||||||
_assignmentServerHostname = argumentList[argumentIndex + 1];
|
|
||||||
|
|
||||||
// set the custom assignment socket on our NodeList
|
// set the custom assignment socket on our NodeList
|
||||||
HifiSockAddr customAssignmentSocket = HifiSockAddr(_assignmentServerHostname, DEFAULT_DOMAIN_SERVER_PORT);
|
HifiSockAddr customAssignmentSocket = HifiSockAddr(_assignmentServerHostname, DEFAULT_DOMAIN_SERVER_PORT);
|
||||||
|
|
||||||
nodeList->setAssignmentServerSocket(customAssignmentSocket);
|
nodeList->setAssignmentServerSocket(customAssignmentSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
// call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required
|
// call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required
|
||||||
qDebug() << "Waiting for assignment -" << _requestAssignment;
|
qDebug() << "Waiting for assignment -" << _requestAssignment;
|
||||||
|
|
||||||
QTimer* timer = new QTimer(this);
|
QTimer* timer = new QTimer(this);
|
||||||
connect(timer, SIGNAL(timeout()), SLOT(sendAssignmentRequest()));
|
connect(timer, SIGNAL(timeout()), SLOT(sendAssignmentRequest()));
|
||||||
timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS);
|
timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS);
|
||||||
|
|
||||||
// connect our readPendingDatagrams method to the readyRead() signal of the socket
|
// connect our readPendingDatagrams method to the readyRead() signal of the socket
|
||||||
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
|
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
|
||||||
|
|
||||||
// connections to AccountManager for authentication
|
// connections to AccountManager for authentication
|
||||||
connect(&AccountManager::getInstance(), &AccountManager::authRequired,
|
connect(&AccountManager::getInstance(), &AccountManager::authRequired,
|
||||||
this, &AssignmentClient::handleAuthenticationRequest);
|
this, &AssignmentClient::handleAuthenticationRequest);
|
||||||
|
@ -121,49 +114,49 @@ void AssignmentClient::sendAssignmentRequest() {
|
||||||
|
|
||||||
void AssignmentClient::readPendingDatagrams() {
|
void AssignmentClient::readPendingDatagrams() {
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
QByteArray receivedPacket;
|
QByteArray receivedPacket;
|
||||||
HifiSockAddr senderSockAddr;
|
HifiSockAddr senderSockAddr;
|
||||||
|
|
||||||
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
|
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
|
||||||
receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
|
receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
|
||||||
nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(),
|
nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(),
|
||||||
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
|
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
|
||||||
|
|
||||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||||
if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) {
|
if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) {
|
||||||
// construct the deployed assignment from the packet data
|
// construct the deployed assignment from the packet data
|
||||||
_currentAssignment = SharedAssignmentPointer(AssignmentFactory::unpackAssignment(receivedPacket));
|
_currentAssignment = SharedAssignmentPointer(AssignmentFactory::unpackAssignment(receivedPacket));
|
||||||
|
|
||||||
if (_currentAssignment) {
|
if (_currentAssignment) {
|
||||||
qDebug() << "Received an assignment -" << *_currentAssignment;
|
qDebug() << "Received an assignment -" << *_currentAssignment;
|
||||||
|
|
||||||
// switch our DomainHandler hostname and port to whoever sent us the assignment
|
// switch our DomainHandler hostname and port to whoever sent us the assignment
|
||||||
|
|
||||||
nodeList->getDomainHandler().setSockAddr(senderSockAddr, _assignmentServerHostname);
|
nodeList->getDomainHandler().setSockAddr(senderSockAddr, _assignmentServerHostname);
|
||||||
nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID());
|
nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID());
|
||||||
|
|
||||||
qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
|
qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
|
||||||
|
|
||||||
// start the deployed assignment
|
// start the deployed assignment
|
||||||
AssignmentThread* workerThread = new AssignmentThread(_currentAssignment, this);
|
AssignmentThread* workerThread = new AssignmentThread(_currentAssignment, this);
|
||||||
|
|
||||||
connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run);
|
connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run);
|
||||||
connect(_currentAssignment.data(), &ThreadedAssignment::finished, workerThread, &QThread::quit);
|
connect(_currentAssignment.data(), &ThreadedAssignment::finished, workerThread, &QThread::quit);
|
||||||
connect(_currentAssignment.data(), &ThreadedAssignment::finished,
|
connect(_currentAssignment.data(), &ThreadedAssignment::finished,
|
||||||
this, &AssignmentClient::assignmentCompleted);
|
this, &AssignmentClient::assignmentCompleted);
|
||||||
connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
|
connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
|
||||||
|
|
||||||
_currentAssignment->moveToThread(workerThread);
|
_currentAssignment->moveToThread(workerThread);
|
||||||
|
|
||||||
// move the NodeList to the thread used for the _current assignment
|
// move the NodeList to the thread used for the _current assignment
|
||||||
nodeList->moveToThread(workerThread);
|
nodeList->moveToThread(workerThread);
|
||||||
|
|
||||||
// let the assignment handle the incoming datagrams for its duration
|
// let the assignment handle the incoming datagrams for its duration
|
||||||
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
|
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
|
||||||
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment.data(),
|
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment.data(),
|
||||||
&ThreadedAssignment::readPendingDatagrams);
|
&ThreadedAssignment::readPendingDatagrams);
|
||||||
|
|
||||||
// Starts an event loop, and emits workerThread->started()
|
// Starts an event loop, and emits workerThread->started()
|
||||||
workerThread->start();
|
workerThread->start();
|
||||||
} else {
|
} else {
|
||||||
|
@ -180,15 +173,15 @@ void AssignmentClient::readPendingDatagrams() {
|
||||||
void AssignmentClient::handleAuthenticationRequest() {
|
void AssignmentClient::handleAuthenticationRequest() {
|
||||||
const QString DATA_SERVER_USERNAME_ENV = "HIFI_AC_USERNAME";
|
const QString DATA_SERVER_USERNAME_ENV = "HIFI_AC_USERNAME";
|
||||||
const QString DATA_SERVER_PASSWORD_ENV = "HIFI_AC_PASSWORD";
|
const QString DATA_SERVER_PASSWORD_ENV = "HIFI_AC_PASSWORD";
|
||||||
|
|
||||||
// this node will be using an authentication server, let's make sure we have a username/password
|
// this node will be using an authentication server, let's make sure we have a username/password
|
||||||
QProcessEnvironment sysEnvironment = QProcessEnvironment::systemEnvironment();
|
QProcessEnvironment sysEnvironment = QProcessEnvironment::systemEnvironment();
|
||||||
|
|
||||||
QString username = sysEnvironment.value(DATA_SERVER_USERNAME_ENV);
|
QString username = sysEnvironment.value(DATA_SERVER_USERNAME_ENV);
|
||||||
QString password = sysEnvironment.value(DATA_SERVER_PASSWORD_ENV);
|
QString password = sysEnvironment.value(DATA_SERVER_PASSWORD_ENV);
|
||||||
|
|
||||||
AccountManager& accountManager = AccountManager::getInstance();
|
AccountManager& accountManager = AccountManager::getInstance();
|
||||||
|
|
||||||
if (!username.isEmpty() && !password.isEmpty()) {
|
if (!username.isEmpty() && !password.isEmpty()) {
|
||||||
// ask the account manager to log us in from the env variables
|
// ask the account manager to log us in from the env variables
|
||||||
accountManager.requestAccessToken(username, password);
|
accountManager.requestAccessToken(username, password);
|
||||||
|
@ -196,7 +189,7 @@ void AssignmentClient::handleAuthenticationRequest() {
|
||||||
qDebug() << "Authentication was requested against" << qPrintable(accountManager.getAuthURL().toString())
|
qDebug() << "Authentication was requested against" << qPrintable(accountManager.getAuthURL().toString())
|
||||||
<< "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV)
|
<< "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV)
|
||||||
<< "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Unable to authenticate.";
|
<< "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Unable to authenticate.";
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,15 +197,15 @@ void AssignmentClient::handleAuthenticationRequest() {
|
||||||
void AssignmentClient::assignmentCompleted() {
|
void AssignmentClient::assignmentCompleted() {
|
||||||
// reset the logging target to the the CHILD_TARGET_NAME
|
// reset the logging target to the the CHILD_TARGET_NAME
|
||||||
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||||
|
|
||||||
qDebug("Assignment finished or never started - waiting for new assignment.");
|
qDebug("Assignment finished or never started - waiting for new assignment.");
|
||||||
|
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
// have us handle incoming NodeList datagrams again
|
// have us handle incoming NodeList datagrams again
|
||||||
disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment.data(), 0);
|
disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment.data(), 0);
|
||||||
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
|
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
|
||||||
|
|
||||||
// clear our current assignment shared pointer now that we're done with it
|
// clear our current assignment shared pointer now that we're done with it
|
||||||
// if the assignment thread is still around it has its own shared pointer to the assignment
|
// if the assignment thread is still around it has its own shared pointer to the assignment
|
||||||
_currentAssignment.clear();
|
_currentAssignment.clear();
|
||||||
|
|
|
@ -335,7 +335,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) {
|
||||||
AudioMixerClientData* otherNodeClientData = (AudioMixerClientData*) otherNode->getLinkedData();
|
AudioMixerClientData* otherNodeClientData = (AudioMixerClientData*) otherNode->getLinkedData();
|
||||||
|
|
||||||
// enumerate the ARBs attached to the otherNode and add all that should be added to mix
|
// enumerate the ARBs attached to the otherNode and add all that should be added to mix
|
||||||
for (unsigned int i = 0; i < otherNodeClientData->getRingBuffers().size(); i++) {
|
for (int i = 0; i < otherNodeClientData->getRingBuffers().size(); i++) {
|
||||||
PositionalAudioRingBuffer* otherNodeBuffer = otherNodeClientData->getRingBuffers()[i];
|
PositionalAudioRingBuffer* otherNodeBuffer = otherNodeClientData->getRingBuffers()[i];
|
||||||
|
|
||||||
if ((*otherNode != *node
|
if ((*otherNode != *node
|
||||||
|
|
|
@ -25,14 +25,14 @@ AudioMixerClientData::AudioMixerClientData() :
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioMixerClientData::~AudioMixerClientData() {
|
AudioMixerClientData::~AudioMixerClientData() {
|
||||||
for (unsigned int i = 0; i < _ringBuffers.size(); i++) {
|
for (int i = 0; i < _ringBuffers.size(); i++) {
|
||||||
// delete this attached PositionalAudioRingBuffer
|
// delete this attached PositionalAudioRingBuffer
|
||||||
delete _ringBuffers[i];
|
delete _ringBuffers[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarAudioRingBuffer* AudioMixerClientData::getAvatarAudioRingBuffer() const {
|
AvatarAudioRingBuffer* AudioMixerClientData::getAvatarAudioRingBuffer() const {
|
||||||
for (unsigned int i = 0; i < _ringBuffers.size(); i++) {
|
for (int i = 0; i < _ringBuffers.size(); i++) {
|
||||||
if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Microphone) {
|
if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Microphone) {
|
||||||
return (AvatarAudioRingBuffer*) _ringBuffers[i];
|
return (AvatarAudioRingBuffer*) _ringBuffers[i];
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
|
||||||
|
|
||||||
InjectedAudioRingBuffer* matchingInjectedRingBuffer = NULL;
|
InjectedAudioRingBuffer* matchingInjectedRingBuffer = NULL;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < _ringBuffers.size(); i++) {
|
for (int i = 0; i < _ringBuffers.size(); i++) {
|
||||||
if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector
|
if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector
|
||||||
&& ((InjectedAudioRingBuffer*) _ringBuffers[i])->getStreamIdentifier() == streamIdentifier) {
|
&& ((InjectedAudioRingBuffer*) _ringBuffers[i])->getStreamIdentifier() == streamIdentifier) {
|
||||||
matchingInjectedRingBuffer = (InjectedAudioRingBuffer*) _ringBuffers[i];
|
matchingInjectedRingBuffer = (InjectedAudioRingBuffer*) _ringBuffers[i];
|
||||||
|
@ -99,7 +99,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSamples) {
|
void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSamples) {
|
||||||
for (unsigned int i = 0; i < _ringBuffers.size(); i++) {
|
for (int i = 0; i < _ringBuffers.size(); i++) {
|
||||||
if (_ringBuffers[i]->shouldBeAddedToMix(jitterBufferLengthSamples)) {
|
if (_ringBuffers[i]->shouldBeAddedToMix(jitterBufferLengthSamples)) {
|
||||||
// this is a ring buffer that is ready to go
|
// this is a ring buffer that is ready to go
|
||||||
// set its flag so we know to push its buffer when all is said and done
|
// set its flag so we know to push its buffer when all is said and done
|
||||||
|
@ -113,7 +113,7 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSam
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixerClientData::pushBuffersAfterFrameSend() {
|
void AudioMixerClientData::pushBuffersAfterFrameSend() {
|
||||||
for (unsigned int i = 0; i < _ringBuffers.size(); i++) {
|
for (int i = 0; i < _ringBuffers.size(); i++) {
|
||||||
// this was a used buffer, push the output pointer forwards
|
// this was a used buffer, push the output pointer forwards
|
||||||
PositionalAudioRingBuffer* audioBuffer = _ringBuffers[i];
|
PositionalAudioRingBuffer* audioBuffer = _ringBuffers[i];
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,6 +5,19 @@
|
||||||
// Created by Clément Brisset on 4/24/14.
|
// Created by Clément Brisset on 4/24/14.
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
|
// This script allows you to edit models either with the razor hydras or with your mouse
|
||||||
|
//
|
||||||
|
// If using the hydras :
|
||||||
|
// grab grab models with the triggers, you can then move the models around or scale them with both hands.
|
||||||
|
// You can switch mode using the bumpers so that you can move models roud more easily.
|
||||||
|
//
|
||||||
|
// If using the mouse :
|
||||||
|
// - left click lets you move the model in the plane facing you.
|
||||||
|
// If pressing shift, it will move on the horizontale plane it's in.
|
||||||
|
// - right click lets you rotate the model. z and x give you access to more axix of rotation while shift allows for finer control.
|
||||||
|
// - left + right click lets you scale the model.
|
||||||
|
// - you can press r while holding the model to reset its rotation
|
||||||
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
@ -21,6 +34,8 @@ var LASER_COLOR = { red: 255, green: 0, blue: 0 };
|
||||||
var LASER_LENGTH_FACTOR = 500
|
var LASER_LENGTH_FACTOR = 500
|
||||||
;
|
;
|
||||||
|
|
||||||
|
var MAX_ANGULAR_SIZE = 45;
|
||||||
|
|
||||||
var LEFT = 0;
|
var LEFT = 0;
|
||||||
var RIGHT = 1;
|
var RIGHT = 1;
|
||||||
|
|
||||||
|
@ -263,6 +278,11 @@ function controller(wichSide) {
|
||||||
var d = Vec3.length(Vec3.subtract(P, X));
|
var d = Vec3.length(Vec3.subtract(P, X));
|
||||||
|
|
||||||
if (0 < x && x < LASER_LENGTH_FACTOR) {
|
if (0 < x && x < LASER_LENGTH_FACTOR) {
|
||||||
|
if (2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14 > MAX_ANGULAR_SIZE) {
|
||||||
|
print("Angular size too big: " + 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14);
|
||||||
|
return { valid: false };
|
||||||
|
}
|
||||||
|
|
||||||
return { valid: true, x: x, y: y, z: z };
|
return { valid: true, x: x, y: y, z: z };
|
||||||
}
|
}
|
||||||
return { valid: false };
|
return { valid: false };
|
||||||
|
@ -513,9 +533,13 @@ function controller(wichSide) {
|
||||||
if (isLocked(newProperties)) {
|
if (isLocked(newProperties)) {
|
||||||
print("Model locked " + newProperties.id);
|
print("Model locked " + newProperties.id);
|
||||||
} else {
|
} else {
|
||||||
|
var check = this.checkModel(newProperties);
|
||||||
|
if (!check.valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.grab(newModel, newProperties);
|
this.grab(newModel, newProperties);
|
||||||
|
|
||||||
var check = this.checkModel(newProperties);
|
|
||||||
this.x = check.x;
|
this.x = check.x;
|
||||||
this.y = check.y;
|
this.y = check.y;
|
||||||
this.z = check.z;
|
this.z = check.z;
|
||||||
|
@ -653,16 +677,18 @@ function initToolBar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveOverlays() {
|
function moveOverlays() {
|
||||||
|
var newViewPort = Controller.getViewportDimensions();
|
||||||
|
|
||||||
if (typeof(toolBar) === 'undefined') {
|
if (typeof(toolBar) === 'undefined') {
|
||||||
initToolBar();
|
initToolBar();
|
||||||
|
|
||||||
} else if (windowDimensions.x == Controller.getViewportDimensions().x &&
|
} else if (windowDimensions.x == newViewPort.x &&
|
||||||
windowDimensions.y == Controller.getViewportDimensions().y) {
|
windowDimensions.y == newViewPort.y) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
windowDimensions = Controller.getViewportDimensions();
|
windowDimensions = newViewPort;
|
||||||
var toolsX = windowDimensions.x - 8 - toolBar.width;
|
var toolsX = windowDimensions.x - 8 - toolBar.width;
|
||||||
var toolsY = (windowDimensions.y - toolBar.height) / 2;
|
var toolsY = (windowDimensions.y - toolBar.height) / 2;
|
||||||
|
|
||||||
|
@ -680,8 +706,6 @@ var intersection;
|
||||||
|
|
||||||
|
|
||||||
var SCALE_FACTOR = 200.0;
|
var SCALE_FACTOR = 200.0;
|
||||||
var TRANSLATION_FACTOR = 100.0;
|
|
||||||
var ROTATION_FACTOR = 100.0;
|
|
||||||
|
|
||||||
function rayPlaneIntersection(pickRay, point, normal) {
|
function rayPlaneIntersection(pickRay, point, normal) {
|
||||||
var d = -Vec3.dot(point, normal);
|
var d = -Vec3.dot(point, normal);
|
||||||
|
@ -690,7 +714,53 @@ function rayPlaneIntersection(pickRay, point, normal) {
|
||||||
return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t));
|
return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Tooltip() {
|
||||||
|
this.x = 285;
|
||||||
|
this.y = 115;
|
||||||
|
this.width = 110;
|
||||||
|
this.height = 115 ;
|
||||||
|
this.margin = 5;
|
||||||
|
this.decimals = 3;
|
||||||
|
|
||||||
|
this.textOverlay = Overlays.addOverlay("text", {
|
||||||
|
x: this.x,
|
||||||
|
y: this.y,
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
margin: this.margin,
|
||||||
|
text: "",
|
||||||
|
color: { red: 128, green: 128, blue: 128 },
|
||||||
|
alpha: 0.2,
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
this.show = function(doShow) {
|
||||||
|
Overlays.editOverlay(this.textOverlay, { visible: doShow });
|
||||||
|
}
|
||||||
|
this.updateText = function(properties) {
|
||||||
|
var angles = Quat.safeEulerAngles(properties.modelRotation);
|
||||||
|
var text = "Model Properties:\n"
|
||||||
|
text += "x: " + properties.position.x.toFixed(this.decimals) + "\n"
|
||||||
|
text += "y: " + properties.position.y.toFixed(this.decimals) + "\n"
|
||||||
|
text += "z: " + properties.position.z.toFixed(this.decimals) + "\n"
|
||||||
|
text += "pitch: " + angles.x.toFixed(this.decimals) + "\n"
|
||||||
|
text += "yaw: " + angles.y.toFixed(this.decimals) + "\n"
|
||||||
|
text += "roll: " + angles.z.toFixed(this.decimals) + "\n"
|
||||||
|
text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n"
|
||||||
|
|
||||||
|
Overlays.editOverlay(this.textOverlay, { text: text });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cleanup = function() {
|
||||||
|
Overlays.deleteOverlay(this.textOverlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var tooltip = new Tooltip();
|
||||||
|
|
||||||
function mousePressEvent(event) {
|
function mousePressEvent(event) {
|
||||||
|
if (event.isAlt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mouseLastPosition = { x: event.x, y: event.y };
|
mouseLastPosition = { x: event.x, y: event.y };
|
||||||
modelSelected = false;
|
modelSelected = false;
|
||||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||||
|
@ -756,12 +826,16 @@ function mousePressEvent(event) {
|
||||||
var d = Vec3.length(Vec3.subtract(P, X));
|
var d = Vec3.length(Vec3.subtract(P, X));
|
||||||
|
|
||||||
if (0 < x && x < LASER_LENGTH_FACTOR) {
|
if (0 < x && x < LASER_LENGTH_FACTOR) {
|
||||||
modelSelected = true;
|
if (2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14 < MAX_ANGULAR_SIZE) {
|
||||||
selectedModelID = foundModel;
|
modelSelected = true;
|
||||||
selectedModelProperties = properties;
|
selectedModelID = foundModel;
|
||||||
|
selectedModelProperties = properties;
|
||||||
orientation = MyAvatar.orientation;
|
|
||||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
orientation = MyAvatar.orientation;
|
||||||
|
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
||||||
|
} else {
|
||||||
|
print("Angular size too big: " + 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -782,6 +856,8 @@ function mousePressEvent(event) {
|
||||||
selectedModelProperties.glowLevel = 0.0;
|
selectedModelProperties.glowLevel = 0.0;
|
||||||
|
|
||||||
print("Clicked on " + selectedModelID.id + " " + modelSelected);
|
print("Clicked on " + selectedModelID.id + " " + modelSelected);
|
||||||
|
tooltip.updateText(selectedModelProperties);
|
||||||
|
tooltip.show(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,6 +866,10 @@ var oldModifier = 0;
|
||||||
var modifier = 0;
|
var modifier = 0;
|
||||||
var wasShifted = false;
|
var wasShifted = false;
|
||||||
function mouseMoveEvent(event) {
|
function mouseMoveEvent(event) {
|
||||||
|
if (event.isAlt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
|
|
||||||
if (!modelSelected) {
|
if (!modelSelected) {
|
||||||
|
@ -878,22 +958,54 @@ function mouseMoveEvent(event) {
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
// Let's rotate
|
// Let's rotate
|
||||||
var rotation = Quat.fromVec3Degrees({ x: event.y - mouseLastPosition.y, y: event.x - mouseLastPosition.x, z: 0 });
|
if (somethingChanged) {
|
||||||
if (event.isShifted) {
|
selectedModelProperties.oldRotation.x = selectedModelProperties.modelRotation.x;
|
||||||
rotation = Quat.fromVec3Degrees({ x: event.y - mouseLastPosition.y, y: 0, z: mouseLastPosition.x - event.x });
|
selectedModelProperties.oldRotation.y = selectedModelProperties.modelRotation.y;
|
||||||
|
selectedModelProperties.oldRotation.z = selectedModelProperties.modelRotation.z;
|
||||||
|
selectedModelProperties.oldRotation.w = selectedModelProperties.modelRotation.w;
|
||||||
|
mouseLastPosition.x = event.x;
|
||||||
|
mouseLastPosition.y = event.y;
|
||||||
|
somethingChanged = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newRotation = Quat.multiply(orientation, rotation);
|
|
||||||
newRotation = Quat.multiply(newRotation, Quat.inverse(orientation));
|
|
||||||
|
|
||||||
selectedModelProperties.modelRotation = Quat.multiply(newRotation, selectedModelProperties.oldRotation);
|
var pixelPerDegrees = windowDimensions.y / (1 * 360); // the entire height of the window allow you to make 2 full rotations
|
||||||
|
|
||||||
|
var STEP = 15;
|
||||||
|
var delta = Math.floor((event.x - mouseLastPosition.x) / pixelPerDegrees);
|
||||||
|
|
||||||
|
if (!event.isShifted) {
|
||||||
|
delta = Math.floor(delta / STEP) * STEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rotation = Quat.fromVec3Degrees({
|
||||||
|
x: (!zIsPressed && xIsPressed) ? delta : 0, // z is pressed
|
||||||
|
y: (!zIsPressed && !xIsPressed) ? delta : 0, // x is pressed
|
||||||
|
z: (zIsPressed && !xIsPressed) ? delta : 0 // neither is pressed
|
||||||
|
});
|
||||||
|
rotation = Quat.multiply(selectedModelProperties.oldRotation, rotation);
|
||||||
|
|
||||||
|
selectedModelProperties.modelRotation.x = rotation.x;
|
||||||
|
selectedModelProperties.modelRotation.y = rotation.y;
|
||||||
|
selectedModelProperties.modelRotation.z = rotation.z;
|
||||||
|
selectedModelProperties.modelRotation.w = rotation.w;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Models.editModel(selectedModelID, selectedModelProperties);
|
Models.editModel(selectedModelID, selectedModelProperties);
|
||||||
|
tooltip.updateText(selectedModelProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function mouseReleaseEvent(event) {
|
function mouseReleaseEvent(event) {
|
||||||
|
if (event.isAlt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelSelected) {
|
||||||
|
tooltip.show(false);
|
||||||
|
}
|
||||||
|
|
||||||
modelSelected = false;
|
modelSelected = false;
|
||||||
|
|
||||||
glowedModelID.id = -1;
|
glowedModelID.id = -1;
|
||||||
|
@ -931,6 +1043,7 @@ function scriptEnding() {
|
||||||
rightController.cleanup();
|
rightController.cleanup();
|
||||||
toolBar.cleanup();
|
toolBar.cleanup();
|
||||||
cleanupModelMenus();
|
cleanupModelMenus();
|
||||||
|
tooltip.cleanup();
|
||||||
}
|
}
|
||||||
Script.scriptEnding.connect(scriptEnding);
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
|
||||||
|
@ -963,3 +1076,35 @@ Menu.menuItemEvent.connect(function(menuItem){
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// handling of inspect.js concurrence
|
||||||
|
var zIsPressed = false;
|
||||||
|
var xIsPressed = false;
|
||||||
|
var somethingChanged = false;
|
||||||
|
Controller.keyPressEvent.connect(function(event) {
|
||||||
|
if ((event.text == "z" || event.text == "Z") && !zIsPressed) {
|
||||||
|
zIsPressed = true;
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
if ((event.text == "x" || event.text == "X") && !xIsPressed) {
|
||||||
|
xIsPressed = true;
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resets model orientation when holding with mouse
|
||||||
|
if (event.text == "r" && modelSelected) {
|
||||||
|
selectedModelProperties.modelRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 });
|
||||||
|
Models.editModel(selectedModelID, selectedModelProperties);
|
||||||
|
tooltip.updateText(selectedModelProperties);
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Controller.keyReleaseEvent.connect(function(event) {
|
||||||
|
if (event.text == "z" || event.text == "Z") {
|
||||||
|
zIsPressed = false;
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
if (event.text == "x" || event.text == "X") {
|
||||||
|
xIsPressed = false;
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
});
|
|
@ -19,8 +19,8 @@
|
||||||
var damping = 0.9;
|
var damping = 0.9;
|
||||||
var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z };
|
var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z };
|
||||||
var joysticksCaptured = false;
|
var joysticksCaptured = false;
|
||||||
var THRUST_CONTROLLER = 1;
|
var THRUST_CONTROLLER = 0;
|
||||||
var VIEW_CONTROLLER = 0;
|
var VIEW_CONTROLLER = 1;
|
||||||
var INITIAL_THRUST_MULTPLIER = 1.0;
|
var INITIAL_THRUST_MULTPLIER = 1.0;
|
||||||
var THRUST_INCREASE_RATE = 1.05;
|
var THRUST_INCREASE_RATE = 1.05;
|
||||||
var MAX_THRUST_MULTIPLIER = 75.0;
|
var MAX_THRUST_MULTIPLIER = 75.0;
|
||||||
|
|
|
@ -34,6 +34,7 @@ var noMode = 0;
|
||||||
var orbitMode = 1;
|
var orbitMode = 1;
|
||||||
var radialMode = 2;
|
var radialMode = 2;
|
||||||
var panningMode = 3;
|
var panningMode = 3;
|
||||||
|
var detachedMode = 4;
|
||||||
|
|
||||||
var mode = noMode;
|
var mode = noMode;
|
||||||
|
|
||||||
|
@ -48,6 +49,9 @@ var radius = 0.0;
|
||||||
var azimuth = 0.0;
|
var azimuth = 0.0;
|
||||||
var altitude = 0.0;
|
var altitude = 0.0;
|
||||||
|
|
||||||
|
var avatarPosition;
|
||||||
|
var avatarOrientation;
|
||||||
|
|
||||||
|
|
||||||
function handleRadialMode(dx, dy) {
|
function handleRadialMode(dx, dy) {
|
||||||
azimuth += dx / AZIMUTH_RATE;
|
azimuth += dx / AZIMUTH_RATE;
|
||||||
|
@ -108,7 +112,7 @@ function restoreCameraState() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleModes() {
|
function handleModes() {
|
||||||
var newMode = noMode;
|
var newMode = (mode == noMode) ? noMode : detachedMode;
|
||||||
if (alt) {
|
if (alt) {
|
||||||
if (control) {
|
if (control) {
|
||||||
if (shift) {
|
if (shift) {
|
||||||
|
@ -121,6 +125,22 @@ function handleModes() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if entering detachMode
|
||||||
|
if (newMode == detachedMode && mode != detachedMode) {
|
||||||
|
avatarPosition = MyAvatar.position;
|
||||||
|
avatarOrientation = MyAvatar.orientation;
|
||||||
|
}
|
||||||
|
// if leaving detachMode
|
||||||
|
if (mode == detachedMode && newMode == detachedMode &&
|
||||||
|
(avatarPosition.x != MyAvatar.position.x ||
|
||||||
|
avatarPosition.y != MyAvatar.position.y ||
|
||||||
|
avatarPosition.z != MyAvatar.position.z ||
|
||||||
|
avatarOrientation.x != MyAvatar.orientation.x ||
|
||||||
|
avatarOrientation.y != MyAvatar.orientation.y ||
|
||||||
|
avatarOrientation.z != MyAvatar.orientation.z ||
|
||||||
|
avatarOrientation.w != MyAvatar.orientation.w)) {
|
||||||
|
newMode = noMode;
|
||||||
|
}
|
||||||
// if leaving noMode
|
// if leaving noMode
|
||||||
if (mode == noMode && newMode != noMode) {
|
if (mode == noMode && newMode != noMode) {
|
||||||
saveCameraState();
|
saveCameraState();
|
||||||
|
@ -177,30 +197,45 @@ function keyReleaseEvent(event) {
|
||||||
|
|
||||||
function mousePressEvent(event) {
|
function mousePressEvent(event) {
|
||||||
if (alt && !isActive) {
|
if (alt && !isActive) {
|
||||||
isActive = true;
|
|
||||||
mouseLastX = event.x;
|
mouseLastX = event.x;
|
||||||
mouseLastY = event.y;
|
mouseLastY = event.y;
|
||||||
|
|
||||||
// Compute trajectories related values
|
// Compute trajectories related values
|
||||||
var pickRay = Camera.computePickRay(mouseLastX, mouseLastY);
|
var pickRay = Camera.computePickRay(mouseLastX, mouseLastY);
|
||||||
var intersection = Voxels.findRayIntersection(pickRay);
|
var voxelIntersection = Voxels.findRayIntersection(pickRay);
|
||||||
|
var modelIntersection = Models.findRayIntersection(pickRay);
|
||||||
|
|
||||||
position = Camera.getPosition();
|
position = Camera.getPosition();
|
||||||
|
|
||||||
avatarTarget = MyAvatar.getTargetAvatarPosition();
|
var avatarTarget = MyAvatar.getTargetAvatarPosition();
|
||||||
voxelTarget = intersection.intersection;
|
var voxelTarget = voxelIntersection.intersection;
|
||||||
if (Vec3.length(Vec3.subtract(avatarTarget, position)) < Vec3.length(Vec3.subtract(voxelTarget, position))) {
|
|
||||||
if (avatarTarget.x != 0 || avatarTarget.y != 0 || avatarTarget.z != 0) {
|
|
||||||
center = avatarTarget;
|
var distance = -1;
|
||||||
} else {
|
var string;
|
||||||
center = voxelTarget;
|
|
||||||
}
|
if (modelIntersection.intersects && modelIntersection.accurate) {
|
||||||
} else {
|
distance = modelIntersection.distance;
|
||||||
if (voxelTarget.x != 0 || voxelTarget.y != 0 || voxelTarget.z != 0) {
|
center = modelIntersection.modelProperties.position;
|
||||||
center = voxelTarget;
|
string = "Inspecting model";
|
||||||
} else {
|
}
|
||||||
center = avatarTarget;
|
|
||||||
}
|
if ((distance == -1 || Vec3.length(Vec3.subtract(avatarTarget, position)) < distance) &&
|
||||||
|
(avatarTarget.x != 0 || avatarTarget.y != 0 || avatarTarget.z != 0)) {
|
||||||
|
distance = Vec3.length(Vec3.subtract(avatarTarget, position));
|
||||||
|
center = avatarTarget;
|
||||||
|
string = "Inspecting avatar";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((distance == -1 || Vec3.length(Vec3.subtract(voxelTarget, position)) < distance) &&
|
||||||
|
(voxelTarget.x != 0 || voxelTarget.y != 0 || voxelTarget.z != 0)) {
|
||||||
|
distance = Vec3.length(Vec3.subtract(voxelTarget, position));
|
||||||
|
center = voxelTarget;
|
||||||
|
string = "Inspecting voxel";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distance == -1) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector = Vec3.subtract(position, center);
|
vector = Vec3.subtract(position, center);
|
||||||
|
@ -209,6 +244,8 @@ function mousePressEvent(event) {
|
||||||
altitude = Math.asin(vector.y / Vec3.length(vector));
|
altitude = Math.asin(vector.y / Vec3.length(vector));
|
||||||
|
|
||||||
Camera.keepLookingAt(center);
|
Camera.keepLookingAt(center);
|
||||||
|
print(string);
|
||||||
|
isActive = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,6 +272,10 @@ function mouseMoveEvent(event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
handleModes();
|
||||||
|
}
|
||||||
|
|
||||||
function scriptEnding() {
|
function scriptEnding() {
|
||||||
if (mode != noMode) {
|
if (mode != noMode) {
|
||||||
restoreCameraState();
|
restoreCameraState();
|
||||||
|
@ -248,4 +289,5 @@ Controller.mousePressEvent.connect(mousePressEvent);
|
||||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||||
|
|
||||||
|
Script.update.connect(update);
|
||||||
Script.scriptEnding.connect(scriptEnding);
|
Script.scriptEnding.connect(scriptEnding);
|
119
examples/myBalance.js
Normal file
119
examples/myBalance.js
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
//
|
||||||
|
// myBalance.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Stojce Slavkovski on June 5, 2014
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Show wallet ₵ balance
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
var Controller = Controller || {};
|
||||||
|
var Overlays = Overlays || {};
|
||||||
|
var Script = Script || {};
|
||||||
|
var Account = Account || {};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
var iconUrl = 'http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/',
|
||||||
|
overlayWidth = 150,
|
||||||
|
overlayHeight = 50,
|
||||||
|
overlayTopOffset = 15,
|
||||||
|
overlayRightOffset = 140,
|
||||||
|
textRightOffset = 105,
|
||||||
|
maxIntegers = 5,
|
||||||
|
downColor = {
|
||||||
|
red: 0,
|
||||||
|
green: 0,
|
||||||
|
blue: 255
|
||||||
|
},
|
||||||
|
upColor = {
|
||||||
|
red: 0,
|
||||||
|
green: 255,
|
||||||
|
blue: 0
|
||||||
|
},
|
||||||
|
normalColor = {
|
||||||
|
red: 204,
|
||||||
|
green: 204,
|
||||||
|
blue: 204
|
||||||
|
},
|
||||||
|
balance = -1,
|
||||||
|
walletBox = Overlays.addOverlay("image", {
|
||||||
|
x: 0,
|
||||||
|
y: overlayTopOffset,
|
||||||
|
width: 122,
|
||||||
|
height: 32,
|
||||||
|
imageURL: iconUrl + "wallet.svg",
|
||||||
|
alpha: 1
|
||||||
|
}),
|
||||||
|
textOverlay = Overlays.addOverlay("text", {
|
||||||
|
x: 0,
|
||||||
|
y: overlayTopOffset,
|
||||||
|
topMargin: 10,
|
||||||
|
font: {
|
||||||
|
size: 16
|
||||||
|
},
|
||||||
|
color: normalColor
|
||||||
|
});
|
||||||
|
|
||||||
|
function scriptEnding() {
|
||||||
|
Overlays.deleteOverlay(walletBox);
|
||||||
|
Overlays.deleteOverlay(textOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(deltaTime) {
|
||||||
|
var xPos = Controller.getViewportDimensions().x;
|
||||||
|
Overlays.editOverlay(walletBox, {
|
||||||
|
x: xPos - overlayRightOffset,
|
||||||
|
visible: Account.isLoggedIn()
|
||||||
|
});
|
||||||
|
|
||||||
|
Overlays.editOverlay(textOverlay, {
|
||||||
|
x: xPos - textRightOffset,
|
||||||
|
visible: Account.isLoggedIn()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatedBalance() {
|
||||||
|
var integers = balance.toFixed(0).length,
|
||||||
|
decimals = Math.abs(maxIntegers - integers) + 2;
|
||||||
|
|
||||||
|
var x = balance.toFixed(decimals).split('.'),
|
||||||
|
x1 = x[0],
|
||||||
|
x2 = x.length > 1 ? '.' + x[1] : '';
|
||||||
|
var rgx = /(\d+)(\d{3})/;
|
||||||
|
while (rgx.test(x1)) {
|
||||||
|
x1 = x1.replace(rgx, '$1' + ',' + '$2');
|
||||||
|
}
|
||||||
|
return x1 + x2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBalance(newBalance) {
|
||||||
|
if (balance === newBalance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var change = newBalance - balance,
|
||||||
|
textColor = change < 0 ? downColor : upColor;
|
||||||
|
|
||||||
|
balance = newBalance;
|
||||||
|
Overlays.editOverlay(textOverlay, {
|
||||||
|
text: formatedBalance(),
|
||||||
|
color: textColor
|
||||||
|
});
|
||||||
|
|
||||||
|
Script.setTimeout(function () {
|
||||||
|
Overlays.editOverlay(textOverlay, {
|
||||||
|
color: normalColor
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBalance(Account.getBalance());
|
||||||
|
Account.balanceChanged.connect(updateBalance);
|
||||||
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
Script.update.connect(update);
|
||||||
|
}());
|
BIN
interface/resources/images/sixense-reticle.png
Normal file
BIN
interface/resources/images/sixense-reticle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
|
@ -73,6 +73,7 @@
|
||||||
#include "devices/TV3DManager.h"
|
#include "devices/TV3DManager.h"
|
||||||
#include "renderer/ProgramObject.h"
|
#include "renderer/ProgramObject.h"
|
||||||
|
|
||||||
|
#include "scripting/AccountScriptingInterface.h"
|
||||||
#include "scripting/AudioDeviceScriptingInterface.h"
|
#include "scripting/AudioDeviceScriptingInterface.h"
|
||||||
#include "scripting/ClipboardScriptingInterface.h"
|
#include "scripting/ClipboardScriptingInterface.h"
|
||||||
#include "scripting/MenuScriptingInterface.h"
|
#include "scripting/MenuScriptingInterface.h"
|
||||||
|
@ -150,6 +151,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_mouseX(0),
|
_mouseX(0),
|
||||||
_mouseY(0),
|
_mouseY(0),
|
||||||
_lastMouseMove(usecTimestampNow()),
|
_lastMouseMove(usecTimestampNow()),
|
||||||
|
_lastMouseMoveType(QEvent::MouseMove),
|
||||||
_mouseHidden(false),
|
_mouseHidden(false),
|
||||||
_seenMouseMove(false),
|
_seenMouseMove(false),
|
||||||
_touchAvgX(0.0f),
|
_touchAvgX(0.0f),
|
||||||
|
@ -246,7 +248,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
|
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
|
||||||
connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer)));
|
connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer)));
|
||||||
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer)));
|
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer)));
|
||||||
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_octreeProcessor, SLOT(nodeKilled(SharedNodePointer)));
|
|
||||||
connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle);
|
connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle);
|
||||||
connect(nodeList, SIGNAL(uuidChanged(const QUuid&)), _myAvatar, SLOT(setSessionUUID(const QUuid&)));
|
connect(nodeList, SIGNAL(uuidChanged(const QUuid&)), _myAvatar, SLOT(setSessionUUID(const QUuid&)));
|
||||||
connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset);
|
connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset);
|
||||||
|
@ -656,7 +657,14 @@ void Application::paintGL() {
|
||||||
|
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("paintGL/renderOverlay");
|
PerformanceTimer perfTimer("paintGL/renderOverlay");
|
||||||
_applicationOverlay.renderOverlay();
|
//If alpha is 1, we can render directly to the screen.
|
||||||
|
if (_applicationOverlay.getAlpha() == 1.0f) {
|
||||||
|
_applicationOverlay.renderOverlay();
|
||||||
|
} else {
|
||||||
|
//Render to to texture so we can fade it
|
||||||
|
_applicationOverlay.renderOverlay(true);
|
||||||
|
_applicationOverlay.displayOverlayTexture();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,6 +1100,16 @@ void Application::focusOutEvent(QFocusEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::mouseMoveEvent(QMouseEvent* event) {
|
void Application::mouseMoveEvent(QMouseEvent* event) {
|
||||||
|
|
||||||
|
bool showMouse = true;
|
||||||
|
// If this mouse move event is emitted by a controller, dont show the mouse cursor
|
||||||
|
if (event->type() == CONTROLLER_MOVE_EVENT) {
|
||||||
|
showMouse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by application overlay to determine how to draw cursor(s)
|
||||||
|
_lastMouseMoveType = event->type();
|
||||||
|
|
||||||
_controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts
|
_controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts
|
||||||
|
|
||||||
// if one of our scripts have asked to capture this event, then stop processing it
|
// if one of our scripts have asked to capture this event, then stop processing it
|
||||||
|
@ -1099,10 +1117,9 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_lastMouseMove = usecTimestampNow();
|
_lastMouseMove = usecTimestampNow();
|
||||||
|
|
||||||
if (_mouseHidden && !OculusManager::isConnected()) {
|
if (_mouseHidden && showMouse && !OculusManager::isConnected()) {
|
||||||
getGLWidget()->setCursor(Qt::ArrowCursor);
|
getGLWidget()->setCursor(Qt::ArrowCursor);
|
||||||
_mouseHidden = false;
|
_mouseHidden = false;
|
||||||
_seenMouseMove = true;
|
_seenMouseMove = true;
|
||||||
|
@ -1372,6 +1389,9 @@ void Application::setEnable3DTVMode(bool enable3DTVMode) {
|
||||||
resizeGL(_glWidget->width(),_glWidget->height());
|
resizeGL(_glWidget->width(),_glWidget->height());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::setEnableVRMode(bool enableVRMode) {
|
||||||
|
resizeGL(_glWidget->width(), _glWidget->height());
|
||||||
|
}
|
||||||
|
|
||||||
void Application::setRenderVoxels(bool voxelRender) {
|
void Application::setRenderVoxels(bool voxelRender) {
|
||||||
_voxelEditSender.setShouldSend(voxelRender);
|
_voxelEditSender.setShouldSend(voxelRender);
|
||||||
|
@ -3251,6 +3271,11 @@ void Application::nodeAdded(SharedNodePointer node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::nodeKilled(SharedNodePointer node) {
|
void Application::nodeKilled(SharedNodePointer node) {
|
||||||
|
|
||||||
|
// this is here because connecting NodeList::nodeKilled to OctreePacketProcessor::nodeKilled doesn't work:
|
||||||
|
// OctreePacketProcessor::nodeKilled is not called when NodeList::nodeKilled is emitted for some reason.
|
||||||
|
_octreeProcessor.nodeKilled(node);
|
||||||
|
|
||||||
if (node->getType() == NodeType::VoxelServer) {
|
if (node->getType() == NodeType::VoxelServer) {
|
||||||
QUuid nodeUUID = node->getUUID();
|
QUuid nodeUUID = node->getUUID();
|
||||||
// see if this is the first we've heard of this node...
|
// see if this is the first we've heard of this node...
|
||||||
|
@ -3508,12 +3533,13 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
|
||||||
} else {
|
} else {
|
||||||
// start the script on a new thread...
|
// start the script on a new thread...
|
||||||
scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
|
scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
|
||||||
_scriptEnginesHash.insert(scriptURLString, scriptEngine);
|
|
||||||
|
|
||||||
if (!scriptEngine->hasScript()) {
|
if (!scriptEngine->hasScript()) {
|
||||||
qDebug() << "Application::loadScript(), script failed to load...";
|
qDebug() << "Application::loadScript(), script failed to load...";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_scriptEnginesHash.insert(scriptURLString, scriptEngine);
|
||||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3556,6 +3582,7 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
|
||||||
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
|
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
|
||||||
scriptEngine->registerGlobalObject("AnimationCache", &_animationCache);
|
scriptEngine->registerGlobalObject("AnimationCache", &_animationCache);
|
||||||
scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector);
|
scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector);
|
||||||
|
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
|
||||||
|
|
||||||
QThread* workerThread = new QThread(this);
|
QThread* workerThread = new QThread(this);
|
||||||
|
|
||||||
|
|
|
@ -206,6 +206,7 @@ public:
|
||||||
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
||||||
int getMouseX() const { return _mouseX; }
|
int getMouseX() const { return _mouseX; }
|
||||||
int getMouseY() const { return _mouseY; }
|
int getMouseY() const { return _mouseY; }
|
||||||
|
unsigned int getLastMouseMoveType() const { return _lastMouseMoveType; }
|
||||||
Faceplus* getFaceplus() { return &_faceplus; }
|
Faceplus* getFaceplus() { return &_faceplus; }
|
||||||
Faceshift* getFaceshift() { return &_faceshift; }
|
Faceshift* getFaceshift() { return &_faceshift; }
|
||||||
Visage* getVisage() { return &_visage; }
|
Visage* getVisage() { return &_visage; }
|
||||||
|
@ -345,6 +346,7 @@ private slots:
|
||||||
|
|
||||||
void setFullscreen(bool fullscreen);
|
void setFullscreen(bool fullscreen);
|
||||||
void setEnable3DTVMode(bool enable3DTVMode);
|
void setEnable3DTVMode(bool enable3DTVMode);
|
||||||
|
void setEnableVRMode(bool enableVRMode);
|
||||||
void cameraMenuChanged();
|
void cameraMenuChanged();
|
||||||
|
|
||||||
glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint);
|
glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint);
|
||||||
|
@ -505,6 +507,7 @@ private:
|
||||||
int _mouseDragStartedX;
|
int _mouseDragStartedX;
|
||||||
int _mouseDragStartedY;
|
int _mouseDragStartedY;
|
||||||
quint64 _lastMouseMove;
|
quint64 _lastMouseMove;
|
||||||
|
unsigned int _lastMouseMoveType;
|
||||||
bool _mouseHidden;
|
bool _mouseHidden;
|
||||||
bool _seenMouseMove;
|
bool _seenMouseMove;
|
||||||
|
|
||||||
|
@ -521,6 +524,7 @@ private:
|
||||||
|
|
||||||
QSet<int> _keysPressed;
|
QSet<int> _keysPressed;
|
||||||
|
|
||||||
|
|
||||||
GeometryCache _geometryCache;
|
GeometryCache _geometryCache;
|
||||||
AnimationCache _animationCache;
|
AnimationCache _animationCache;
|
||||||
TextureCache _textureCache;
|
TextureCache _textureCache;
|
||||||
|
|
|
@ -306,7 +306,7 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples,
|
||||||
} else {
|
} else {
|
||||||
// this is a 48 to 24 resampling but both source and destination are two channels
|
// this is a 48 to 24 resampling but both source and destination are two channels
|
||||||
// squish two samples into one in each channel
|
// squish two samples into one in each channel
|
||||||
for (int i = 0; i < numSourceSamples; i += 4) {
|
for (unsigned int i = 0; i < numSourceSamples; i += 4) {
|
||||||
destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 2] / 2);
|
destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 2] / 2);
|
||||||
destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] / 2) + (sourceSamples[i + 3] / 2);
|
destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] / 2) + (sourceSamples[i + 3] / 2);
|
||||||
}
|
}
|
||||||
|
@ -1419,7 +1419,7 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo)
|
||||||
// proportional to the accelerator ratio.
|
// proportional to the accelerator ratio.
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
const float Audio::CALLBACK_ACCELERATOR_RATIO = 0.4f;
|
const float Audio::CALLBACK_ACCELERATOR_RATIO = 0.1f;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include "ui/ModelsBrowser.h"
|
#include "ui/ModelsBrowser.h"
|
||||||
#include "ui/LoginDialog.h"
|
#include "ui/LoginDialog.h"
|
||||||
#include "ui/NodeBounds.h"
|
#include "ui/NodeBounds.h"
|
||||||
|
#include "devices/OculusManager.h"
|
||||||
|
|
||||||
|
|
||||||
Menu* Menu::_instance = NULL;
|
Menu* Menu::_instance = NULL;
|
||||||
|
@ -83,6 +84,7 @@ Menu::Menu() :
|
||||||
_audioJitterBufferSamples(0),
|
_audioJitterBufferSamples(0),
|
||||||
_bandwidthDialog(NULL),
|
_bandwidthDialog(NULL),
|
||||||
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
|
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
|
||||||
|
_realWorldFieldOfView(DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
|
||||||
_faceshiftEyeDeflection(DEFAULT_FACESHIFT_EYE_DEFLECTION),
|
_faceshiftEyeDeflection(DEFAULT_FACESHIFT_EYE_DEFLECTION),
|
||||||
_frustumDrawMode(FRUSTUM_DRAW_MODE_ALL),
|
_frustumDrawMode(FRUSTUM_DRAW_MODE_ALL),
|
||||||
_viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET),
|
_viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET),
|
||||||
|
@ -91,6 +93,9 @@ Menu::Menu() :
|
||||||
_lodToolsDialog(NULL),
|
_lodToolsDialog(NULL),
|
||||||
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
|
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
|
||||||
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
||||||
|
_oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE),
|
||||||
|
_sixenseReticleMoveSpeed(DEFAULT_SIXENSE_RETICLE_MOVE_SPEED),
|
||||||
|
_invertSixenseButtons(DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS),
|
||||||
_automaticAvatarLOD(true),
|
_automaticAvatarLOD(true),
|
||||||
_avatarLODDecreaseFPS(DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS),
|
_avatarLODDecreaseFPS(DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS),
|
||||||
_avatarLODIncreaseFPS(ADJUST_LOD_UP_FPS),
|
_avatarLODIncreaseFPS(ADJUST_LOD_UP_FPS),
|
||||||
|
@ -103,8 +108,10 @@ Menu::Menu() :
|
||||||
_fastFPSAverage(ONE_SECOND_OF_FRAMES),
|
_fastFPSAverage(ONE_SECOND_OF_FRAMES),
|
||||||
_loginAction(NULL),
|
_loginAction(NULL),
|
||||||
_preferencesDialog(NULL),
|
_preferencesDialog(NULL),
|
||||||
_snapshotsLocation(),
|
_scriptsLocation(),
|
||||||
_scriptsLocation() {
|
_loginDialog(NULL),
|
||||||
|
_snapshotsLocation()
|
||||||
|
{
|
||||||
Application *appInstance = Application::getInstance();
|
Application *appInstance = Application::getInstance();
|
||||||
|
|
||||||
QMenu* fileMenu = addMenu("File");
|
QMenu* fileMenu = addMenu("File");
|
||||||
|
@ -126,7 +133,7 @@ Menu::Menu() :
|
||||||
toggleLoginMenuItem();
|
toggleLoginMenuItem();
|
||||||
|
|
||||||
// connect to the appropriate slots of the AccountManager so that we can change the Login/Logout menu item
|
// connect to the appropriate slots of the AccountManager so that we can change the Login/Logout menu item
|
||||||
connect(&accountManager, &AccountManager::accessTokenChanged, this, &Menu::toggleLoginMenuItem);
|
connect(&accountManager, &AccountManager::profileChanged, this, &Menu::toggleLoginMenuItem);
|
||||||
connect(&accountManager, &AccountManager::logoutComplete, this, &Menu::toggleLoginMenuItem);
|
connect(&accountManager, &AccountManager::logoutComplete, this, &Menu::toggleLoginMenuItem);
|
||||||
|
|
||||||
addDisabledActionAndSeparator(fileMenu, "Scripts");
|
addDisabledActionAndSeparator(fileMenu, "Scripts");
|
||||||
|
@ -164,6 +171,8 @@ Menu::Menu() :
|
||||||
Qt::Key_At,
|
Qt::Key_At,
|
||||||
this,
|
this,
|
||||||
SLOT(goTo()));
|
SLOT(goTo()));
|
||||||
|
connect(&LocationManager::getInstance(), &LocationManager::multipleDestinationsFound,
|
||||||
|
this, &Menu::multipleDestinationsDecision);
|
||||||
|
|
||||||
addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model");
|
addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model");
|
||||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead()));
|
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead()));
|
||||||
|
@ -253,6 +262,11 @@ Menu::Menu() :
|
||||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false,
|
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false,
|
||||||
appInstance, SLOT(cameraMenuChanged()));
|
appInstance, SLOT(cameraMenuChanged()));
|
||||||
|
|
||||||
|
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::EnableVRMode, 0,
|
||||||
|
false,
|
||||||
|
appInstance,
|
||||||
|
SLOT(setEnableVRMode(bool)));
|
||||||
|
|
||||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0,
|
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0,
|
||||||
false,
|
false,
|
||||||
appInstance,
|
appInstance,
|
||||||
|
@ -320,7 +334,7 @@ Menu::Menu() :
|
||||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
|
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
|
||||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
|
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
|
||||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
|
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
|
||||||
|
|
||||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
|
||||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false);
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true);
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true);
|
||||||
|
@ -379,6 +393,9 @@ Menu::Menu() :
|
||||||
addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false);
|
addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::DisplayOculusOverlays, 0, true);
|
addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::DisplayOculusOverlays, 0, true);
|
||||||
|
|
||||||
|
QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options");
|
||||||
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true);
|
||||||
|
|
||||||
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");
|
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");
|
||||||
|
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu,
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu,
|
||||||
|
@ -397,7 +414,7 @@ Menu::Menu() :
|
||||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisableNackPackets, 0, false);
|
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisableNackPackets, 0, false);
|
||||||
|
|
||||||
addDisabledActionAndSeparator(developerMenu, "Testing");
|
addDisabledActionAndSeparator(developerMenu, "Testing");
|
||||||
|
|
||||||
QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools");
|
QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools");
|
||||||
QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true);
|
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true);
|
||||||
|
@ -452,7 +469,7 @@ Menu::Menu() :
|
||||||
false,
|
false,
|
||||||
appInstance->getAudio(),
|
appInstance->getAudio(),
|
||||||
SLOT(toggleToneInjection()));
|
SLOT(toggleToneInjection()));
|
||||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope,
|
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope,
|
||||||
Qt::CTRL | Qt::Key_P, false,
|
Qt::CTRL | Qt::Key_P, false,
|
||||||
appInstance->getAudio(),
|
appInstance->getAudio(),
|
||||||
SLOT(toggleScope()));
|
SLOT(toggleScope()));
|
||||||
|
@ -915,9 +932,11 @@ void sendFakeEnterEvent() {
|
||||||
const float DIALOG_RATIO_OF_WINDOW = 0.30f;
|
const float DIALOG_RATIO_OF_WINDOW = 0.30f;
|
||||||
|
|
||||||
void Menu::loginForCurrentDomain() {
|
void Menu::loginForCurrentDomain() {
|
||||||
LoginDialog* loginDialog = new LoginDialog(Application::getInstance()->getWindow());
|
if (!_loginDialog) {
|
||||||
loginDialog->show();
|
_loginDialog = new LoginDialog(Application::getInstance()->getWindow());
|
||||||
loginDialog->resizeAndPosition(false);
|
_loginDialog->show();
|
||||||
|
_loginDialog->resizeAndPosition(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::editPreferences() {
|
void Menu::editPreferences() {
|
||||||
|
@ -1062,9 +1081,7 @@ bool Menu::goToURL(QString location) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::goToUser(const QString& user) {
|
void Menu::goToUser(const QString& user) {
|
||||||
LocationManager* manager = &LocationManager::getInstance();
|
LocationManager::getInstance().goTo(user);
|
||||||
manager->goTo(user);
|
|
||||||
connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open a url, shortcutting any "hifi" scheme URLs to the local application.
|
/// Open a url, shortcutting any "hifi" scheme URLs to the local application.
|
||||||
|
@ -1086,13 +1103,10 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson
|
||||||
int userResponse = msgBox.exec();
|
int userResponse = msgBox.exec();
|
||||||
|
|
||||||
if (userResponse == QMessageBox::Ok) {
|
if (userResponse == QMessageBox::Ok) {
|
||||||
Application::getInstance()->getAvatar()->goToLocationFromResponse(userData);
|
Application::getInstance()->getAvatar()->goToLocationFromAddress(userData["address"].toObject());
|
||||||
} else if (userResponse == QMessageBox::Open) {
|
} else if (userResponse == QMessageBox::Open) {
|
||||||
Application::getInstance()->getAvatar()->goToLocationFromResponse(userData);
|
Application::getInstance()->getAvatar()->goToLocationFromAddress(placeData["address"].toObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
LocationManager* manager = reinterpret_cast<LocationManager*>(sender());
|
|
||||||
disconnect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::muteEnvironment() {
|
void Menu::muteEnvironment() {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "ui/PreferencesDialog.h"
|
#include "ui/PreferencesDialog.h"
|
||||||
#include "ui/ChatWindow.h"
|
#include "ui/ChatWindow.h"
|
||||||
#include "ui/JSConsole.h"
|
#include "ui/JSConsole.h"
|
||||||
|
#include "ui/LoginDialog.h"
|
||||||
#include "ui/ScriptEditorWindow.h"
|
#include "ui/ScriptEditorWindow.h"
|
||||||
|
|
||||||
const float ADJUST_LOD_DOWN_FPS = 40.0;
|
const float ADJUST_LOD_DOWN_FPS = 40.0;
|
||||||
|
@ -89,6 +90,12 @@ public:
|
||||||
void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; }
|
void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; }
|
||||||
float getRealWorldFieldOfView() const { return _realWorldFieldOfView; }
|
float getRealWorldFieldOfView() const { return _realWorldFieldOfView; }
|
||||||
void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; }
|
void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; }
|
||||||
|
float getOculusUIAngularSize() const { return _oculusUIAngularSize; }
|
||||||
|
void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; }
|
||||||
|
float getSixenseReticleMoveSpeed() const { return _sixenseReticleMoveSpeed; }
|
||||||
|
void setSixenseReticleMoveSpeed(float sixenseReticleMoveSpeed) { _sixenseReticleMoveSpeed = sixenseReticleMoveSpeed; }
|
||||||
|
bool getInvertSixenseButtons() const { return _invertSixenseButtons; }
|
||||||
|
void setInvertSixenseButtons(bool invertSixenseButtons) { _invertSixenseButtons = invertSixenseButtons; }
|
||||||
|
|
||||||
float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; }
|
float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; }
|
||||||
void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; }
|
void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; }
|
||||||
|
@ -260,6 +267,9 @@ private:
|
||||||
LodToolsDialog* _lodToolsDialog;
|
LodToolsDialog* _lodToolsDialog;
|
||||||
int _maxVoxels;
|
int _maxVoxels;
|
||||||
float _voxelSizeScale;
|
float _voxelSizeScale;
|
||||||
|
float _oculusUIAngularSize;
|
||||||
|
float _sixenseReticleMoveSpeed;
|
||||||
|
bool _invertSixenseButtons;
|
||||||
bool _automaticAvatarLOD;
|
bool _automaticAvatarLOD;
|
||||||
float _avatarLODDecreaseFPS;
|
float _avatarLODDecreaseFPS;
|
||||||
float _avatarLODIncreaseFPS;
|
float _avatarLODIncreaseFPS;
|
||||||
|
@ -276,6 +286,7 @@ private:
|
||||||
QPointer<PreferencesDialog> _preferencesDialog;
|
QPointer<PreferencesDialog> _preferencesDialog;
|
||||||
QPointer<AttachmentsDialog> _attachmentsDialog;
|
QPointer<AttachmentsDialog> _attachmentsDialog;
|
||||||
QPointer<AnimationsDialog> _animationsDialog;
|
QPointer<AnimationsDialog> _animationsDialog;
|
||||||
|
QPointer<LoginDialog> _loginDialog;
|
||||||
QAction* _chatAction;
|
QAction* _chatAction;
|
||||||
QString _snapshotsLocation;
|
QString _snapshotsLocation;
|
||||||
QString _scriptsLocation;
|
QString _scriptsLocation;
|
||||||
|
@ -341,6 +352,7 @@ namespace MenuOption {
|
||||||
const QString EchoLocalAudio = "Echo Local Audio";
|
const QString EchoLocalAudio = "Echo Local Audio";
|
||||||
const QString EchoServerAudio = "Echo Server Audio";
|
const QString EchoServerAudio = "Echo Server Audio";
|
||||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||||
|
const QString EnableVRMode = "Enable VR Mode";
|
||||||
const QString ExpandMiscAvatarTiming = "Expand Misc MyAvatar Timing";
|
const QString ExpandMiscAvatarTiming = "Expand Misc MyAvatar Timing";
|
||||||
const QString ExpandAvatarUpdateTiming = "Expand MyAvatar update Timing";
|
const QString ExpandAvatarUpdateTiming = "Expand MyAvatar update Timing";
|
||||||
const QString ExpandAvatarSimulateTiming = "Expand MyAvatar simulate Timing";
|
const QString ExpandAvatarSimulateTiming = "Expand MyAvatar simulate Timing";
|
||||||
|
@ -404,6 +416,7 @@ namespace MenuOption {
|
||||||
const QString SettingsExport = "Export Settings";
|
const QString SettingsExport = "Export Settings";
|
||||||
const QString SettingsImport = "Import Settings";
|
const QString SettingsImport = "Import Settings";
|
||||||
const QString SimpleShadows = "Simple";
|
const QString SimpleShadows = "Simple";
|
||||||
|
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
|
||||||
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
|
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
|
||||||
const QString ShowBordersModelNodes = "Show Model Nodes";
|
const QString ShowBordersModelNodes = "Show Model Nodes";
|
||||||
const QString ShowBordersParticleNodes = "Show Particle Nodes";
|
const QString ShowBordersParticleNodes = "Show Particle Nodes";
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
#include "ui/TextRenderer.h"
|
#include "ui/TextRenderer.h"
|
||||||
#include "VoxelConstants.h"
|
#include "VoxelConstants.h"
|
||||||
|
@ -409,8 +411,44 @@ void runTimingTests() {
|
||||||
|
|
||||||
float NSEC_TO_USEC = 1.0f / 1000.0f;
|
float NSEC_TO_USEC = 1.0f / 1000.0f;
|
||||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||||
qDebug("QElapsedTimer::nsecElapsed() usecs: %f", elapsedUsecs / (float) numTests);
|
qDebug("QElapsedTimer::nsecElapsed() usecs: %f", elapsedUsecs);
|
||||||
|
|
||||||
|
// Test sleep functions for accuracy
|
||||||
|
startTime.start();
|
||||||
|
QThread::msleep(1);
|
||||||
|
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||||
|
qDebug("QThread::msleep(1) ms: %f", elapsedUsecs / 1000.0f);
|
||||||
|
|
||||||
|
startTime.start();
|
||||||
|
QThread::sleep(1);
|
||||||
|
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||||
|
qDebug("QThread::sleep(1) ms: %f", elapsedUsecs / 1000.0f);
|
||||||
|
|
||||||
|
startTime.start();
|
||||||
|
usleep(1);
|
||||||
|
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||||
|
qDebug("usleep(1) ms: %f", elapsedUsecs / 1000.0f);
|
||||||
|
|
||||||
|
startTime.start();
|
||||||
|
usleep(10);
|
||||||
|
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||||
|
qDebug("usleep(10) ms: %f", elapsedUsecs / 1000.0f);
|
||||||
|
|
||||||
|
startTime.start();
|
||||||
|
usleep(100);
|
||||||
|
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||||
|
qDebug("usleep(100) ms: %f", elapsedUsecs / 1000.0f);
|
||||||
|
|
||||||
|
startTime.start();
|
||||||
|
usleep(1000);
|
||||||
|
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||||
|
qDebug("usleep(1000) ms: %f", elapsedUsecs / 1000.0f);
|
||||||
|
|
||||||
|
startTime.start();
|
||||||
|
usleep(15000);
|
||||||
|
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||||
|
qDebug("usleep(15000) ms: %f", elapsedUsecs / 1000.0f);
|
||||||
|
|
||||||
// Random number generation
|
// Random number generation
|
||||||
startTime.start();
|
startTime.start();
|
||||||
for (int i = 0; i < numTests; i++) {
|
for (int i = 0; i < numTests; i++) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ XmppClient::XmppClient() :
|
||||||
_xmppMUCManager()
|
_xmppMUCManager()
|
||||||
{
|
{
|
||||||
AccountManager& accountManager = AccountManager::getInstance();
|
AccountManager& accountManager = AccountManager::getInstance();
|
||||||
connect(&accountManager, SIGNAL(accessTokenChanged()), this, SLOT(connectToServer()));
|
connect(&accountManager, SIGNAL(profileChanged()), this, SLOT(connectToServer()));
|
||||||
connect(&accountManager, SIGNAL(logoutComplete()), this, SLOT(disconnectFromServer()));
|
connect(&accountManager, SIGNAL(logoutComplete()), this, SLOT(disconnectFromServer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1626,44 +1626,46 @@ void MyAvatar::resetSize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
|
void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
|
||||||
|
|
||||||
if (jsonObject["status"].toString() == "success") {
|
if (jsonObject["status"].toString() == "success") {
|
||||||
|
|
||||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
|
||||||
sendKillAvatar();
|
|
||||||
|
|
||||||
QJsonObject locationObject = jsonObject["data"].toObject()["address"].toObject();
|
QJsonObject locationObject = jsonObject["data"].toObject()["address"].toObject();
|
||||||
QString positionString = locationObject["position"].toString();
|
goToLocationFromAddress(locationObject);
|
||||||
QString orientationString = locationObject["orientation"].toString();
|
|
||||||
QString domainHostnameString = locationObject["domain"].toString();
|
|
||||||
|
|
||||||
qDebug() << "Changing domain to" << domainHostnameString <<
|
|
||||||
", position to" << positionString <<
|
|
||||||
", and orientation to" << orientationString;
|
|
||||||
|
|
||||||
QStringList coordinateItems = positionString.split(',');
|
|
||||||
QStringList orientationItems = orientationString.split(',');
|
|
||||||
|
|
||||||
NodeList::getInstance()->getDomainHandler().setHostname(domainHostnameString);
|
|
||||||
|
|
||||||
// orient the user to face the target
|
|
||||||
glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(),
|
|
||||||
orientationItems[1].toFloat(),
|
|
||||||
orientationItems[2].toFloat())))
|
|
||||||
* glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
|
||||||
setOrientation(newOrientation);
|
|
||||||
|
|
||||||
// move the user a couple units away
|
|
||||||
const float DISTANCE_TO_USER = 2.0f;
|
|
||||||
glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(),
|
|
||||||
coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
|
|
||||||
setPosition(newPosition);
|
|
||||||
emit transformChanged();
|
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found.");
|
QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyAvatar::goToLocationFromAddress(const QJsonObject& locationObject) {
|
||||||
|
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||||
|
sendKillAvatar();
|
||||||
|
|
||||||
|
QString positionString = locationObject["position"].toString();
|
||||||
|
QString orientationString = locationObject["orientation"].toString();
|
||||||
|
QString domainHostnameString = locationObject["domain"].toString();
|
||||||
|
|
||||||
|
qDebug() << "Changing domain to" << domainHostnameString <<
|
||||||
|
", position to" << positionString <<
|
||||||
|
", and orientation to" << orientationString;
|
||||||
|
|
||||||
|
QStringList coordinateItems = positionString.split(',');
|
||||||
|
QStringList orientationItems = orientationString.split(',');
|
||||||
|
|
||||||
|
NodeList::getInstance()->getDomainHandler().setHostname(domainHostnameString);
|
||||||
|
|
||||||
|
// orient the user to face the target
|
||||||
|
glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(),
|
||||||
|
orientationItems[1].toFloat(),
|
||||||
|
orientationItems[2].toFloat())))
|
||||||
|
* glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
setOrientation(newOrientation);
|
||||||
|
|
||||||
|
// move the user a couple units away
|
||||||
|
const float DISTANCE_TO_USER = 2.0f;
|
||||||
|
glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(),
|
||||||
|
coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
|
||||||
|
setPosition(newPosition);
|
||||||
|
emit transformChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::updateMotionBehaviorsFromMenu() {
|
void MyAvatar::updateMotionBehaviorsFromMenu() {
|
||||||
Menu* menu = Menu::getInstance();
|
Menu* menu = Menu::getInstance();
|
||||||
if (menu->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) {
|
if (menu->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) {
|
||||||
|
|
|
@ -129,6 +129,7 @@ public slots:
|
||||||
void resetSize();
|
void resetSize();
|
||||||
|
|
||||||
void goToLocationFromResponse(const QJsonObject& jsonObject);
|
void goToLocationFromResponse(const QJsonObject& jsonObject);
|
||||||
|
void goToLocationFromAddress(const QJsonObject& jsonObject);
|
||||||
|
|
||||||
// Set/Get update the thrust that will move the avatar around
|
// Set/Get update the thrust that will move the avatar around
|
||||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||||
|
|
|
@ -72,6 +72,14 @@ void OculusManager::connect() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OculusManager::isConnected() {
|
||||||
|
#ifdef HAVE_LIBOVR
|
||||||
|
return _isConnected && Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode);
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenHeight) {
|
void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenHeight) {
|
||||||
#ifdef HAVE_LIBOVR
|
#ifdef HAVE_LIBOVR
|
||||||
_stereoConfig.SetFullViewport(Viewport(0, 0, screenWidth, screenHeight));
|
_stereoConfig.SetFullViewport(Viewport(0, 0, screenWidth, screenHeight));
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
#include "renderer/ProgramObject.h"
|
#include "renderer/ProgramObject.h"
|
||||||
|
|
||||||
|
const float DEFAULT_OCULUS_UI_ANGULAR_SIZE = 72.0f;
|
||||||
|
|
||||||
class Camera;
|
class Camera;
|
||||||
|
|
||||||
/// Handles interaction with the Oculus Rift.
|
/// Handles interaction with the Oculus Rift.
|
||||||
|
@ -27,7 +29,7 @@ class OculusManager {
|
||||||
public:
|
public:
|
||||||
static void connect();
|
static void connect();
|
||||||
|
|
||||||
static bool isConnected() { return _isConnected; }
|
static bool isConnected();
|
||||||
|
|
||||||
static void configureCamera(Camera& camera, int screenWidth, int screenHeight);
|
static void configureCamera(Camera& camera, int screenWidth, int screenHeight);
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,14 @@ SixenseManager::SixenseManager() {
|
||||||
|
|
||||||
sixenseInit();
|
sixenseInit();
|
||||||
#endif
|
#endif
|
||||||
|
_triggerPressed[0] = false;
|
||||||
|
_bumperPressed[0] = false;
|
||||||
|
_oldX[0] = -1;
|
||||||
|
_oldY[0] = -1;
|
||||||
|
_triggerPressed[1] = false;
|
||||||
|
_bumperPressed[1] = false;
|
||||||
|
_oldX[1] = -1;
|
||||||
|
_oldY[1] = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
SixenseManager::~SixenseManager() {
|
SixenseManager::~SixenseManager() {
|
||||||
|
@ -107,6 +115,12 @@ void SixenseManager::update(float deltaTime) {
|
||||||
palm->setTrigger(data->trigger);
|
palm->setTrigger(data->trigger);
|
||||||
palm->setJoystick(data->joystick_x, data->joystick_y);
|
palm->setJoystick(data->joystick_x, data->joystick_y);
|
||||||
|
|
||||||
|
|
||||||
|
// Emulate the mouse so we can use scripts
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {
|
||||||
|
emulateMouse(palm, numActiveControllers - 1);
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
|
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
|
||||||
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
|
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
|
||||||
position *= METERS_PER_MILLIMETER;
|
position *= METERS_PER_MILLIMETER;
|
||||||
|
@ -171,6 +185,17 @@ void SixenseManager::update(float deltaTime) {
|
||||||
#endif // HAVE_SIXENSE
|
#endif // HAVE_SIXENSE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Constants for getCursorPixelRangeMultiplier()
|
||||||
|
const float MIN_PIXEL_RANGE_MULT = 0.4f;
|
||||||
|
const float MAX_PIXEL_RANGE_MULT = 2.0f;
|
||||||
|
const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01;
|
||||||
|
|
||||||
|
//Returns a multiplier to be applied to the cursor range for the controllers
|
||||||
|
float SixenseManager::getCursorPixelRangeMult() const {
|
||||||
|
//scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT)
|
||||||
|
return Menu::getInstance()->getSixenseReticleMoveSpeed() * RANGE_MULT + MIN_PIXEL_RANGE_MULT;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE
|
||||||
|
|
||||||
// the calibration sequence is:
|
// the calibration sequence is:
|
||||||
|
@ -313,5 +338,121 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Injecting mouse movements and clicks
|
||||||
|
void SixenseManager::emulateMouse(PalmData* palm, int index) {
|
||||||
|
Application* application = Application::getInstance();
|
||||||
|
MyAvatar* avatar = application->getAvatar();
|
||||||
|
QGLWidget* widget = application->getGLWidget();
|
||||||
|
QPoint pos;
|
||||||
|
// Get directon relative to avatar orientation
|
||||||
|
glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection();
|
||||||
|
|
||||||
|
Qt::MouseButton bumperButton;
|
||||||
|
Qt::MouseButton triggerButton;
|
||||||
|
|
||||||
|
if (Menu::getInstance()->getInvertSixenseButtons()) {
|
||||||
|
bumperButton = Qt::LeftButton;
|
||||||
|
triggerButton = Qt::RightButton;
|
||||||
|
} else {
|
||||||
|
bumperButton = Qt::RightButton;
|
||||||
|
triggerButton = Qt::LeftButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the angles, scaled between (-0.5,0.5)
|
||||||
|
float xAngle = (atan2(direction.z, direction.x) + M_PI_2);
|
||||||
|
float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
|
||||||
|
|
||||||
|
// Get the pixel range over which the xAngle and yAngle are scaled
|
||||||
|
float cursorRange = widget->width() * getCursorPixelRangeMult();
|
||||||
|
|
||||||
|
pos.setX(widget->width() / 2.0f + cursorRange * xAngle);
|
||||||
|
pos.setY(widget->height() / 2.0f + cursorRange * yAngle);
|
||||||
|
|
||||||
|
//If we are off screen then we should stop processing, and if a trigger or bumper is pressed,
|
||||||
|
//we should unpress them.
|
||||||
|
if (pos.x() < 0 || pos.x() > widget->width() || pos.y() < 0 || pos.y() > widget->height()) {
|
||||||
|
if (_bumperPressed[index]) {
|
||||||
|
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0);
|
||||||
|
|
||||||
|
application->mouseReleaseEvent(&mouseEvent);
|
||||||
|
|
||||||
|
_bumperPressed[index] = false;
|
||||||
|
}
|
||||||
|
if (_triggerPressed[index]) {
|
||||||
|
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0);
|
||||||
|
|
||||||
|
application->mouseReleaseEvent(&mouseEvent);
|
||||||
|
|
||||||
|
_triggerPressed[index] = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If position has changed, emit a mouse move to the application
|
||||||
|
if (pos.x() != _oldX[index] || pos.y() != _oldY[index]) {
|
||||||
|
QMouseEvent mouseEvent(static_cast<QEvent::Type>(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0);
|
||||||
|
|
||||||
|
//Only send the mouse event if the opposite left button isnt held down.
|
||||||
|
//This is specifically for edit voxels
|
||||||
|
if (triggerButton == Qt::LeftButton) {
|
||||||
|
if (!_triggerPressed[(int)(!index)]) {
|
||||||
|
application->mouseMoveEvent(&mouseEvent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!_bumperPressed[(int)(!index)]) {
|
||||||
|
application->mouseMoveEvent(&mouseEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_oldX[index] = pos.x();
|
||||||
|
_oldY[index] = pos.y();
|
||||||
|
|
||||||
|
|
||||||
|
//We need separate coordinates for clicks, since we need to check if
|
||||||
|
//a magnification window was clicked on
|
||||||
|
int clickX = pos.x();
|
||||||
|
int clickY = pos.y();
|
||||||
|
//Checks for magnification window click
|
||||||
|
application->getApplicationOverlay().getClickLocation(clickX, clickY);
|
||||||
|
//Set pos to the new click location, which may be the same if no magnification window is open
|
||||||
|
pos.setX(clickX);
|
||||||
|
pos.setY(clickY);
|
||||||
|
|
||||||
|
//Check for bumper press ( Right Click )
|
||||||
|
if (palm->getControllerButtons() & BUTTON_FWD) {
|
||||||
|
if (!_bumperPressed[index]) {
|
||||||
|
_bumperPressed[index] = true;
|
||||||
|
|
||||||
|
QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0);
|
||||||
|
|
||||||
|
application->mousePressEvent(&mouseEvent);
|
||||||
|
}
|
||||||
|
} else if (_bumperPressed[index]) {
|
||||||
|
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0);
|
||||||
|
|
||||||
|
application->mouseReleaseEvent(&mouseEvent);
|
||||||
|
|
||||||
|
_bumperPressed[index] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check for trigger press ( Left Click )
|
||||||
|
if (palm->getTrigger() == 1.0f) {
|
||||||
|
if (!_triggerPressed[index]) {
|
||||||
|
_triggerPressed[index] = true;
|
||||||
|
|
||||||
|
QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, triggerButton, triggerButton, 0);
|
||||||
|
|
||||||
|
application->mousePressEvent(&mouseEvent);
|
||||||
|
}
|
||||||
|
} else if (_triggerPressed[index]) {
|
||||||
|
QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0);
|
||||||
|
|
||||||
|
application->mouseReleaseEvent(&mouseEvent);
|
||||||
|
|
||||||
|
_triggerPressed[index] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif // HAVE_SIXENSE
|
#endif // HAVE_SIXENSE
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,12 @@ const unsigned int BUTTON_3 = 1U << 3;
|
||||||
const unsigned int BUTTON_4 = 1U << 4;
|
const unsigned int BUTTON_4 = 1U << 4;
|
||||||
const unsigned int BUTTON_FWD = 1U << 7;
|
const unsigned int BUTTON_FWD = 1U << 7;
|
||||||
|
|
||||||
|
// Event type that represents moving the controller
|
||||||
|
const unsigned int CONTROLLER_MOVE_EVENT = 1500U;
|
||||||
|
|
||||||
|
const float DEFAULT_SIXENSE_RETICLE_MOVE_SPEED = 37.5f;
|
||||||
|
const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false;
|
||||||
|
|
||||||
/// Handles interaction with the Sixense SDK (e.g., Razer Hydra).
|
/// Handles interaction with the Sixense SDK (e.g., Razer Hydra).
|
||||||
class SixenseManager : public QObject {
|
class SixenseManager : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -36,6 +42,7 @@ public:
|
||||||
~SixenseManager();
|
~SixenseManager();
|
||||||
|
|
||||||
void update(float deltaTime);
|
void update(float deltaTime);
|
||||||
|
float getCursorPixelRangeMult() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
|
@ -44,6 +51,7 @@ public slots:
|
||||||
private:
|
private:
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE
|
||||||
void updateCalibration(const sixenseControllerData* controllers);
|
void updateCalibration(const sixenseControllerData* controllers);
|
||||||
|
void emulateMouse(PalmData* palm, int index);
|
||||||
|
|
||||||
int _calibrationState;
|
int _calibrationState;
|
||||||
|
|
||||||
|
@ -65,6 +73,12 @@ private:
|
||||||
#endif
|
#endif
|
||||||
quint64 _lastMovement;
|
quint64 _lastMovement;
|
||||||
glm::vec3 _amountMoved;
|
glm::vec3 _amountMoved;
|
||||||
|
|
||||||
|
// for mouse emulation with the two controllers
|
||||||
|
bool _triggerPressed[2];
|
||||||
|
bool _bumperPressed[2];
|
||||||
|
int _oldX[2];
|
||||||
|
int _oldY[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_SixenseManager_h
|
#endif // hifi_SixenseManager_h
|
||||||
|
|
|
@ -16,10 +16,11 @@
|
||||||
|
|
||||||
const QString GET_USER_ADDRESS = "/api/v1/users/%1/address";
|
const QString GET_USER_ADDRESS = "/api/v1/users/%1/address";
|
||||||
const QString GET_PLACE_ADDRESS = "/api/v1/places/%1/address";
|
const QString GET_PLACE_ADDRESS = "/api/v1/places/%1/address";
|
||||||
|
const QString GET_ADDRESSES = "/api/v1/addresses/%1";
|
||||||
const QString POST_PLACE_CREATE = "/api/v1/places/";
|
const QString POST_PLACE_CREATE = "/api/v1/places/";
|
||||||
|
|
||||||
|
|
||||||
LocationManager::LocationManager() : _userData(), _placeData() {
|
LocationManager::LocationManager() {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,59 +75,38 @@ void LocationManager::goTo(QString destination) {
|
||||||
|
|
||||||
// go to coordinate destination or to Username
|
// go to coordinate destination or to Username
|
||||||
if (!goToDestination(destination)) {
|
if (!goToDestination(destination)) {
|
||||||
// reset data on local variables
|
|
||||||
_userData = QJsonObject();
|
|
||||||
_placeData = QJsonObject();
|
|
||||||
|
|
||||||
destination = QString(QUrl::toPercentEncoding(destination));
|
destination = QString(QUrl::toPercentEncoding(destination));
|
||||||
|
|
||||||
JSONCallbackParameters callbackParams;
|
JSONCallbackParameters callbackParams;
|
||||||
callbackParams.jsonCallbackReceiver = this;
|
callbackParams.jsonCallbackReceiver = this;
|
||||||
callbackParams.jsonCallbackMethod = "goToUserFromResponse";
|
callbackParams.jsonCallbackMethod = "goToAddressFromResponse";
|
||||||
AccountManager::getInstance().authenticatedRequest(GET_USER_ADDRESS.arg(destination),
|
AccountManager::getInstance().authenticatedRequest(GET_ADDRESSES.arg(destination),
|
||||||
QNetworkAccessManager::GetOperation,
|
|
||||||
callbackParams);
|
|
||||||
|
|
||||||
callbackParams.jsonCallbackMethod = "goToLocationFromResponse";
|
|
||||||
AccountManager::getInstance().authenticatedRequest(GET_PLACE_ADDRESS.arg(destination),
|
|
||||||
QNetworkAccessManager::GetOperation,
|
QNetworkAccessManager::GetOperation,
|
||||||
callbackParams);
|
callbackParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocationManager::goToUserFromResponse(const QJsonObject& jsonObject) {
|
void LocationManager::goToAddressFromResponse(const QJsonObject& responseData) {
|
||||||
_userData = jsonObject;
|
QJsonValue status = responseData["status"];
|
||||||
checkForMultipleDestinations();
|
qDebug() << responseData;
|
||||||
}
|
if (!status.isUndefined() && status.toString() == "success") {
|
||||||
|
const QJsonObject& data = responseData["data"].toObject();
|
||||||
|
const QJsonValue& userObject = data["user"];
|
||||||
|
const QJsonValue& placeObject = data["place"];
|
||||||
|
|
||||||
void LocationManager::goToLocationFromResponse(const QJsonObject& jsonObject) {
|
if (!placeObject.isUndefined() && !userObject.isUndefined()) {
|
||||||
_placeData = jsonObject;
|
emit multipleDestinationsFound(userObject.toObject(), placeObject.toObject());
|
||||||
checkForMultipleDestinations();
|
} else if (placeObject.isUndefined()) {
|
||||||
}
|
Application::getInstance()->getAvatar()->goToLocationFromAddress(userObject.toObject()["address"].toObject());
|
||||||
|
} else {
|
||||||
void LocationManager::checkForMultipleDestinations() {
|
Application::getInstance()->getAvatar()->goToLocationFromAddress(placeObject.toObject()["address"].toObject());
|
||||||
if (!_userData.isEmpty() && !_placeData.isEmpty()) {
|
|
||||||
if (_userData.contains("status") && _userData["status"].toString() == "success" &&
|
|
||||||
_placeData.contains("status") && _placeData["status"].toString() == "success") {
|
|
||||||
emit multipleDestinationsFound(_userData, _placeData);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if (_userData.contains("status") && _userData["status"].toString() == "success") {
|
|
||||||
Application::getInstance()->getAvatar()->goToLocationFromResponse(_userData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_placeData.contains("status") && _placeData["status"].toString() == "success") {
|
|
||||||
Application::getInstance()->getAvatar()->goToLocationFromResponse(_placeData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found.");
|
QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocationManager::goToUser(QString userName) {
|
void LocationManager::goToUser(QString userName) {
|
||||||
|
|
||||||
JSONCallbackParameters callbackParams;
|
JSONCallbackParameters callbackParams;
|
||||||
callbackParams.jsonCallbackReceiver = Application::getInstance()->getAvatar();
|
callbackParams.jsonCallbackReceiver = Application::getInstance()->getAvatar();
|
||||||
callbackParams.jsonCallbackMethod = "goToLocationFromResponse";
|
callbackParams.jsonCallbackMethod = "goToLocationFromResponse";
|
||||||
|
|
|
@ -39,11 +39,7 @@ public:
|
||||||
bool goToDestination(QString destination);
|
bool goToDestination(QString destination);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QJsonObject _userData;
|
|
||||||
QJsonObject _placeData;
|
|
||||||
|
|
||||||
void replaceLastOccurrence(const QChar search, const QChar replace, QString& string);
|
void replaceLastOccurrence(const QChar search, const QChar replace, QString& string);
|
||||||
void checkForMultipleDestinations();
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void creationCompleted(LocationManager::NamedLocationCreateResponse response);
|
void creationCompleted(LocationManager::NamedLocationCreateResponse response);
|
||||||
|
@ -52,8 +48,7 @@ signals:
|
||||||
private slots:
|
private slots:
|
||||||
void namedLocationDataReceived(const QJsonObject& data);
|
void namedLocationDataReceived(const QJsonObject& data);
|
||||||
void errorDataReceived(QNetworkReply::NetworkError error, const QString& message);
|
void errorDataReceived(QNetworkReply::NetworkError error, const QString& message);
|
||||||
void goToLocationFromResponse(const QJsonObject& jsonObject);
|
void goToAddressFromResponse(const QJsonObject& jsonObject);
|
||||||
void goToUserFromResponse(const QJsonObject& jsonObject);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
41
interface/src/scripting/AccountScriptingInterface.cpp
Normal file
41
interface/src/scripting/AccountScriptingInterface.cpp
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
//
|
||||||
|
// AccountScriptingInterface.cpp
|
||||||
|
// interface/src/scripting
|
||||||
|
//
|
||||||
|
// Created by Stojce Slavkovski on 6/07/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AccountManager.h"
|
||||||
|
|
||||||
|
#include "AccountScriptingInterface.h"
|
||||||
|
|
||||||
|
AccountScriptingInterface::AccountScriptingInterface() {
|
||||||
|
AccountManager& accountManager = AccountManager::getInstance();
|
||||||
|
connect(&accountManager, &AccountManager::balanceChanged, this,
|
||||||
|
&AccountScriptingInterface::updateBalance);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountScriptingInterface* AccountScriptingInterface::getInstance() {
|
||||||
|
static AccountScriptingInterface sharedInstance;
|
||||||
|
return &sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AccountScriptingInterface::getBalance() {
|
||||||
|
AccountManager& accountManager = AccountManager::getInstance();
|
||||||
|
return accountManager.getAccountInfo().getBalanceInSatoshis();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccountScriptingInterface::isLoggedIn() {
|
||||||
|
AccountManager& accountManager = AccountManager::getInstance();
|
||||||
|
return accountManager.isLoggedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountScriptingInterface::updateBalance() {
|
||||||
|
AccountManager& accountManager = AccountManager::getInstance();
|
||||||
|
emit balanceChanged(accountManager.getAccountInfo().getBalanceInSatoshis());
|
||||||
|
}
|
31
interface/src/scripting/AccountScriptingInterface.h
Normal file
31
interface/src/scripting/AccountScriptingInterface.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
//
|
||||||
|
// AccountScriptingInterface.h
|
||||||
|
// interface/src/scripting
|
||||||
|
//
|
||||||
|
// Created by Stojce Slavkovski on 6/07/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AccountScriptingInterface_h
|
||||||
|
#define hifi_AccountScriptingInterface_h
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class AccountScriptingInterface : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
AccountScriptingInterface();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void balanceChanged(float newBalance);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
static AccountScriptingInterface* getInstance();
|
||||||
|
float getBalance();
|
||||||
|
bool isLoggedIn();
|
||||||
|
void updateBalance();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AccountScriptingInterface_h
|
File diff suppressed because it is too large
Load diff
|
@ -15,28 +15,27 @@
|
||||||
class Overlays;
|
class Overlays;
|
||||||
class QOpenGLFramebufferObject;
|
class QOpenGLFramebufferObject;
|
||||||
|
|
||||||
|
const float MAGNIFY_WIDTH = 160.0f;
|
||||||
|
const float MAGNIFY_HEIGHT = 80.0f;
|
||||||
|
const float MAGNIFY_MULT = 4.0f;
|
||||||
|
|
||||||
// Handles the drawing of the overlays to the screen
|
// Handles the drawing of the overlays to the screen
|
||||||
class ApplicationOverlay {
|
class ApplicationOverlay {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum UIType { HEMISPHERE, SEMICIRCLE, CURVED_SEMICIRCLE };
|
|
||||||
|
|
||||||
ApplicationOverlay();
|
ApplicationOverlay();
|
||||||
~ApplicationOverlay();
|
~ApplicationOverlay();
|
||||||
|
|
||||||
void renderOverlay(bool renderToTexture = false);
|
void renderOverlay(bool renderToTexture = false);
|
||||||
void displayOverlayTexture(Camera& whichCamera);
|
void displayOverlayTexture();
|
||||||
void displayOverlayTextureOculus(Camera& whichCamera);
|
void displayOverlayTextureOculus(Camera& whichCamera);
|
||||||
void computeOculusPickRay(float x, float y, glm::vec3& direction) const;
|
void computeOculusPickRay(float x, float y, glm::vec3& direction) const;
|
||||||
|
void getClickLocation(int &x, int &y) const;
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
QOpenGLFramebufferObject* getFramebufferObject();
|
QOpenGLFramebufferObject* getFramebufferObject();
|
||||||
float getOculusAngle() const { return _oculusAngle; }
|
float getAlpha() const { return _alpha; }
|
||||||
|
|
||||||
// Setters
|
|
||||||
void setOculusAngle(float oculusAngle) { _oculusAngle = oculusAngle; }
|
|
||||||
void setUIType(UIType uiType) { _uiType = uiType; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Interleaved vertex data
|
// Interleaved vertex data
|
||||||
struct TextureVertex {
|
struct TextureVertex {
|
||||||
|
@ -46,13 +45,31 @@ private:
|
||||||
|
|
||||||
typedef QPair<GLuint, GLuint> VerticesIndices;
|
typedef QPair<GLuint, GLuint> VerticesIndices;
|
||||||
|
|
||||||
|
void renderPointers();
|
||||||
|
void renderControllerPointers();
|
||||||
|
void renderControllerPointersOculus();
|
||||||
|
void renderMagnifier(int mouseX, int mouseY, float sizeMult, bool showBorder) const;
|
||||||
|
void renderAudioMeter();
|
||||||
|
void renderStatsAndLogs();
|
||||||
void renderTexturedHemisphere();
|
void renderTexturedHemisphere();
|
||||||
|
|
||||||
QOpenGLFramebufferObject* _framebufferObject;
|
QOpenGLFramebufferObject* _framebufferObject;
|
||||||
float _trailingAudioLoudness;
|
float _trailingAudioLoudness;
|
||||||
float _oculusAngle;
|
float _textureFov;
|
||||||
float _distance;
|
|
||||||
UIType _uiType;
|
enum MagnifyDevices { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_MAGNIFIERS = RIGHT_CONTROLLER + 1 };
|
||||||
|
bool _reticleActive[NUMBER_OF_MAGNIFIERS];
|
||||||
|
int _mouseX[NUMBER_OF_MAGNIFIERS];
|
||||||
|
int _mouseY[NUMBER_OF_MAGNIFIERS];
|
||||||
|
bool _magActive[NUMBER_OF_MAGNIFIERS];
|
||||||
|
int _magX[NUMBER_OF_MAGNIFIERS];
|
||||||
|
int _magY[NUMBER_OF_MAGNIFIERS];
|
||||||
|
float _magSizeMult[NUMBER_OF_MAGNIFIERS];
|
||||||
|
|
||||||
|
float _alpha;
|
||||||
|
bool _active;
|
||||||
|
|
||||||
|
GLuint _crosshairTexture;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ApplicationOverlay_h
|
#endif // hifi_ApplicationOverlay_h
|
|
@ -103,8 +103,9 @@ void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authoriz
|
||||||
codedAuthorizationURL.setQuery(authQuery);
|
codedAuthorizationURL.setQuery(authQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connect(_activeWebView.data(), &QWebView::urlChanged, this, &OAuthWebViewHandler::handleURLChanged);
|
||||||
|
|
||||||
_activeWebView->load(codedAuthorizationURL);
|
_activeWebView->load(codedAuthorizationURL);
|
||||||
_activeWebView->show();
|
|
||||||
|
|
||||||
connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors,
|
connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors,
|
||||||
this, &OAuthWebViewHandler::handleSSLErrors);
|
this, &OAuthWebViewHandler::handleSSLErrors);
|
||||||
|
@ -137,3 +138,17 @@ void OAuthWebViewHandler::handleLoadFinished(bool success) {
|
||||||
void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) {
|
void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) {
|
||||||
_webViewRedisplayTimer.restart();
|
_webViewRedisplayTimer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OAuthWebViewHandler::handleURLChanged(const QUrl& url) {
|
||||||
|
// check if this is the authorization screen - if it is then we need to show the OAuthWebViewHandler
|
||||||
|
const QString ACCESS_TOKEN_URL_REGEX_STRING = "redirect_uri=[\\w:\\/\\.]+&access_token=";
|
||||||
|
QRegExp accessTokenRegex(ACCESS_TOKEN_URL_REGEX_STRING);
|
||||||
|
|
||||||
|
if (accessTokenRegex.indexIn(url.toString()) != -1) {
|
||||||
|
_activeWebView->show();
|
||||||
|
} else if (url.toString() == DEFAULT_NODE_AUTH_URL.toString() + "/login") {
|
||||||
|
// this is a login request - we're going to close the webview and signal the AccountManager that we need a login
|
||||||
|
_activeWebView->close();
|
||||||
|
AccountManager::getInstance().checkAndSignalForAccessToken();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ private slots:
|
||||||
void handleSSLErrors(QNetworkReply* networkReply, const QList<QSslError>& errorList);
|
void handleSSLErrors(QNetworkReply* networkReply, const QList<QSslError>& errorList);
|
||||||
void handleLoadFinished(bool success);
|
void handleLoadFinished(bool success);
|
||||||
void handleWebViewDestroyed(QObject* destroyedObject);
|
void handleWebViewDestroyed(QObject* destroyedObject);
|
||||||
|
void handleURLChanged(const QUrl& url);
|
||||||
private:
|
private:
|
||||||
QPointer<QWebView> _activeWebView;
|
QPointer<QWebView> _activeWebView;
|
||||||
QElapsedTimer _webViewRedisplayTimer;
|
QElapsedTimer _webViewRedisplayTimer;
|
||||||
|
|
|
@ -159,6 +159,13 @@ void PreferencesDialog::loadPreferences() {
|
||||||
ui.maxVoxelsSpin->setValue(menuInstance->getMaxVoxels());
|
ui.maxVoxelsSpin->setValue(menuInstance->getMaxVoxels());
|
||||||
|
|
||||||
ui.maxVoxelsPPSSpin->setValue(menuInstance->getMaxVoxelPacketsPerSecond());
|
ui.maxVoxelsPPSSpin->setValue(menuInstance->getMaxVoxelPacketsPerSecond());
|
||||||
|
|
||||||
|
ui.oculusUIAngularSizeSpin->setValue(menuInstance->getOculusUIAngularSize());
|
||||||
|
|
||||||
|
ui.sixenseReticleMoveSpeedSpin->setValue(menuInstance->getSixenseReticleMoveSpeed());
|
||||||
|
|
||||||
|
ui.invertSixenseButtonsCheckBox->setChecked(menuInstance->getInvertSixenseButtons());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreferencesDialog::savePreferences() {
|
void PreferencesDialog::savePreferences() {
|
||||||
|
@ -215,6 +222,12 @@ void PreferencesDialog::savePreferences() {
|
||||||
(float)ui.faceshiftEyeDeflectionSider->maximum());
|
(float)ui.faceshiftEyeDeflectionSider->maximum());
|
||||||
Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value());
|
Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value());
|
||||||
|
|
||||||
|
Menu::getInstance()->setOculusUIAngularSize(ui.oculusUIAngularSizeSpin->value());
|
||||||
|
|
||||||
|
Menu::getInstance()->setSixenseReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());
|
||||||
|
|
||||||
|
Menu::getInstance()->setInvertSixenseButtons(ui.invertSixenseButtonsCheckBox->isChecked());
|
||||||
|
|
||||||
Menu::getInstance()->setAudioJitterBufferSamples(ui.audioJitterSpin->value());
|
Menu::getInstance()->setAudioJitterBufferSamples(ui.audioJitterSpin->value());
|
||||||
Application::getInstance()->getAudio()->setJitterBufferSamples(ui.audioJitterSpin->value());
|
Application::getInstance()->getAudio()->setJitterBufferSamples(ui.audioJitterSpin->value());
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,9 @@ Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int wid
|
||||||
}
|
}
|
||||||
|
|
||||||
TextRenderer::TextRenderer(const char* family, int pointSize, int weight,
|
TextRenderer::TextRenderer(const char* family, int pointSize, int weight,
|
||||||
bool italic, EffectType effectType, int effectThickness)
|
bool italic, EffectType effectType, int effectThickness, QColor color)
|
||||||
: _font(family, pointSize, weight, italic), _metrics(_font), _effectType(effectType),
|
: _font(family, pointSize, weight, italic), _metrics(_font), _effectType(effectType),
|
||||||
_effectThickness(effectThickness), _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0) {
|
_effectThickness(effectThickness), _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0), _color(color) {
|
||||||
_font.setKerning(false);
|
_font.setKerning(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ const Glyph& TextRenderer::getGlyph(char c) {
|
||||||
// render the glyph into an image and copy it into the texture
|
// render the glyph into an image and copy it into the texture
|
||||||
QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32);
|
QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32);
|
||||||
if (c == SOLID_BLOCK_CHAR) {
|
if (c == SOLID_BLOCK_CHAR) {
|
||||||
image.fill(QColor(255, 255, 255));
|
image.fill(_color);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
image.fill(0);
|
image.fill(0);
|
||||||
|
@ -180,7 +180,7 @@ const Glyph& TextRenderer::getGlyph(char c) {
|
||||||
painter.setRenderHint(QPainter::Antialiasing);
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
painter.drawPath(path);
|
painter.drawPath(path);
|
||||||
}
|
}
|
||||||
painter.setPen(QColor(255, 255, 255));
|
painter.setPen(_color);
|
||||||
painter.drawText(-bounds.x(), -bounds.y(), ch);
|
painter.drawText(-bounds.x(), -bounds.y(), ch);
|
||||||
}
|
}
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
|
glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#ifndef hifi_TextRenderer_h
|
#ifndef hifi_TextRenderer_h
|
||||||
#define hifi_TextRenderer_h
|
#define hifi_TextRenderer_h
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QFontMetrics>
|
#include <QFontMetrics>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
@ -41,7 +42,8 @@ public:
|
||||||
enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT };
|
enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT };
|
||||||
|
|
||||||
TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false,
|
TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false,
|
||||||
EffectType effect = NO_EFFECT, int effectThickness = 1);
|
EffectType effect = NO_EFFECT, int effectThickness = 1,
|
||||||
|
QColor color = QColor(255, 255, 255));
|
||||||
~TextRenderer();
|
~TextRenderer();
|
||||||
|
|
||||||
const QFontMetrics& metrics() const { return _metrics; }
|
const QFontMetrics& metrics() const { return _metrics; }
|
||||||
|
@ -85,6 +87,9 @@ private:
|
||||||
|
|
||||||
// the list of all texture ids for which we're responsible
|
// the list of all texture ids for which we're responsible
|
||||||
QVector<GLuint> _allTextureIDs;
|
QVector<GLuint> _allTextureIDs;
|
||||||
|
|
||||||
|
// text color
|
||||||
|
QColor _color;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Glyph {
|
class Glyph {
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
|
|
||||||
TextOverlay::TextOverlay() :
|
TextOverlay::TextOverlay() :
|
||||||
_leftMargin(DEFAULT_MARGIN),
|
_leftMargin(DEFAULT_MARGIN),
|
||||||
_topMargin(DEFAULT_MARGIN)
|
_topMargin(DEFAULT_MARGIN),
|
||||||
|
_fontSize(DEFAULT_FONTSIZE)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ void TextOverlay::render() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const float MAX_COLOR = 255;
|
const float MAX_COLOR = 255;
|
||||||
glColor4f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, _alpha);
|
glColor4f(0 / MAX_COLOR, 0 / MAX_COLOR, 0 / MAX_COLOR, _alpha);
|
||||||
|
|
||||||
glBegin(GL_QUADS);
|
glBegin(GL_QUADS);
|
||||||
glVertex2f(_bounds.left(), _bounds.top());
|
glVertex2f(_bounds.left(), _bounds.top());
|
||||||
|
@ -43,8 +44,9 @@ void TextOverlay::render() {
|
||||||
|
|
||||||
//TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false,
|
//TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false,
|
||||||
// EffectType effect = NO_EFFECT, int effectThickness = 1);
|
// EffectType effect = NO_EFFECT, int effectThickness = 1);
|
||||||
|
TextRenderer textRenderer(SANS_FONT_FAMILY, _fontSize, 50, false, TextRenderer::NO_EFFECT, 1,
|
||||||
TextRenderer textRenderer(SANS_FONT_FAMILY, 11, 50);
|
QColor(_color.red, _color.green, _color.blue));
|
||||||
|
|
||||||
const int leftAdjust = -1; // required to make text render relative to left edge of bounds
|
const int leftAdjust = -1; // required to make text render relative to left edge of bounds
|
||||||
const int topAdjust = -2; // required to make text render relative to top edge of bounds
|
const int topAdjust = -2; // required to make text render relative to top edge of bounds
|
||||||
int x = _bounds.left() + _leftMargin + leftAdjust;
|
int x = _bounds.left() + _leftMargin + leftAdjust;
|
||||||
|
@ -67,6 +69,13 @@ void TextOverlay::render() {
|
||||||
|
|
||||||
void TextOverlay::setProperties(const QScriptValue& properties) {
|
void TextOverlay::setProperties(const QScriptValue& properties) {
|
||||||
Overlay2D::setProperties(properties);
|
Overlay2D::setProperties(properties);
|
||||||
|
|
||||||
|
QScriptValue font = properties.property("font");
|
||||||
|
if (font.isObject()) {
|
||||||
|
if (font.property("size").isValid()) {
|
||||||
|
setFontSize(font.property("size").toInt32());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QScriptValue text = properties.property("text");
|
QScriptValue text = properties.property("text");
|
||||||
if (text.isValid()) {
|
if (text.isValid()) {
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "Overlay2D.h"
|
#include "Overlay2D.h"
|
||||||
|
|
||||||
const int DEFAULT_MARGIN = 10;
|
const int DEFAULT_MARGIN = 10;
|
||||||
|
const int DEFAULT_FONTSIZE = 11;
|
||||||
|
|
||||||
class TextOverlay : public Overlay2D {
|
class TextOverlay : public Overlay2D {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -47,6 +48,7 @@ public:
|
||||||
void setText(const QString& text) { _text = text; }
|
void setText(const QString& text) { _text = text; }
|
||||||
void setLeftMargin(int margin) { _leftMargin = margin; }
|
void setLeftMargin(int margin) { _leftMargin = margin; }
|
||||||
void setTopMargin(int margin) { _topMargin = margin; }
|
void setTopMargin(int margin) { _topMargin = margin; }
|
||||||
|
void setFontSize(int fontSize) { _fontSize = fontSize; }
|
||||||
|
|
||||||
virtual void setProperties(const QScriptValue& properties);
|
virtual void setProperties(const QScriptValue& properties);
|
||||||
|
|
||||||
|
@ -55,7 +57,7 @@ private:
|
||||||
QString _text;
|
QString _text;
|
||||||
int _leftMargin;
|
int _leftMargin;
|
||||||
int _topMargin;
|
int _topMargin;
|
||||||
|
int _fontSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1711,6 +1711,331 @@ padding: 10px;margin-top:10px</string>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="oculusRiftTitleLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Arial</family>
|
||||||
|
<pointsize>20</pointsize>
|
||||||
|
<weight>50</weight>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color: #0e7077</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Oculus Rift</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_15">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_12">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Arial</family>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color: rgb(51, 51, 51)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>User Interface Angular Size</string>
|
||||||
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>15</number>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>maxVoxelsSpin</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_15">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Arial</family>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="oculusUIAngularSizeSpin">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>125</width>
|
||||||
|
<height>36</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Arial</family>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>30</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>160</number>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>72</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="sixenseControllersTitleLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Arial</family>
|
||||||
|
<pointsize>20</pointsize>
|
||||||
|
<weight>50</weight>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color: #0e7077</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Sixense Controllers</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_17">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_14">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Arial</family>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color: rgb(51, 51, 51)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Invert Mouse Buttons</string>
|
||||||
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>15</number>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>maxVoxelsSpin</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_17">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Arial</family>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="invertSixenseButtonsCheckBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>32</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="baseSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>32</width>
|
||||||
|
<height>32</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_16">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_13">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Arial</family>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color: rgb(51, 51, 51)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Reticle Movement Speed</string>
|
||||||
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>15</number>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>maxVoxelsSpin</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_16">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Arial</family>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="sixenseReticleMoveSpeedSpin">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>125</width>
|
||||||
|
<height>36</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>Arial</family>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>50</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
|
|
@ -258,7 +258,7 @@ void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& ou
|
||||||
// Now pull out the data
|
// Now pull out the data
|
||||||
quint32 outputAudioByteArraySize = qFromLittleEndian<quint32>(dataHeader.descriptor.size);
|
quint32 outputAudioByteArraySize = qFromLittleEndian<quint32>(dataHeader.descriptor.size);
|
||||||
outputAudioByteArray.resize(outputAudioByteArraySize);
|
outputAudioByteArray.resize(outputAudioByteArraySize);
|
||||||
if (waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize) != outputAudioByteArraySize) {
|
if (waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize) != (int)outputAudioByteArraySize) {
|
||||||
qDebug() << "Error reading WAV file";
|
qDebug() << "Error reading WAV file";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -35,19 +35,25 @@ class QUrl;
|
||||||
class Attribute;
|
class Attribute;
|
||||||
class AttributeValue;
|
class AttributeValue;
|
||||||
class Bitstream;
|
class Bitstream;
|
||||||
class FieldReader;
|
class GenericValue;
|
||||||
class ObjectReader;
|
class ObjectReader;
|
||||||
|
class ObjectStreamer;
|
||||||
class OwnedAttributeValue;
|
class OwnedAttributeValue;
|
||||||
class PropertyReader;
|
|
||||||
class PropertyWriter;
|
|
||||||
class TypeReader;
|
|
||||||
class TypeStreamer;
|
class TypeStreamer;
|
||||||
|
|
||||||
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
|
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
|
||||||
|
|
||||||
typedef QPair<QByteArray, QByteArray> ScopeNamePair;
|
typedef QPair<QByteArray, QByteArray> ScopeNamePair;
|
||||||
typedef QVector<PropertyReader> PropertyReaderVector;
|
typedef QPair<QByteArray, int> NameIntPair;
|
||||||
typedef QVector<PropertyWriter> PropertyWriterVector;
|
typedef QSharedPointer<ObjectStreamer> ObjectStreamerPointer;
|
||||||
|
typedef QWeakPointer<ObjectStreamer> WeakObjectStreamerPointer;
|
||||||
|
typedef QSharedPointer<TypeStreamer> TypeStreamerPointer;
|
||||||
|
typedef QWeakPointer<TypeStreamer> WeakTypeStreamerPointer;
|
||||||
|
|
||||||
|
typedef QPair<QVariant, QVariant> QVariantPair;
|
||||||
|
typedef QList<QVariantPair> QVariantPairList;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QVariantPairList)
|
||||||
|
|
||||||
/// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that
|
/// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that
|
||||||
/// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows
|
/// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows
|
||||||
|
@ -197,7 +203,7 @@ public:
|
||||||
|
|
||||||
class WriteMappings {
|
class WriteMappings {
|
||||||
public:
|
public:
|
||||||
QHash<const QMetaObject*, int> metaObjectOffsets;
|
QHash<const ObjectStreamer*, int> objectStreamerOffsets;
|
||||||
QHash<const TypeStreamer*, int> typeStreamerOffsets;
|
QHash<const TypeStreamer*, int> typeStreamerOffsets;
|
||||||
QHash<AttributePointer, int> attributeOffsets;
|
QHash<AttributePointer, int> attributeOffsets;
|
||||||
QHash<QScriptString, int> scriptStringOffsets;
|
QHash<QScriptString, int> scriptStringOffsets;
|
||||||
|
@ -206,8 +212,8 @@ public:
|
||||||
|
|
||||||
class ReadMappings {
|
class ReadMappings {
|
||||||
public:
|
public:
|
||||||
QHash<int, ObjectReader> metaObjectValues;
|
QHash<int, ObjectStreamerPointer> objectStreamerValues;
|
||||||
QHash<int, TypeReader> typeStreamerValues;
|
QHash<int, TypeStreamerPointer> typeStreamerValues;
|
||||||
QHash<int, AttributePointer> attributeValues;
|
QHash<int, AttributePointer> attributeValues;
|
||||||
QHash<int, QScriptString> scriptStringValues;
|
QHash<int, QScriptString> scriptStringValues;
|
||||||
QHash<int, SharedObjectPointer> sharedObjectValues;
|
QHash<int, SharedObjectPointer> sharedObjectValues;
|
||||||
|
@ -232,8 +238,17 @@ public:
|
||||||
|
|
||||||
enum MetadataType { NO_METADATA, HASH_METADATA, FULL_METADATA };
|
enum MetadataType { NO_METADATA, HASH_METADATA, FULL_METADATA };
|
||||||
|
|
||||||
|
enum GenericsMode { NO_GENERICS, FALLBACK_GENERICS, ALL_GENERICS };
|
||||||
|
|
||||||
/// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both.
|
/// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both.
|
||||||
Bitstream(QDataStream& underlying, MetadataType metadataType = NO_METADATA, QObject* parent = NULL);
|
/// \param metadataType the metadata type, which determines the amount of version-resiliency: with no metadata, all types
|
||||||
|
/// must match exactly; with hash metadata, any types which do not match exactly will be ignored; with full metadata,
|
||||||
|
/// fields (and enum values, etc.) will be remapped by name
|
||||||
|
/// \param genericsMode the generics mode, which determines which types will be replaced by generic equivalents: with
|
||||||
|
/// no generics, no generics will be created; with fallback generics, generics will be created for any unknown types; with
|
||||||
|
/// all generics, generics will be created for all non-simple types
|
||||||
|
Bitstream(QDataStream& underlying, MetadataType metadataType = NO_METADATA,
|
||||||
|
GenericsMode = NO_GENERICS, QObject* parent = NULL);
|
||||||
|
|
||||||
/// Substitutes the supplied metaobject for the given class name's default mapping.
|
/// Substitutes the supplied metaobject for the given class name's default mapping.
|
||||||
void addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject);
|
void addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject);
|
||||||
|
@ -364,6 +379,9 @@ public:
|
||||||
Bitstream& operator<<(const AttributeValue& attributeValue);
|
Bitstream& operator<<(const AttributeValue& attributeValue);
|
||||||
Bitstream& operator>>(OwnedAttributeValue& attributeValue);
|
Bitstream& operator>>(OwnedAttributeValue& attributeValue);
|
||||||
|
|
||||||
|
Bitstream& operator<<(const GenericValue& value);
|
||||||
|
Bitstream& operator>>(GenericValue& value);
|
||||||
|
|
||||||
template<class T> Bitstream& operator<<(const QList<T>& list);
|
template<class T> Bitstream& operator<<(const QList<T>& list);
|
||||||
template<class T> Bitstream& operator>>(QList<T>& list);
|
template<class T> Bitstream& operator>>(QList<T>& list);
|
||||||
|
|
||||||
|
@ -381,11 +399,14 @@ public:
|
||||||
|
|
||||||
Bitstream& operator<<(const QMetaObject* metaObject);
|
Bitstream& operator<<(const QMetaObject* metaObject);
|
||||||
Bitstream& operator>>(const QMetaObject*& metaObject);
|
Bitstream& operator>>(const QMetaObject*& metaObject);
|
||||||
Bitstream& operator>>(ObjectReader& objectReader);
|
|
||||||
|
Bitstream& operator<<(const ObjectStreamer* streamer);
|
||||||
|
Bitstream& operator>>(const ObjectStreamer*& streamer);
|
||||||
|
Bitstream& operator>>(ObjectStreamerPointer& streamer);
|
||||||
|
|
||||||
Bitstream& operator<<(const TypeStreamer* streamer);
|
Bitstream& operator<<(const TypeStreamer* streamer);
|
||||||
Bitstream& operator>>(const TypeStreamer*& streamer);
|
Bitstream& operator>>(const TypeStreamer*& streamer);
|
||||||
Bitstream& operator>>(TypeReader& reader);
|
Bitstream& operator>>(TypeStreamerPointer& streamer);
|
||||||
|
|
||||||
Bitstream& operator<<(const AttributePointer& attribute);
|
Bitstream& operator<<(const AttributePointer& attribute);
|
||||||
Bitstream& operator>>(AttributePointer& attribute);
|
Bitstream& operator>>(AttributePointer& attribute);
|
||||||
|
@ -399,11 +420,11 @@ public:
|
||||||
Bitstream& operator<<(const SharedObjectPointer& object);
|
Bitstream& operator<<(const SharedObjectPointer& object);
|
||||||
Bitstream& operator>>(SharedObjectPointer& object);
|
Bitstream& operator>>(SharedObjectPointer& object);
|
||||||
|
|
||||||
Bitstream& operator<(const QMetaObject* metaObject);
|
Bitstream& operator<(const ObjectStreamer* streamer);
|
||||||
Bitstream& operator>(ObjectReader& objectReader);
|
Bitstream& operator>(ObjectStreamerPointer& streamer);
|
||||||
|
|
||||||
Bitstream& operator<(const TypeStreamer* streamer);
|
Bitstream& operator<(const TypeStreamer* streamer);
|
||||||
Bitstream& operator>(TypeReader& reader);
|
Bitstream& operator>(TypeStreamerPointer& streamer);
|
||||||
|
|
||||||
Bitstream& operator<(const AttributePointer& attribute);
|
Bitstream& operator<(const AttributePointer& attribute);
|
||||||
Bitstream& operator>(AttributePointer& attribute);
|
Bitstream& operator>(AttributePointer& attribute);
|
||||||
|
@ -424,14 +445,18 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
ObjectStreamerPointer readGenericObjectStreamer(const QByteArray& name);
|
||||||
|
TypeStreamerPointer readGenericTypeStreamer(const QByteArray& name, int category);
|
||||||
|
|
||||||
QDataStream& _underlying;
|
QDataStream& _underlying;
|
||||||
quint8 _byte;
|
quint8 _byte;
|
||||||
int _position;
|
int _position;
|
||||||
|
|
||||||
MetadataType _metadataType;
|
MetadataType _metadataType;
|
||||||
|
GenericsMode _genericsMode;
|
||||||
|
|
||||||
RepeatedValueStreamer<const QMetaObject*, const QMetaObject*, ObjectReader> _metaObjectStreamer;
|
RepeatedValueStreamer<const ObjectStreamer*, const ObjectStreamer*, ObjectStreamerPointer> _objectStreamerStreamer;
|
||||||
RepeatedValueStreamer<const TypeStreamer*, const TypeStreamer*, TypeReader> _typeStreamerStreamer;
|
RepeatedValueStreamer<const TypeStreamer*, const TypeStreamer*, TypeStreamerPointer> _typeStreamerStreamer;
|
||||||
RepeatedValueStreamer<AttributePointer> _attributeStreamer;
|
RepeatedValueStreamer<AttributePointer> _attributeStreamer;
|
||||||
RepeatedValueStreamer<QScriptString> _scriptStringStreamer;
|
RepeatedValueStreamer<QScriptString> _scriptStringStreamer;
|
||||||
RepeatedValueStreamer<SharedObjectPointer, SharedObject*> _sharedObjectStreamer;
|
RepeatedValueStreamer<SharedObjectPointer, SharedObject*> _sharedObjectStreamer;
|
||||||
|
@ -447,17 +472,17 @@ private:
|
||||||
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
|
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
|
||||||
static QHash<int, const TypeStreamer*>& getTypeStreamers();
|
static QHash<int, const TypeStreamer*>& getTypeStreamers();
|
||||||
|
|
||||||
|
static const QHash<const QMetaObject*, const ObjectStreamer*>& getObjectStreamers();
|
||||||
|
static QHash<const QMetaObject*, const ObjectStreamer*> createObjectStreamers();
|
||||||
|
|
||||||
static const QHash<ScopeNamePair, const TypeStreamer*>& getEnumStreamers();
|
static const QHash<ScopeNamePair, const TypeStreamer*>& getEnumStreamers();
|
||||||
static QHash<ScopeNamePair, const TypeStreamer*> createEnumStreamers();
|
static QHash<ScopeNamePair, const TypeStreamer*> createEnumStreamers();
|
||||||
|
|
||||||
static const QHash<QByteArray, const TypeStreamer*>& getEnumStreamersByName();
|
static const QHash<QByteArray, const TypeStreamer*>& getEnumStreamersByName();
|
||||||
static QHash<QByteArray, const TypeStreamer*> createEnumStreamersByName();
|
static QHash<QByteArray, const TypeStreamer*> createEnumStreamersByName();
|
||||||
|
|
||||||
static const QHash<const QMetaObject*, PropertyReaderVector>& getPropertyReaders();
|
static const TypeStreamer* getInvalidTypeStreamer();
|
||||||
static QHash<const QMetaObject*, PropertyReaderVector> createPropertyReaders();
|
static const TypeStreamer* createInvalidTypeStreamer();
|
||||||
|
|
||||||
static const QHash<const QMetaObject*, PropertyWriterVector>& getPropertyWriters();
|
|
||||||
static QHash<const QMetaObject*, PropertyWriterVector> createPropertyWriters();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T> inline void Bitstream::writeDelta(const T& value, const T& reference) {
|
template<class T> inline void Bitstream::writeDelta(const T& value, const T& reference) {
|
||||||
|
@ -741,133 +766,75 @@ template<class K, class V> inline Bitstream& Bitstream::operator>>(QHash<K, V>&
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef QSharedPointer<TypeReader> TypeReaderPointer;
|
typedef QPair<TypeStreamerPointer, QMetaProperty> StreamerPropertyPair;
|
||||||
|
|
||||||
/// Contains the information required to read a type from the stream.
|
/// Contains the information required to stream an object.
|
||||||
class TypeReader {
|
class ObjectStreamer {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum Type { SIMPLE_TYPE, ENUM_TYPE, STREAMABLE_TYPE, LIST_TYPE, SET_TYPE, MAP_TYPE };
|
ObjectStreamer(const QMetaObject* metaObject);
|
||||||
|
|
||||||
TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL);
|
|
||||||
|
|
||||||
TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, int bits, const QHash<int, int>& mappings);
|
|
||||||
|
|
||||||
TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, const QVector<FieldReader>& fields);
|
|
||||||
|
|
||||||
TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, Type type,
|
|
||||||
const TypeReaderPointer& valueReader);
|
|
||||||
|
|
||||||
TypeReader(const QByteArray& typeName, const TypeStreamer* streamer,
|
|
||||||
const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader);
|
|
||||||
|
|
||||||
const QByteArray& getTypeName() const { return _typeName; }
|
|
||||||
const TypeStreamer* getStreamer() const { return _streamer; }
|
|
||||||
|
|
||||||
QVariant read(Bitstream& in) const;
|
|
||||||
void readDelta(Bitstream& in, QVariant& object, const QVariant& reference) const;
|
|
||||||
void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const;
|
|
||||||
|
|
||||||
bool matchesExactly(const TypeStreamer* streamer) const;
|
|
||||||
|
|
||||||
bool operator==(const TypeReader& other) const { return _typeName == other._typeName; }
|
|
||||||
bool operator!=(const TypeReader& other) const { return _typeName != other._typeName; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
QByteArray _typeName;
|
|
||||||
const TypeStreamer* _streamer;
|
|
||||||
bool _exactMatch;
|
|
||||||
Type _type;
|
|
||||||
int _bits;
|
|
||||||
QHash<int, int> _mappings;
|
|
||||||
TypeReaderPointer _keyReader;
|
|
||||||
TypeReaderPointer _valueReader;
|
|
||||||
QVector<FieldReader> _fields;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint qHash(const TypeReader& typeReader, uint seed = 0);
|
|
||||||
|
|
||||||
QDebug& operator<<(QDebug& debug, const TypeReader& typeReader);
|
|
||||||
|
|
||||||
/// Contains the information required to read a metatype field from the stream and apply it.
|
|
||||||
class FieldReader {
|
|
||||||
public:
|
|
||||||
|
|
||||||
FieldReader(const TypeReader& reader = TypeReader(), int index = -1);
|
|
||||||
|
|
||||||
const TypeReader& getReader() const { return _reader; }
|
|
||||||
int getIndex() const { return _index; }
|
|
||||||
|
|
||||||
void read(Bitstream& in, const TypeStreamer* streamer, QVariant& object) const;
|
|
||||||
void readDelta(Bitstream& in, const TypeStreamer* streamer, QVariant& object, const QVariant& reference) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
TypeReader _reader;
|
|
||||||
int _index;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Contains the information required to read an object from the stream.
|
|
||||||
class ObjectReader {
|
|
||||||
public:
|
|
||||||
|
|
||||||
ObjectReader(const QByteArray& className = QByteArray(), const QMetaObject* metaObject = NULL,
|
|
||||||
const QVector<PropertyReader>& properties = QVector<PropertyReader>());
|
|
||||||
|
|
||||||
const QByteArray& getClassName() const { return _className; }
|
|
||||||
const QMetaObject* getMetaObject() const { return _metaObject; }
|
const QMetaObject* getMetaObject() const { return _metaObject; }
|
||||||
|
const ObjectStreamerPointer& getSelf() const { return _self; }
|
||||||
|
|
||||||
|
virtual const char* getName() const = 0;
|
||||||
|
virtual const QVector<StreamerPropertyPair>& getProperties() const;
|
||||||
|
virtual void writeMetadata(Bitstream& out, bool full) const = 0;
|
||||||
|
virtual void write(Bitstream& out, const QObject* object) const = 0;
|
||||||
|
virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const = 0;
|
||||||
|
virtual QObject* read(Bitstream& in, QObject* object = NULL) const = 0;
|
||||||
|
virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
friend class Bitstream;
|
||||||
|
|
||||||
QObject* read(Bitstream& in, QObject* object = NULL) const;
|
|
||||||
QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const;
|
|
||||||
|
|
||||||
bool operator==(const ObjectReader& other) const { return _className == other._className; }
|
|
||||||
bool operator!=(const ObjectReader& other) const { return _className != other._className; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
QByteArray _className;
|
|
||||||
const QMetaObject* _metaObject;
|
const QMetaObject* _metaObject;
|
||||||
QVector<PropertyReader> _properties;
|
ObjectStreamerPointer _self;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint qHash(const ObjectReader& objectReader, uint seed = 0);
|
/// A streamer that maps to a local class.
|
||||||
|
class MappedObjectStreamer : public ObjectStreamer {
|
||||||
QDebug& operator<<(QDebug& debug, const ObjectReader& objectReader);
|
|
||||||
|
|
||||||
/// Contains the information required to read an object property from the stream and apply it.
|
|
||||||
class PropertyReader {
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
PropertyReader(const TypeReader& reader = TypeReader(), const QMetaProperty& property = QMetaProperty());
|
MappedObjectStreamer(const QMetaObject* metaObject, const QVector<StreamerPropertyPair>& properties);
|
||||||
|
|
||||||
const TypeReader& getReader() const { return _reader; }
|
|
||||||
|
|
||||||
void read(Bitstream& in, QObject* object) const;
|
|
||||||
void readDelta(Bitstream& in, QObject* object, const QObject* reference) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
TypeReader _reader;
|
|
||||||
QMetaProperty _property;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Contains the information required to obtain an object property and write it to the stream.
|
|
||||||
class PropertyWriter {
|
|
||||||
public:
|
|
||||||
|
|
||||||
PropertyWriter(const QMetaProperty& property = QMetaProperty(), const TypeStreamer* streamer = NULL);
|
|
||||||
|
|
||||||
const QMetaProperty& getProperty() const { return _property; }
|
|
||||||
const TypeStreamer* getStreamer() const { return _streamer; }
|
|
||||||
|
|
||||||
void write(Bitstream& out, const QObject* object) const;
|
|
||||||
void writeDelta(Bitstream& out, const QObject* object, const QObject* reference) const;
|
|
||||||
|
|
||||||
|
virtual const char* getName() const;
|
||||||
|
virtual const QVector<StreamerPropertyPair>& getProperties() const;
|
||||||
|
virtual void writeMetadata(Bitstream& out, bool full) const;
|
||||||
|
virtual void write(Bitstream& out, const QObject* object) const;
|
||||||
|
virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const;
|
||||||
|
virtual QObject* read(Bitstream& in, QObject* object = NULL) const;
|
||||||
|
virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QMetaProperty _property;
|
QVector<StreamerPropertyPair> _properties;
|
||||||
const TypeStreamer* _streamer;
|
};
|
||||||
|
|
||||||
|
typedef QPair<TypeStreamerPointer, QByteArray> StreamerNamePair;
|
||||||
|
|
||||||
|
/// A streamer for generic objects.
|
||||||
|
class GenericObjectStreamer : public ObjectStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
GenericObjectStreamer(const QByteArray& name, const QVector<StreamerNamePair>& properties, const QByteArray& hash);
|
||||||
|
|
||||||
|
virtual const char* getName() const;
|
||||||
|
virtual void writeMetadata(Bitstream& out, bool full) const;
|
||||||
|
virtual void write(Bitstream& out, const QObject* object) const;
|
||||||
|
virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const;
|
||||||
|
virtual QObject* read(Bitstream& in, QObject* object = NULL) const;
|
||||||
|
virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class Bitstream;
|
||||||
|
|
||||||
|
QByteArray _name;
|
||||||
|
WeakObjectStreamerPointer _weakSelf;
|
||||||
|
QVector<StreamerNamePair> _properties;
|
||||||
|
QByteArray _hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Describes a metatype field.
|
/// Describes a metatype field.
|
||||||
|
@ -890,27 +857,75 @@ Q_DECLARE_METATYPE(const QMetaObject*)
|
||||||
/// Macro for registering streamable meta-objects.
|
/// Macro for registering streamable meta-objects.
|
||||||
#define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject);
|
#define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject);
|
||||||
|
|
||||||
|
/// Contains a value along with a pointer to its streamer.
|
||||||
|
class GenericValue {
|
||||||
|
public:
|
||||||
|
|
||||||
|
GenericValue(const TypeStreamerPointer& streamer = TypeStreamerPointer(), const QVariant& value = QVariant());
|
||||||
|
|
||||||
|
const TypeStreamerPointer& getStreamer() const { return _streamer; }
|
||||||
|
const QVariant& getValue() const { return _value; }
|
||||||
|
|
||||||
|
bool operator==(const GenericValue& other) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
TypeStreamerPointer _streamer;
|
||||||
|
QVariant _value;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(GenericValue)
|
||||||
|
|
||||||
|
/// Contains a list of property values along with a pointer to their metadata.
|
||||||
|
class GenericSharedObject : public SharedObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
GenericSharedObject(const ObjectStreamerPointer& streamer);
|
||||||
|
|
||||||
|
const ObjectStreamerPointer& getStreamer() const { return _streamer; }
|
||||||
|
|
||||||
|
void setValues(const QVariantList& values) { _values = values; }
|
||||||
|
const QVariantList& getValues() const { return _values; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
ObjectStreamerPointer _streamer;
|
||||||
|
QVariantList _values;
|
||||||
|
};
|
||||||
|
|
||||||
/// Interface for objects that can write values to and read values from bitstreams.
|
/// Interface for objects that can write values to and read values from bitstreams.
|
||||||
class TypeStreamer {
|
class TypeStreamer {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
enum Category { SIMPLE_CATEGORY, ENUM_CATEGORY, STREAMABLE_CATEGORY, LIST_CATEGORY, SET_CATEGORY, MAP_CATEGORY };
|
||||||
|
|
||||||
virtual ~TypeStreamer();
|
virtual ~TypeStreamer();
|
||||||
|
|
||||||
void setType(int type) { _type = type; }
|
|
||||||
int getType() const { return _type; }
|
int getType() const { return _type; }
|
||||||
|
|
||||||
|
const TypeStreamerPointer& getSelf() const { return _self; }
|
||||||
|
|
||||||
virtual const char* getName() const;
|
virtual const char* getName() const;
|
||||||
|
|
||||||
virtual bool equal(const QVariant& first, const QVariant& second) const = 0;
|
virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const;
|
||||||
|
|
||||||
virtual void write(Bitstream& out, const QVariant& value) const = 0;
|
virtual void writeMetadata(Bitstream& out, bool full) const;
|
||||||
virtual QVariant read(Bitstream& in) const = 0;
|
|
||||||
|
virtual bool equal(const QVariant& first, const QVariant& second) const;
|
||||||
|
|
||||||
|
virtual void write(Bitstream& out, const QVariant& value) const;
|
||||||
|
virtual QVariant read(Bitstream& in) const;
|
||||||
|
|
||||||
virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const = 0;
|
virtual void writeVariant(Bitstream& out, const QVariant& value) const;
|
||||||
virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const = 0;
|
virtual QVariant readVariant(Bitstream& in) const;
|
||||||
|
|
||||||
virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const = 0;
|
virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const;
|
||||||
virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const = 0;
|
virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const;
|
||||||
|
|
||||||
|
virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const;
|
||||||
|
virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const;
|
||||||
|
|
||||||
virtual void setEnumValue(QVariant& object, int value, const QHash<int, int>& mappings) const;
|
virtual void setEnumValue(QVariant& object, int value, const QHash<int, int>& mappings) const;
|
||||||
|
|
||||||
|
@ -919,7 +934,7 @@ public:
|
||||||
virtual void setField(QVariant& object, int index, const QVariant& value) const;
|
virtual void setField(QVariant& object, int index, const QVariant& value) const;
|
||||||
virtual QVariant getField(const QVariant& object, int index) const;
|
virtual QVariant getField(const QVariant& object, int index) const;
|
||||||
|
|
||||||
virtual TypeReader::Type getReaderType() const;
|
virtual Category getCategory() const;
|
||||||
|
|
||||||
virtual int getBits() const;
|
virtual int getBits() const;
|
||||||
virtual QMetaEnum getMetaEnum() const;
|
virtual QMetaEnum getMetaEnum() const;
|
||||||
|
@ -937,9 +952,12 @@ public:
|
||||||
virtual QVariant getValue(const QVariant& object, int index) const;
|
virtual QVariant getValue(const QVariant& object, int index) const;
|
||||||
virtual void setValue(QVariant& object, int index, const QVariant& value) const;
|
virtual void setValue(QVariant& object, int index, const QVariant& value) const;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
|
||||||
|
friend class Bitstream;
|
||||||
|
|
||||||
int _type;
|
int _type;
|
||||||
|
TypeStreamerPointer _self;
|
||||||
};
|
};
|
||||||
|
|
||||||
QDebug& operator<<(QDebug& debug, const TypeStreamer* typeStreamer);
|
QDebug& operator<<(QDebug& debug, const TypeStreamer* typeStreamer);
|
||||||
|
@ -971,7 +989,8 @@ public:
|
||||||
EnumTypeStreamer(const QMetaEnum& metaEnum);
|
EnumTypeStreamer(const QMetaEnum& metaEnum);
|
||||||
|
|
||||||
virtual const char* getName() const;
|
virtual const char* getName() const;
|
||||||
virtual TypeReader::Type getReaderType() const;
|
virtual void writeMetadata(Bitstream& out, bool full) const;
|
||||||
|
virtual Category getCategory() const;
|
||||||
virtual int getBits() const;
|
virtual int getBits() const;
|
||||||
virtual QMetaEnum getMetaEnum() const;
|
virtual QMetaEnum getMetaEnum() const;
|
||||||
virtual bool equal(const QVariant& first, const QVariant& second) const;
|
virtual bool equal(const QVariant& first, const QVariant& second) const;
|
||||||
|
@ -992,11 +1011,62 @@ private:
|
||||||
int _bits;
|
int _bits;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A streamer class for enums that maps to a local type.
|
||||||
|
class MappedEnumTypeStreamer : public TypeStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
MappedEnumTypeStreamer(const TypeStreamer* baseStreamer, int bits, const QHash<int, int>& mappings);
|
||||||
|
|
||||||
|
virtual QVariant read(Bitstream& in) const;
|
||||||
|
virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const TypeStreamer* _baseStreamer;
|
||||||
|
int _bits;
|
||||||
|
QHash<int, int> _mappings;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Base class for generic type streamers, which contain all the metadata required to write out a type.
|
||||||
|
class GenericTypeStreamer : public TypeStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
GenericTypeStreamer(const QByteArray& name);
|
||||||
|
|
||||||
|
virtual const char* getName() const;
|
||||||
|
virtual QVariant readVariant(Bitstream& in) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
friend class Bitstream;
|
||||||
|
|
||||||
|
QByteArray _name;
|
||||||
|
WeakTypeStreamerPointer _weakSelf;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A streamer for generic enums.
|
||||||
|
class GenericEnumTypeStreamer : public GenericTypeStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
GenericEnumTypeStreamer(const QByteArray& name, const QVector<NameIntPair>& values, int bits, const QByteArray& hash);
|
||||||
|
|
||||||
|
virtual void writeMetadata(Bitstream& out, bool full) const;
|
||||||
|
virtual void write(Bitstream& out, const QVariant& value) const;
|
||||||
|
virtual QVariant read(Bitstream& in) const;
|
||||||
|
virtual Category getCategory() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QVector<NameIntPair> _values;
|
||||||
|
int _bits;
|
||||||
|
QByteArray _hash;
|
||||||
|
};
|
||||||
|
|
||||||
/// A streamer for types compiled by mtc.
|
/// A streamer for types compiled by mtc.
|
||||||
template<class T> class StreamableTypeStreamer : public SimpleTypeStreamer<T> {
|
template<class T> class StreamableTypeStreamer : public SimpleTypeStreamer<T> {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual TypeReader::Type getReaderType() const { return TypeReader::STREAMABLE_TYPE; }
|
virtual TypeStreamer::Category getCategory() const { return TypeStreamer::STREAMABLE_CATEGORY; }
|
||||||
virtual const QVector<MetaField>& getMetaFields() const { return T::getMetaFields(); }
|
virtual const QVector<MetaField>& getMetaFields() const { return T::getMetaFields(); }
|
||||||
virtual int getFieldIndex(const QByteArray& name) const { return T::getFieldIndex(name); }
|
virtual int getFieldIndex(const QByteArray& name) const { return T::getFieldIndex(name); }
|
||||||
virtual void setField(QVariant& object, int index, const QVariant& value) const {
|
virtual void setField(QVariant& object, int index, const QVariant& value) const {
|
||||||
|
@ -1005,6 +1075,40 @@ public:
|
||||||
return static_cast<const T*>(object.constData())->getField(index); }
|
return static_cast<const T*>(object.constData())->getField(index); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef QPair<TypeStreamerPointer, int> StreamerIndexPair;
|
||||||
|
|
||||||
|
/// A streamer class for streamables that maps to a local type.
|
||||||
|
class MappedStreamableTypeStreamer : public TypeStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
MappedStreamableTypeStreamer(const TypeStreamer* baseStreamer, const QVector<StreamerIndexPair>& fields);
|
||||||
|
|
||||||
|
virtual QVariant read(Bitstream& in) const;
|
||||||
|
virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const TypeStreamer* _baseStreamer;
|
||||||
|
QVector<StreamerIndexPair> _fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A streamer for generic enums.
|
||||||
|
class GenericStreamableTypeStreamer : public GenericTypeStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
GenericStreamableTypeStreamer(const QByteArray& name, const QVector<StreamerNamePair>& fields, const QByteArray& hash);
|
||||||
|
|
||||||
|
virtual void writeMetadata(Bitstream& out, bool full) const;
|
||||||
|
virtual void write(Bitstream& out, const QVariant& value) const;
|
||||||
|
virtual QVariant read(Bitstream& in) const;
|
||||||
|
virtual Category getCategory() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QVector<StreamerNamePair> _fields;
|
||||||
|
QByteArray _hash;
|
||||||
|
};
|
||||||
|
|
||||||
/// Base template for collection streamers.
|
/// Base template for collection streamers.
|
||||||
template<class T> class CollectionTypeStreamer : public SimpleTypeStreamer<T> {
|
template<class T> class CollectionTypeStreamer : public SimpleTypeStreamer<T> {
|
||||||
};
|
};
|
||||||
|
@ -1013,7 +1117,8 @@ template<class T> class CollectionTypeStreamer : public SimpleTypeStreamer<T> {
|
||||||
template<class T> class CollectionTypeStreamer<QList<T> > : public SimpleTypeStreamer<QList<T> > {
|
template<class T> class CollectionTypeStreamer<QList<T> > : public SimpleTypeStreamer<QList<T> > {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual TypeReader::Type getReaderType() const { return TypeReader::LIST_TYPE; }
|
virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); }
|
||||||
|
virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; }
|
||||||
virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<T>()); }
|
virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<T>()); }
|
||||||
virtual void insert(QVariant& object, const QVariant& value) const {
|
virtual void insert(QVariant& object, const QVariant& value) const {
|
||||||
static_cast<QList<T>*>(object.data())->append(value.value<T>()); }
|
static_cast<QList<T>*>(object.data())->append(value.value<T>()); }
|
||||||
|
@ -1029,7 +1134,8 @@ public:
|
||||||
template<class T> class CollectionTypeStreamer<QVector<T> > : public SimpleTypeStreamer<QVector<T> > {
|
template<class T> class CollectionTypeStreamer<QVector<T> > : public SimpleTypeStreamer<QVector<T> > {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual TypeReader::Type getReaderType() const { return TypeReader::LIST_TYPE; }
|
virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); }
|
||||||
|
virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; }
|
||||||
virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<T>()); }
|
virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<T>()); }
|
||||||
virtual void insert(QVariant& object, const QVariant& value) const {
|
virtual void insert(QVariant& object, const QVariant& value) const {
|
||||||
static_cast<QVector<T>*>(object.data())->append(value.value<T>()); }
|
static_cast<QVector<T>*>(object.data())->append(value.value<T>()); }
|
||||||
|
@ -1041,11 +1147,43 @@ public:
|
||||||
static_cast<QVector<T>*>(object.data())->replace(index, value.value<T>()); }
|
static_cast<QVector<T>*>(object.data())->replace(index, value.value<T>()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A streamer for lists that maps to a local type.
|
||||||
|
class MappedListTypeStreamer : public TypeStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
MappedListTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer);
|
||||||
|
|
||||||
|
virtual QVariant read(Bitstream& in) const;
|
||||||
|
virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
const TypeStreamer* _baseStreamer;
|
||||||
|
TypeStreamerPointer _valueStreamer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A streamer for generic lists.
|
||||||
|
class GenericListTypeStreamer : public GenericTypeStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
GenericListTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer);
|
||||||
|
|
||||||
|
virtual void writeMetadata(Bitstream& out, bool full) const;
|
||||||
|
virtual void write(Bitstream& out, const QVariant& value) const;
|
||||||
|
virtual QVariant read(Bitstream& in) const;
|
||||||
|
virtual Category getCategory() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
TypeStreamerPointer _valueStreamer;
|
||||||
|
};
|
||||||
|
|
||||||
/// A streamer for set types.
|
/// A streamer for set types.
|
||||||
template<class T> class CollectionTypeStreamer<QSet<T> > : public SimpleTypeStreamer<QSet<T> > {
|
template<class T> class CollectionTypeStreamer<QSet<T> > : public SimpleTypeStreamer<QSet<T> > {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual TypeReader::Type getReaderType() const { return TypeReader::SET_TYPE; }
|
virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); }
|
||||||
|
virtual TypeStreamer::Category getCategory() const { return TypeStreamer::SET_CATEGORY; }
|
||||||
virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<T>()); }
|
virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<T>()); }
|
||||||
virtual void insert(QVariant& object, const QVariant& value) const {
|
virtual void insert(QVariant& object, const QVariant& value) const {
|
||||||
static_cast<QSet<T>*>(object.data())->insert(value.value<T>()); }
|
static_cast<QSet<T>*>(object.data())->insert(value.value<T>()); }
|
||||||
|
@ -1053,11 +1191,30 @@ public:
|
||||||
return static_cast<QSet<T>*>(object.data())->remove(key.value<T>()); }
|
return static_cast<QSet<T>*>(object.data())->remove(key.value<T>()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A streamer for sets that maps to a local type.
|
||||||
|
class MappedSetTypeStreamer : public MappedListTypeStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
MappedSetTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer);
|
||||||
|
|
||||||
|
virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A streamer for generic sets.
|
||||||
|
class GenericSetTypeStreamer : public GenericListTypeStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
GenericSetTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer);
|
||||||
|
|
||||||
|
virtual Category getCategory() const;
|
||||||
|
};
|
||||||
|
|
||||||
/// A streamer for hash types.
|
/// A streamer for hash types.
|
||||||
template<class K, class V> class CollectionTypeStreamer<QHash<K, V> > : public SimpleTypeStreamer<QHash<K, V> > {
|
template<class K, class V> class CollectionTypeStreamer<QHash<K, V> > : public SimpleTypeStreamer<QHash<K, V> > {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual TypeReader::Type getReaderType() const { return TypeReader::MAP_TYPE; }
|
virtual void writeMetadata(Bitstream& out, bool full) const { out << getKeyStreamer() << getValueStreamer(); }
|
||||||
|
virtual TypeStreamer::Category getCategory() const { return TypeStreamer::MAP_CATEGORY; }
|
||||||
virtual const TypeStreamer* getKeyStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<K>()); }
|
virtual const TypeStreamer* getKeyStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<K>()); }
|
||||||
virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<V>()); }
|
virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<V>()); }
|
||||||
virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const {
|
virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const {
|
||||||
|
@ -1068,6 +1225,48 @@ public:
|
||||||
return QVariant::fromValue(static_cast<const QHash<K, V>*>(object.constData())->value(key.value<K>())); }
|
return QVariant::fromValue(static_cast<const QHash<K, V>*>(object.constData())->value(key.value<K>())); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A streamer for maps that maps to a local type.
|
||||||
|
class MappedMapTypeStreamer : public TypeStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
MappedMapTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& keyStreamer,
|
||||||
|
const TypeStreamerPointer& valueStreamer);
|
||||||
|
|
||||||
|
virtual QVariant read(Bitstream& in) const;
|
||||||
|
virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const TypeStreamer* _baseStreamer;
|
||||||
|
TypeStreamerPointer _keyStreamer;
|
||||||
|
TypeStreamerPointer _valueStreamer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A streamer for generic maps.
|
||||||
|
class GenericMapTypeStreamer : public GenericTypeStreamer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
GenericMapTypeStreamer(const QByteArray& name, const TypeStreamerPointer& keyStreamer,
|
||||||
|
const TypeStreamerPointer& valueStreamer);
|
||||||
|
|
||||||
|
virtual void writeMetadata(Bitstream& out, bool full) const;
|
||||||
|
virtual void write(Bitstream& out, const QVariant& value) const;
|
||||||
|
virtual QVariant read(Bitstream& in) const;
|
||||||
|
virtual Category getCategory() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
TypeStreamerPointer _keyStreamer;
|
||||||
|
TypeStreamerPointer _valueStreamer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A streamer class for generic values.
|
||||||
|
class GenericValueStreamer : public SimpleTypeStreamer<GenericValue> {
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void writeVariant(Bitstream& out, const QVariant& value) const;
|
||||||
|
};
|
||||||
|
|
||||||
/// Macro for registering simple type streamers.
|
/// Macro for registering simple type streamers.
|
||||||
#define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \
|
#define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \
|
||||||
Bitstream::registerTypeStreamer(qMetaTypeId<X>(), new SimpleTypeStreamer<X>());
|
Bitstream::registerTypeStreamer(qMetaTypeId<X>(), new SimpleTypeStreamer<X>());
|
||||||
|
|
|
@ -90,6 +90,7 @@ bool operator==(const QScriptValue& first, const QScriptValue& second) {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// if none of the above tests apply, first must be invalid
|
||||||
return !second.isValid();
|
return !second.isValid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,11 @@ SharedObject::SharedObject() :
|
||||||
_weakHash.insert(_id, this);
|
_weakHash.insert(_id, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SharedObject::setID(int id) {
|
||||||
|
_weakHash.remove(_id);
|
||||||
|
_weakHash.insert(_id = id, this);
|
||||||
|
}
|
||||||
|
|
||||||
void SharedObject::incrementReferenceCount() {
|
void SharedObject::incrementReferenceCount() {
|
||||||
_referenceCount.ref();
|
_referenceCount.ref();
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,8 @@ public:
|
||||||
/// Returns the unique local ID for this object.
|
/// Returns the unique local ID for this object.
|
||||||
int getID() const { return _id; }
|
int getID() const { return _id; }
|
||||||
|
|
||||||
|
void setID(int id);
|
||||||
|
|
||||||
/// Returns the local origin ID for this object.
|
/// Returns the local origin ID for this object.
|
||||||
int getOriginID() const { return _originID; }
|
int getOriginID() const { return _originID; }
|
||||||
|
|
||||||
|
|
|
@ -55,13 +55,13 @@ AccountManager::AccountManager() :
|
||||||
{
|
{
|
||||||
qRegisterMetaType<OAuthAccessToken>("OAuthAccessToken");
|
qRegisterMetaType<OAuthAccessToken>("OAuthAccessToken");
|
||||||
qRegisterMetaTypeStreamOperators<OAuthAccessToken>("OAuthAccessToken");
|
qRegisterMetaTypeStreamOperators<OAuthAccessToken>("OAuthAccessToken");
|
||||||
|
|
||||||
qRegisterMetaType<DataServerAccountInfo>("DataServerAccountInfo");
|
qRegisterMetaType<DataServerAccountInfo>("DataServerAccountInfo");
|
||||||
qRegisterMetaTypeStreamOperators<DataServerAccountInfo>("DataServerAccountInfo");
|
qRegisterMetaTypeStreamOperators<DataServerAccountInfo>("DataServerAccountInfo");
|
||||||
|
|
||||||
qRegisterMetaType<QNetworkAccessManager::Operation>("QNetworkAccessManager::Operation");
|
qRegisterMetaType<QNetworkAccessManager::Operation>("QNetworkAccessManager::Operation");
|
||||||
qRegisterMetaType<JSONCallbackParameters>("JSONCallbackParameters");
|
qRegisterMetaType<JSONCallbackParameters>("JSONCallbackParameters");
|
||||||
|
|
||||||
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
|
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,18 +70,18 @@ const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash";
|
||||||
void AccountManager::logout() {
|
void AccountManager::logout() {
|
||||||
// a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file
|
// a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file
|
||||||
_accountInfo = DataServerAccountInfo();
|
_accountInfo = DataServerAccountInfo();
|
||||||
|
|
||||||
emit balanceChanged(0);
|
emit balanceChanged(0);
|
||||||
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
|
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
|
||||||
|
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
settings.beginGroup(ACCOUNTS_GROUP);
|
settings.beginGroup(ACCOUNTS_GROUP);
|
||||||
|
|
||||||
QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE));
|
QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE));
|
||||||
settings.remove(keyURLString);
|
settings.remove(keyURLString);
|
||||||
|
|
||||||
qDebug() << "Removed account info for" << _authURL << "from in-memory accounts and .ini file";
|
qDebug() << "Removed account info for" << _authURL << "from in-memory accounts and .ini file";
|
||||||
|
|
||||||
emit logoutComplete();
|
emit logoutComplete();
|
||||||
// the username has changed to blank
|
// the username has changed to blank
|
||||||
emit usernameChanged(QString());
|
emit usernameChanged(QString());
|
||||||
|
@ -93,7 +93,7 @@ void AccountManager::updateBalance() {
|
||||||
JSONCallbackParameters callbackParameters;
|
JSONCallbackParameters callbackParameters;
|
||||||
callbackParameters.jsonCallbackReceiver = &_accountInfo;
|
callbackParameters.jsonCallbackReceiver = &_accountInfo;
|
||||||
callbackParameters.jsonCallbackMethod = "setBalanceFromJSON";
|
callbackParameters.jsonCallbackMethod = "setBalanceFromJSON";
|
||||||
|
|
||||||
authenticatedRequest("/api/v1/wallets/mine", QNetworkAccessManager::GetOperation, callbackParameters);
|
authenticatedRequest("/api/v1/wallets/mine", QNetworkAccessManager::GetOperation, callbackParameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,28 +105,33 @@ void AccountManager::accountInfoBalanceChanged(qint64 newBalance) {
|
||||||
void AccountManager::setAuthURL(const QUrl& authURL) {
|
void AccountManager::setAuthURL(const QUrl& authURL) {
|
||||||
if (_authURL != authURL) {
|
if (_authURL != authURL) {
|
||||||
_authURL = authURL;
|
_authURL = authURL;
|
||||||
|
|
||||||
qDebug() << "URL for node authentication has been changed to" << qPrintable(_authURL.toString());
|
qDebug() << "URL for node authentication has been changed to" << qPrintable(_authURL.toString());
|
||||||
qDebug() << "Re-setting authentication flow.";
|
qDebug() << "Re-setting authentication flow.";
|
||||||
|
|
||||||
// check if there are existing access tokens to load from settings
|
// check if there are existing access tokens to load from settings
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
settings.beginGroup(ACCOUNTS_GROUP);
|
settings.beginGroup(ACCOUNTS_GROUP);
|
||||||
|
|
||||||
foreach(const QString& key, settings.allKeys()) {
|
foreach(const QString& key, settings.allKeys()) {
|
||||||
// take a key copy to perform the double slash replacement
|
// take a key copy to perform the double slash replacement
|
||||||
QString keyCopy(key);
|
QString keyCopy(key);
|
||||||
QUrl keyURL(keyCopy.replace("slashslash", "//"));
|
QUrl keyURL(keyCopy.replace("slashslash", "//"));
|
||||||
|
|
||||||
if (keyURL == _authURL) {
|
if (keyURL == _authURL) {
|
||||||
// pull out the stored access token and store it in memory
|
// pull out the stored access token and store it in memory
|
||||||
_accountInfo = settings.value(key).value<DataServerAccountInfo>();
|
_accountInfo = settings.value(key).value<DataServerAccountInfo>();
|
||||||
qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString());
|
qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString());
|
||||||
|
|
||||||
emit accessTokenChanged();
|
// profile info isn't guaranteed to be saved too
|
||||||
|
if (_accountInfo.hasProfile()) {
|
||||||
|
emit profileChanged();
|
||||||
|
} else {
|
||||||
|
requestProfile();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tell listeners that the auth endpoint has changed
|
// tell listeners that the auth endpoint has changed
|
||||||
emit authEndpointChanged();
|
emit authEndpointChanged();
|
||||||
}
|
}
|
||||||
|
@ -147,36 +152,36 @@ void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessMan
|
||||||
void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
||||||
const JSONCallbackParameters& callbackParams,
|
const JSONCallbackParameters& callbackParams,
|
||||||
const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) {
|
const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) {
|
||||||
|
|
||||||
if (!_networkAccessManager) {
|
if (!_networkAccessManager) {
|
||||||
_networkAccessManager = new QNetworkAccessManager(this);
|
_networkAccessManager = new QNetworkAccessManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasValidAccessToken()) {
|
if (hasValidAccessToken()) {
|
||||||
QNetworkRequest authenticatedRequest;
|
QNetworkRequest authenticatedRequest;
|
||||||
|
|
||||||
QUrl requestURL = _authURL;
|
QUrl requestURL = _authURL;
|
||||||
|
|
||||||
if (path.startsWith("/")) {
|
if (path.startsWith("/")) {
|
||||||
requestURL.setPath(path);
|
requestURL.setPath(path);
|
||||||
} else {
|
} else {
|
||||||
requestURL.setPath("/" + path);
|
requestURL.setPath("/" + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestURL.setQuery("access_token=" + _accountInfo.getAccessToken().token);
|
requestURL.setQuery("access_token=" + _accountInfo.getAccessToken().token);
|
||||||
|
|
||||||
authenticatedRequest.setUrl(requestURL);
|
authenticatedRequest.setUrl(requestURL);
|
||||||
|
|
||||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||||
qDebug() << "Making an authenticated request to" << qPrintable(requestURL.toString());
|
qDebug() << "Making an authenticated request to" << qPrintable(requestURL.toString());
|
||||||
|
|
||||||
if (!dataByteArray.isEmpty()) {
|
if (!dataByteArray.isEmpty()) {
|
||||||
qDebug() << "The POST/PUT body -" << QString(dataByteArray);
|
qDebug() << "The POST/PUT body -" << QString(dataByteArray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply* networkReply = NULL;
|
QNetworkReply* networkReply = NULL;
|
||||||
|
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case QNetworkAccessManager::GetOperation:
|
case QNetworkAccessManager::GetOperation:
|
||||||
networkReply = _networkAccessManager->get(authenticatedRequest);
|
networkReply = _networkAccessManager->get(authenticatedRequest);
|
||||||
|
@ -198,24 +203,24 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
|
||||||
networkReply = _networkAccessManager->put(authenticatedRequest, dataByteArray);
|
networkReply = _networkAccessManager->put(authenticatedRequest, dataByteArray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// other methods not yet handled
|
// other methods not yet handled
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (networkReply) {
|
if (networkReply) {
|
||||||
if (!callbackParams.isEmpty()) {
|
if (!callbackParams.isEmpty()) {
|
||||||
// if we have information for a callback, insert the callbackParams into our local map
|
// if we have information for a callback, insert the callbackParams into our local map
|
||||||
_pendingCallbackMap.insert(networkReply, callbackParams);
|
_pendingCallbackMap.insert(networkReply, callbackParams);
|
||||||
|
|
||||||
if (callbackParams.updateReciever && !callbackParams.updateSlot.isEmpty()) {
|
if (callbackParams.updateReciever && !callbackParams.updateSlot.isEmpty()) {
|
||||||
callbackParams.updateReciever->connect(networkReply, SIGNAL(uploadProgress(qint64, qint64)),
|
callbackParams.updateReciever->connect(networkReply, SIGNAL(uploadProgress(qint64, qint64)),
|
||||||
callbackParams.updateSlot.toStdString().c_str());
|
callbackParams.updateSlot.toStdString().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we ended up firing of a request, hook up to it now
|
// if we ended up firing of a request, hook up to it now
|
||||||
connect(networkReply, SIGNAL(finished()), SLOT(processReply()));
|
connect(networkReply, SIGNAL(finished()), SLOT(processReply()));
|
||||||
}
|
}
|
||||||
|
@ -224,7 +229,7 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
|
||||||
|
|
||||||
void AccountManager::processReply() {
|
void AccountManager::processReply() {
|
||||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||||
|
|
||||||
if (requestReply->error() == QNetworkReply::NoError) {
|
if (requestReply->error() == QNetworkReply::NoError) {
|
||||||
passSuccessToCallback(requestReply);
|
passSuccessToCallback(requestReply);
|
||||||
} else {
|
} else {
|
||||||
|
@ -235,17 +240,17 @@ void AccountManager::processReply() {
|
||||||
|
|
||||||
void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
|
void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
|
||||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||||
|
|
||||||
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
|
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
|
||||||
|
|
||||||
if (callbackParams.jsonCallbackReceiver) {
|
if (callbackParams.jsonCallbackReceiver) {
|
||||||
// invoke the right method on the callback receiver
|
// invoke the right method on the callback receiver
|
||||||
QMetaObject::invokeMethod(callbackParams.jsonCallbackReceiver, qPrintable(callbackParams.jsonCallbackMethod),
|
QMetaObject::invokeMethod(callbackParams.jsonCallbackReceiver, qPrintable(callbackParams.jsonCallbackMethod),
|
||||||
Q_ARG(const QJsonObject&, jsonResponse.object()));
|
Q_ARG(const QJsonObject&, jsonResponse.object()));
|
||||||
|
|
||||||
// remove the related reply-callback group from the map
|
// remove the related reply-callback group from the map
|
||||||
_pendingCallbackMap.remove(requestReply);
|
_pendingCallbackMap.remove(requestReply);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||||
qDebug() << "Received JSON response from data-server that has no matching callback.";
|
qDebug() << "Received JSON response from data-server that has no matching callback.";
|
||||||
|
@ -256,13 +261,13 @@ void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
|
||||||
|
|
||||||
void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
|
void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
|
||||||
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
|
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
|
||||||
|
|
||||||
if (callbackParams.errorCallbackReceiver) {
|
if (callbackParams.errorCallbackReceiver) {
|
||||||
// invoke the right method on the callback receiver
|
// invoke the right method on the callback receiver
|
||||||
QMetaObject::invokeMethod(callbackParams.errorCallbackReceiver, qPrintable(callbackParams.errorCallbackMethod),
|
QMetaObject::invokeMethod(callbackParams.errorCallbackReceiver, qPrintable(callbackParams.errorCallbackMethod),
|
||||||
Q_ARG(QNetworkReply::NetworkError, requestReply->error()),
|
Q_ARG(QNetworkReply::NetworkError, requestReply->error()),
|
||||||
Q_ARG(const QString&, requestReply->errorString()));
|
Q_ARG(const QString&, requestReply->errorString()));
|
||||||
|
|
||||||
// remove the related reply-callback group from the map
|
// remove the related reply-callback group from the map
|
||||||
_pendingCallbackMap.remove(requestReply);
|
_pendingCallbackMap.remove(requestReply);
|
||||||
} else {
|
} else {
|
||||||
|
@ -274,12 +279,12 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AccountManager::hasValidAccessToken() {
|
bool AccountManager::hasValidAccessToken() {
|
||||||
|
|
||||||
if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) {
|
if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) {
|
||||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||||
qDebug() << "An access token is required for requests to" << qPrintable(_authURL.toString());
|
qDebug() << "An access token is required for requests to" << qPrintable(_authURL.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
|
@ -288,12 +293,12 @@ bool AccountManager::hasValidAccessToken() {
|
||||||
|
|
||||||
bool AccountManager::checkAndSignalForAccessToken() {
|
bool AccountManager::checkAndSignalForAccessToken() {
|
||||||
bool hasToken = hasValidAccessToken();
|
bool hasToken = hasValidAccessToken();
|
||||||
|
|
||||||
if (!hasToken) {
|
if (!hasToken) {
|
||||||
// emit a signal so somebody can call back to us and request an access token given a username and password
|
// emit a signal so somebody can call back to us and request an access token given a username and password
|
||||||
emit authRequired();
|
emit authRequired();
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasToken;
|
return hasToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,36 +309,36 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
|
|
||||||
QUrl grantURL = _authURL;
|
QUrl grantURL = _authURL;
|
||||||
grantURL.setPath("/oauth/token");
|
grantURL.setPath("/oauth/token");
|
||||||
|
|
||||||
const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
|
const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
|
||||||
|
|
||||||
QByteArray postData;
|
QByteArray postData;
|
||||||
postData.append("grant_type=password&");
|
postData.append("grant_type=password&");
|
||||||
postData.append("username=" + login + "&");
|
postData.append("username=" + login + "&");
|
||||||
postData.append("password=" + QUrl::toPercentEncoding(password) + "&");
|
postData.append("password=" + QUrl::toPercentEncoding(password) + "&");
|
||||||
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE);
|
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE);
|
||||||
|
|
||||||
request.setUrl(grantURL);
|
request.setUrl(grantURL);
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
QNetworkReply* requestReply = _networkAccessManager->post(request, postData);
|
QNetworkReply* requestReply = _networkAccessManager->post(request, postData);
|
||||||
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestFinished);
|
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished);
|
||||||
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError)));
|
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AccountManager::requestFinished() {
|
void AccountManager::requestAccessTokenFinished() {
|
||||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||||
|
|
||||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||||
const QJsonObject& rootObject = jsonResponse.object();
|
const QJsonObject& rootObject = jsonResponse.object();
|
||||||
|
|
||||||
if (!rootObject.contains("error")) {
|
if (!rootObject.contains("error")) {
|
||||||
// construct an OAuthAccessToken from the json object
|
// construct an OAuthAccessToken from the json object
|
||||||
|
|
||||||
if (!rootObject.contains("access_token") || !rootObject.contains("expires_in")
|
if (!rootObject.contains("access_token") || !rootObject.contains("expires_in")
|
||||||
|| !rootObject.contains("token_type")) {
|
|| !rootObject.contains("token_type")) {
|
||||||
// TODO: error handling - malformed token response
|
// TODO: error handling - malformed token response
|
||||||
|
@ -342,23 +347,21 @@ void AccountManager::requestFinished() {
|
||||||
// clear the path from the response URL so we have the right root URL for this access token
|
// clear the path from the response URL so we have the right root URL for this access token
|
||||||
QUrl rootURL = requestReply->url();
|
QUrl rootURL = requestReply->url();
|
||||||
rootURL.setPath("");
|
rootURL.setPath("");
|
||||||
|
|
||||||
qDebug() << "Storing an account with access-token for" << qPrintable(rootURL.toString());
|
qDebug() << "Storing an account with access-token for" << qPrintable(rootURL.toString());
|
||||||
|
|
||||||
_accountInfo = DataServerAccountInfo(rootObject);
|
_accountInfo = DataServerAccountInfo();
|
||||||
|
_accountInfo.setAccessTokenFromJSON(rootObject);
|
||||||
|
|
||||||
emit loginComplete(rootURL);
|
emit loginComplete(rootURL);
|
||||||
// the username has changed to whatever came back
|
|
||||||
emit usernameChanged(_accountInfo.getUsername());
|
|
||||||
|
|
||||||
// we have found or requested an access token
|
|
||||||
emit accessTokenChanged();
|
|
||||||
|
|
||||||
// store this access token into the local settings
|
// store this access token into the local settings
|
||||||
QSettings localSettings;
|
QSettings localSettings;
|
||||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||||
QVariant::fromValue(_accountInfo));
|
QVariant::fromValue(_accountInfo));
|
||||||
|
|
||||||
|
requestProfile();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: error handling
|
// TODO: error handling
|
||||||
|
@ -367,7 +370,53 @@ void AccountManager::requestFinished() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccountManager::requestError(QNetworkReply::NetworkError error) {
|
void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) {
|
||||||
// TODO: error handling
|
// TODO: error handling
|
||||||
qDebug() << "AccountManager requestError - " << error;
|
qDebug() << "AccountManager requestError - " << error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AccountManager::requestProfile() {
|
||||||
|
if (!_networkAccessManager) {
|
||||||
|
_networkAccessManager = new QNetworkAccessManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl profileURL = _authURL;
|
||||||
|
profileURL.setPath("/api/v1/users/profile");
|
||||||
|
profileURL.setQuery("access_token=" + _accountInfo.getAccessToken().token);
|
||||||
|
|
||||||
|
QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL));
|
||||||
|
connect(profileReply, &QNetworkReply::finished, this, &AccountManager::requestProfileFinished);
|
||||||
|
connect(profileReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountManager::requestProfileFinished() {
|
||||||
|
QNetworkReply* profileReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||||
|
|
||||||
|
QJsonDocument jsonResponse = QJsonDocument::fromJson(profileReply->readAll());
|
||||||
|
const QJsonObject& rootObject = jsonResponse.object();
|
||||||
|
|
||||||
|
if (rootObject.contains("status") && rootObject["status"].toString() == "success") {
|
||||||
|
_accountInfo.setProfileInfoFromJSON(rootObject);
|
||||||
|
|
||||||
|
emit profileChanged();
|
||||||
|
|
||||||
|
// the username has changed to whatever came back
|
||||||
|
emit usernameChanged(_accountInfo.getUsername());
|
||||||
|
|
||||||
|
// store the whole profile into the local settings
|
||||||
|
QUrl rootURL = profileReply->url();
|
||||||
|
rootURL.setPath("");
|
||||||
|
QSettings localSettings;
|
||||||
|
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||||
|
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||||
|
QVariant::fromValue(_accountInfo));
|
||||||
|
} else {
|
||||||
|
// TODO: error handling
|
||||||
|
qDebug() << "Error in response for profile";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountManager::requestProfileError(QNetworkReply::NetworkError error) {
|
||||||
|
// TODO: error handling
|
||||||
|
qDebug() << "AccountManager requestProfileError - " << error;
|
||||||
|
}
|
||||||
|
|
|
@ -23,9 +23,9 @@
|
||||||
class JSONCallbackParameters {
|
class JSONCallbackParameters {
|
||||||
public:
|
public:
|
||||||
JSONCallbackParameters();
|
JSONCallbackParameters();
|
||||||
|
|
||||||
bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; }
|
bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; }
|
||||||
|
|
||||||
QObject* jsonCallbackReceiver;
|
QObject* jsonCallbackReceiver;
|
||||||
QString jsonCallbackMethod;
|
QString jsonCallbackMethod;
|
||||||
QObject* errorCallbackReceiver;
|
QObject* errorCallbackReceiver;
|
||||||
|
@ -38,30 +38,33 @@ class AccountManager : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
static AccountManager& getInstance();
|
static AccountManager& getInstance();
|
||||||
|
|
||||||
void authenticatedRequest(const QString& path,
|
void authenticatedRequest(const QString& path,
|
||||||
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
|
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
|
||||||
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
|
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
|
||||||
const QByteArray& dataByteArray = QByteArray(),
|
const QByteArray& dataByteArray = QByteArray(),
|
||||||
QHttpMultiPart* dataMultiPart = NULL);
|
QHttpMultiPart* dataMultiPart = NULL);
|
||||||
|
|
||||||
const QUrl& getAuthURL() const { return _authURL; }
|
const QUrl& getAuthURL() const { return _authURL; }
|
||||||
void setAuthURL(const QUrl& authURL);
|
void setAuthURL(const QUrl& authURL);
|
||||||
bool hasAuthEndpoint() { return !_authURL.isEmpty(); }
|
bool hasAuthEndpoint() { return !_authURL.isEmpty(); }
|
||||||
|
|
||||||
bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); }
|
bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); }
|
||||||
bool hasValidAccessToken();
|
bool hasValidAccessToken();
|
||||||
Q_INVOKABLE bool checkAndSignalForAccessToken();
|
Q_INVOKABLE bool checkAndSignalForAccessToken();
|
||||||
|
|
||||||
void requestAccessToken(const QString& login, const QString& password);
|
void requestAccessToken(const QString& login, const QString& password);
|
||||||
|
void requestProfile();
|
||||||
|
|
||||||
const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; }
|
const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; }
|
||||||
|
|
||||||
void destroy() { delete _networkAccessManager; }
|
void destroy() { delete _networkAccessManager; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void requestFinished();
|
void requestAccessTokenFinished();
|
||||||
void requestError(QNetworkReply::NetworkError error);
|
void requestProfileFinished();
|
||||||
|
void requestAccessTokenError(QNetworkReply::NetworkError error);
|
||||||
|
void requestProfileError(QNetworkReply::NetworkError error);
|
||||||
void logout();
|
void logout();
|
||||||
void updateBalance();
|
void updateBalance();
|
||||||
void accountInfoBalanceChanged(qint64 newBalance);
|
void accountInfoBalanceChanged(qint64 newBalance);
|
||||||
|
@ -69,7 +72,7 @@ signals:
|
||||||
void authRequired();
|
void authRequired();
|
||||||
void authEndpointChanged();
|
void authEndpointChanged();
|
||||||
void usernameChanged(const QString& username);
|
void usernameChanged(const QString& username);
|
||||||
void accessTokenChanged();
|
void profileChanged();
|
||||||
void loginComplete(const QUrl& authURL);
|
void loginComplete(const QUrl& authURL);
|
||||||
void loginFailed();
|
void loginFailed();
|
||||||
void logoutComplete();
|
void logoutComplete();
|
||||||
|
@ -80,19 +83,19 @@ private:
|
||||||
AccountManager();
|
AccountManager();
|
||||||
AccountManager(AccountManager const& other); // not implemented
|
AccountManager(AccountManager const& other); // not implemented
|
||||||
void operator=(AccountManager const& other); // not implemented
|
void operator=(AccountManager const& other); // not implemented
|
||||||
|
|
||||||
void passSuccessToCallback(QNetworkReply* reply);
|
void passSuccessToCallback(QNetworkReply* reply);
|
||||||
void passErrorToCallback(QNetworkReply* reply);
|
void passErrorToCallback(QNetworkReply* reply);
|
||||||
|
|
||||||
Q_INVOKABLE void invokedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
Q_INVOKABLE void invokedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
||||||
const JSONCallbackParameters& callbackParams,
|
const JSONCallbackParameters& callbackParams,
|
||||||
const QByteArray& dataByteArray,
|
const QByteArray& dataByteArray,
|
||||||
QHttpMultiPart* dataMultiPart);
|
QHttpMultiPart* dataMultiPart);
|
||||||
|
|
||||||
QUrl _authURL;
|
QUrl _authURL;
|
||||||
QNetworkAccessManager* _networkAccessManager;
|
QNetworkAccessManager* _networkAccessManager;
|
||||||
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
|
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
|
||||||
|
|
||||||
DataServerAccountInfo _accountInfo;
|
DataServerAccountInfo _accountInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,20 +21,7 @@ DataServerAccountInfo::DataServerAccountInfo() :
|
||||||
_balance(0),
|
_balance(0),
|
||||||
_hasBalance(false)
|
_hasBalance(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) :
|
|
||||||
_accessToken(jsonObject),
|
|
||||||
_username(),
|
|
||||||
_xmppPassword(),
|
|
||||||
_balance(0),
|
|
||||||
_hasBalance(false)
|
|
||||||
{
|
|
||||||
QJsonObject userJSONObject = jsonObject["user"].toObject();
|
|
||||||
setUsername(userJSONObject["username"].toString());
|
|
||||||
setXMPPPassword(userJSONObject["xmpp_password"].toString());
|
|
||||||
setDiscourseApiKey(userJSONObject["discourse_api_key"].toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) {
|
DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) {
|
||||||
|
@ -54,7 +41,7 @@ DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountI
|
||||||
|
|
||||||
void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
|
void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
|
|
||||||
swap(_accessToken, otherInfo._accessToken);
|
swap(_accessToken, otherInfo._accessToken);
|
||||||
swap(_username, otherInfo._username);
|
swap(_username, otherInfo._username);
|
||||||
swap(_xmppPassword, otherInfo._xmppPassword);
|
swap(_xmppPassword, otherInfo._xmppPassword);
|
||||||
|
@ -63,10 +50,14 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
|
||||||
swap(_hasBalance, otherInfo._hasBalance);
|
swap(_hasBalance, otherInfo._hasBalance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) {
|
||||||
|
_accessToken = OAuthAccessToken(jsonObject);
|
||||||
|
}
|
||||||
|
|
||||||
void DataServerAccountInfo::setUsername(const QString& username) {
|
void DataServerAccountInfo::setUsername(const QString& username) {
|
||||||
if (_username != username) {
|
if (_username != username) {
|
||||||
_username = username;
|
_username = username;
|
||||||
|
|
||||||
qDebug() << "Username changed to" << username;
|
qDebug() << "Username changed to" << username;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,18 +78,29 @@ void DataServerAccountInfo::setBalance(qint64 balance) {
|
||||||
if (!_hasBalance || _balance != balance) {
|
if (!_hasBalance || _balance != balance) {
|
||||||
_balance = balance;
|
_balance = balance;
|
||||||
_hasBalance = true;
|
_hasBalance = true;
|
||||||
|
|
||||||
emit balanceChanged(_balance);
|
emit balanceChanged(_balance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) {
|
void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) {
|
||||||
if (jsonObject["status"].toString() == "success") {
|
if (jsonObject["status"].toString() == "success") {
|
||||||
qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toInt();
|
qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toDouble();
|
||||||
setBalance(balanceInSatoshis);
|
setBalance(balanceInSatoshis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DataServerAccountInfo::hasProfile() const {
|
||||||
|
return _username.length() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject) {
|
||||||
|
QJsonObject user = jsonObject["data"].toObject()["user"].toObject();
|
||||||
|
setUsername(user["username"].toString());
|
||||||
|
setXMPPPassword(user["xmpp_password"].toString());
|
||||||
|
setDiscourseApiKey(user["discourse_api_key"].toString());
|
||||||
|
}
|
||||||
|
|
||||||
QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {
|
QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {
|
||||||
out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey;
|
out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey;
|
||||||
return out;
|
return out;
|
||||||
|
|
|
@ -22,12 +22,12 @@ class DataServerAccountInfo : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
DataServerAccountInfo();
|
DataServerAccountInfo();
|
||||||
DataServerAccountInfo(const QJsonObject& jsonObject);
|
|
||||||
DataServerAccountInfo(const DataServerAccountInfo& otherInfo);
|
DataServerAccountInfo(const DataServerAccountInfo& otherInfo);
|
||||||
DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo);
|
DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo);
|
||||||
|
|
||||||
const OAuthAccessToken& getAccessToken() const { return _accessToken; }
|
const OAuthAccessToken& getAccessToken() const { return _accessToken; }
|
||||||
|
void setAccessTokenFromJSON(const QJsonObject& jsonObject);
|
||||||
|
|
||||||
const QString& getUsername() const { return _username; }
|
const QString& getUsername() const { return _username; }
|
||||||
void setUsername(const QString& username);
|
void setUsername(const QString& username);
|
||||||
|
|
||||||
|
@ -36,20 +36,25 @@ public:
|
||||||
|
|
||||||
const QString& getDiscourseApiKey() const { return _discourseApiKey; }
|
const QString& getDiscourseApiKey() const { return _discourseApiKey; }
|
||||||
void setDiscourseApiKey(const QString& discourseApiKey);
|
void setDiscourseApiKey(const QString& discourseApiKey);
|
||||||
|
|
||||||
qint64 getBalance() const { return _balance; }
|
qint64 getBalance() const { return _balance; }
|
||||||
|
float getBalanceInSatoshis() const { return _balance / SATOSHIS_PER_CREDIT; }
|
||||||
void setBalance(qint64 balance);
|
void setBalance(qint64 balance);
|
||||||
bool hasBalance() const { return _hasBalance; }
|
bool hasBalance() const { return _hasBalance; }
|
||||||
void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; }
|
void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; }
|
||||||
Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject);
|
Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject);
|
||||||
|
|
||||||
|
bool hasProfile() const;
|
||||||
|
|
||||||
|
void setProfileInfoFromJSON(const QJsonObject& jsonObject);
|
||||||
|
|
||||||
friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info);
|
friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info);
|
||||||
friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info);
|
friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info);
|
||||||
signals:
|
signals:
|
||||||
qint64 balanceChanged(qint64 newBalance);
|
qint64 balanceChanged(qint64 newBalance);
|
||||||
private:
|
private:
|
||||||
void swap(DataServerAccountInfo& otherInfo);
|
void swap(DataServerAccountInfo& otherInfo);
|
||||||
|
|
||||||
OAuthAccessToken _accessToken;
|
OAuthAccessToken _accessToken;
|
||||||
QString _username;
|
QString _username;
|
||||||
QString _xmppPassword;
|
QString _xmppPassword;
|
||||||
|
|
|
@ -281,7 +281,7 @@ void NodeList::processSTUNResponse(const QByteArray& packet) {
|
||||||
int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN;
|
int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN;
|
||||||
|
|
||||||
uint8_t addressFamily = 0;
|
uint8_t addressFamily = 0;
|
||||||
memcpy(&addressFamily, packet.data(), sizeof(addressFamily));
|
memcpy(&addressFamily, packet.data() + byteIndex, sizeof(addressFamily));
|
||||||
|
|
||||||
byteIndex += sizeof(addressFamily);
|
byteIndex += sizeof(addressFamily);
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,8 @@ bool ReceivedPacketProcessor::process() {
|
||||||
return isStillRunning(); // keep running till they terminate us
|
return isStillRunning(); // keep running till they terminate us
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReceivedPacketProcessor::killNode(const SharedNodePointer& node) {
|
void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) {
|
||||||
|
lock();
|
||||||
_nodePacketCounts.remove(node->getUUID());
|
_nodePacketCounts.remove(node->getUUID());
|
||||||
|
unlock();
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ public:
|
||||||
int packetsToProcessCount() const { return _packets.size(); }
|
int packetsToProcessCount() const { return _packets.size(); }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void killNode(const SharedNodePointer& node);
|
void nodeKilled(SharedNodePointer node);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Callback for processing of recieved packets. Implement this to process the incoming packets.
|
/// Callback for processing of recieved packets. Implement this to process the incoming packets.
|
||||||
|
|
|
@ -180,6 +180,18 @@ static bool findIntersection(float origin, float direction, float corner, float
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// finds the intersection between a ray and the inside facing plane on one axis
|
||||||
|
static bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance) {
|
||||||
|
if (direction > EPSILON) {
|
||||||
|
distance = -1.0f * (origin - (corner + size)) / direction;
|
||||||
|
return true;
|
||||||
|
} else if (direction < -EPSILON) {
|
||||||
|
distance = -1.0f * (origin - corner) / direction;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const {
|
bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const {
|
||||||
// handle the trivial cases where the expanded box contains the start or end
|
// handle the trivial cases where the expanded box contains the start or end
|
||||||
if (expandedContains(start, expansion) || expandedContains(end, expansion)) {
|
if (expandedContains(start, expansion) || expandedContains(end, expansion)) {
|
||||||
|
@ -207,9 +219,34 @@ bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& e
|
||||||
bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const {
|
bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const {
|
||||||
// handle the trivial case where the box contains the origin
|
// handle the trivial case where the box contains the origin
|
||||||
if (contains(origin)) {
|
if (contains(origin)) {
|
||||||
|
// We still want to calculate the distance from the origin to the inside out plane
|
||||||
|
float axisDistance;
|
||||||
|
if ((findInsideOutIntersection(origin.x, direction.x, _corner.x, _scale.x, axisDistance) && axisDistance >= 0 &&
|
||||||
|
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) &&
|
||||||
|
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) {
|
||||||
|
distance = axisDistance;
|
||||||
|
face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale.y, axisDistance) && axisDistance >= 0 &&
|
||||||
|
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x) &&
|
||||||
|
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) {
|
||||||
|
distance = axisDistance;
|
||||||
|
face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale.z, axisDistance) && axisDistance >= 0 &&
|
||||||
|
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) &&
|
||||||
|
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x))) {
|
||||||
|
distance = axisDistance;
|
||||||
|
face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// This case is unexpected, but mimics the previous behavior for inside out intersections
|
||||||
distance = 0;
|
distance = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check each axis
|
// check each axis
|
||||||
float axisDistance;
|
float axisDistance;
|
||||||
if ((findIntersection(origin.x, direction.x, _corner.x, _scale.x, axisDistance) && axisDistance >= 0 &&
|
if ((findIntersection(origin.x, direction.x, _corner.x, _scale.x, axisDistance) && axisDistance >= 0 &&
|
||||||
|
|
|
@ -169,6 +169,18 @@ static bool findIntersection(float origin, float direction, float corner, float
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// finds the intersection between a ray and the inside facing plane on one axis
|
||||||
|
static bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance) {
|
||||||
|
if (direction > EPSILON) {
|
||||||
|
distance = -1.0f * (origin - (corner + size)) / direction;
|
||||||
|
return true;
|
||||||
|
} else if (direction < -EPSILON) {
|
||||||
|
distance = -1.0f * (origin - corner) / direction;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool AACube::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const {
|
bool AACube::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const {
|
||||||
// handle the trivial cases where the expanded box contains the start or end
|
// handle the trivial cases where the expanded box contains the start or end
|
||||||
if (expandedContains(start, expansion) || expandedContains(end, expansion)) {
|
if (expandedContains(start, expansion) || expandedContains(end, expansion)) {
|
||||||
|
@ -196,9 +208,35 @@ bool AACube::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3&
|
||||||
bool AACube::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const {
|
bool AACube::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const {
|
||||||
// handle the trivial case where the box contains the origin
|
// handle the trivial case where the box contains the origin
|
||||||
if (contains(origin)) {
|
if (contains(origin)) {
|
||||||
|
|
||||||
|
// We still want to calculate the distance from the origin to the inside out plane
|
||||||
|
float axisDistance;
|
||||||
|
if ((findInsideOutIntersection(origin.x, direction.x, _corner.x, _scale, axisDistance) && axisDistance >= 0 &&
|
||||||
|
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) &&
|
||||||
|
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) {
|
||||||
|
distance = axisDistance;
|
||||||
|
face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale, axisDistance) && axisDistance >= 0 &&
|
||||||
|
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale) &&
|
||||||
|
isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) {
|
||||||
|
distance = axisDistance;
|
||||||
|
face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale, axisDistance) && axisDistance >= 0 &&
|
||||||
|
isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) &&
|
||||||
|
isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale))) {
|
||||||
|
distance = axisDistance;
|
||||||
|
face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// This case is unexpected, but mimics the previous behavior for inside out intersections
|
||||||
distance = 0;
|
distance = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check each axis
|
// check each axis
|
||||||
float axisDistance;
|
float axisDistance;
|
||||||
if ((findIntersection(origin.x, direction.x, _corner.x, _scale, axisDistance) && axisDistance >= 0 &&
|
if ((findIntersection(origin.x, direction.x, _corner.x, _scale, axisDistance) && axisDistance >= 0 &&
|
||||||
|
|
|
@ -147,7 +147,12 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL,
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||||
loop.exec();
|
loop.exec();
|
||||||
_scriptContents = reply->readAll();
|
if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) {
|
||||||
|
_scriptContents = reply->readAll();
|
||||||
|
} else {
|
||||||
|
qDebug() << "ERROR Loading file:" << url.toString();
|
||||||
|
emit errorMessage("ERROR Loading file:" + url.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include <glm/gtx/vector_angle.hpp>
|
#include <glm/gtx/vector_angle.hpp>
|
||||||
|
|
||||||
#include "CapsuleShape.h"
|
#include "CapsuleShape.h"
|
||||||
|
|
||||||
|
#include "GeometryUtil.h"
|
||||||
#include "SharedUtil.h"
|
#include "SharedUtil.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,3 +86,11 @@ void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& en
|
||||||
updateBoundingRadius();
|
updateBoundingRadius();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CapsuleShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const {
|
||||||
|
glm::vec3 capsuleStart, capsuleEnd;
|
||||||
|
getStartPoint(capsuleStart);
|
||||||
|
getEndPoint(capsuleEnd);
|
||||||
|
// NOTE: findRayCapsuleIntersection returns 'true' with distance = 0 when rayStart is inside capsule.
|
||||||
|
// TODO: implement the raycast to return inside surface intersection for the internal rayStart.
|
||||||
|
return findRayCapsuleIntersection(rayStart, rayDirection, capsuleStart, capsuleEnd, _radius, distance);
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
void setRadiusAndHalfHeight(float radius, float height);
|
void setRadiusAndHalfHeight(float radius, float height);
|
||||||
void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint);
|
void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint);
|
||||||
|
|
||||||
|
bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updateBoundingRadius() { _boundingRadius = _radius + _halfHeight; }
|
void updateBoundingRadius() { _boundingRadius = _radius + _halfHeight; }
|
||||||
|
|
||||||
|
|
|
@ -19,71 +19,70 @@
|
||||||
#include "HifiConfigVariantMap.h"
|
#include "HifiConfigVariantMap.h"
|
||||||
|
|
||||||
QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringList& argumentList) {
|
QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringList& argumentList) {
|
||||||
|
|
||||||
QVariantMap mergedMap;
|
QVariantMap mergedMap;
|
||||||
|
|
||||||
// Add anything in the CL parameter list to the variant map.
|
// Add anything in the CL parameter list to the variant map.
|
||||||
// Take anything with a dash in it as a key, and the values after it as the value.
|
// Take anything with a dash in it as a key, and the values after it as the value.
|
||||||
|
|
||||||
const QString DASHED_KEY_REGEX_STRING = "(^-{1,2})([\\w-]+)";
|
const QString DASHED_KEY_REGEX_STRING = "(^-{1,2})([\\w-]+)";
|
||||||
QRegExp dashedKeyRegex(DASHED_KEY_REGEX_STRING);
|
QRegExp dashedKeyRegex(DASHED_KEY_REGEX_STRING);
|
||||||
|
|
||||||
int keyIndex = argumentList.indexOf(dashedKeyRegex);
|
int keyIndex = argumentList.indexOf(dashedKeyRegex);
|
||||||
int nextKeyIndex = 0;
|
int nextKeyIndex = 0;
|
||||||
|
|
||||||
// check if there is a config file to read where we can pull config info not passed on command line
|
// check if there is a config file to read where we can pull config info not passed on command line
|
||||||
const QString CONFIG_FILE_OPTION = "--config";
|
const QString CONFIG_FILE_OPTION = "--config";
|
||||||
|
|
||||||
while (keyIndex != -1) {
|
while (keyIndex != -1) {
|
||||||
if (argumentList[keyIndex] != CONFIG_FILE_OPTION) {
|
if (argumentList[keyIndex] != CONFIG_FILE_OPTION) {
|
||||||
// we have a key - look forward to see how many values associate to it
|
// we have a key - look forward to see how many values associate to it
|
||||||
QString key = dashedKeyRegex.cap(2);
|
QString key = dashedKeyRegex.cap(2);
|
||||||
|
|
||||||
nextKeyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1);
|
nextKeyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1);
|
||||||
|
|
||||||
if (nextKeyIndex == keyIndex + 1 || keyIndex == argumentList.size() - 1) {
|
if (nextKeyIndex == keyIndex + 1 || keyIndex == argumentList.size() - 1) {
|
||||||
// there's no value associated with this option, it's a boolean
|
// this option is simply a switch, so add it to the map with a value of `true`
|
||||||
// so add it to the variant map with NULL as value
|
mergedMap.insertMulti(key, QVariant(true));
|
||||||
mergedMap.insertMulti(key, QVariant());
|
|
||||||
} else {
|
} else {
|
||||||
int maxIndex = (nextKeyIndex == -1) ? argumentList.size() - 1: nextKeyIndex;
|
int maxIndex = (nextKeyIndex == -1) ? argumentList.size() : nextKeyIndex;
|
||||||
|
|
||||||
// there's at least one value associated with the option
|
// there's at least one value associated with the option
|
||||||
// pull the first value to start
|
// pull the first value to start
|
||||||
QString value = argumentList[keyIndex + 1];
|
QString value = argumentList[keyIndex + 1];
|
||||||
|
|
||||||
// for any extra values, append them, with a space, to the value string
|
// for any extra values, append them, with a space, to the value string
|
||||||
for (int i = keyIndex + 2; i <= maxIndex; i++) {
|
for (int i = keyIndex + 2; i < maxIndex; i++) {
|
||||||
value += " " + argumentList[i];
|
value += " " + argumentList[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the finalized value to the merged map
|
// add the finalized value to the merged map
|
||||||
mergedMap.insert(key, value);
|
mergedMap.insert(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
keyIndex = nextKeyIndex;
|
keyIndex = nextKeyIndex;
|
||||||
} else {
|
} else {
|
||||||
keyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1);
|
keyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int configIndex = argumentList.indexOf(CONFIG_FILE_OPTION);
|
int configIndex = argumentList.indexOf(CONFIG_FILE_OPTION);
|
||||||
|
|
||||||
if (configIndex != -1) {
|
if (configIndex != -1) {
|
||||||
// we have a config file - try and read it
|
// we have a config file - try and read it
|
||||||
QString configFilePath = argumentList[configIndex + 1];
|
QString configFilePath = argumentList[configIndex + 1];
|
||||||
QFile configFile(configFilePath);
|
QFile configFile(configFilePath);
|
||||||
|
|
||||||
if (configFile.exists()) {
|
if (configFile.exists()) {
|
||||||
qDebug() << "Reading JSON config file at" << configFilePath;
|
qDebug() << "Reading JSON config file at" << configFilePath;
|
||||||
configFile.open(QIODevice::ReadOnly);
|
configFile.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
|
QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
|
||||||
QJsonObject rootObject = configDocument.object();
|
QJsonObject rootObject = configDocument.object();
|
||||||
|
|
||||||
// enumerate the keys of the configDocument object
|
// enumerate the keys of the configDocument object
|
||||||
foreach(const QString& key, rootObject.keys()) {
|
foreach(const QString& key, rootObject.keys()) {
|
||||||
|
|
||||||
if (!mergedMap.contains(key)) {
|
if (!mergedMap.contains(key)) {
|
||||||
// no match in existing list, add it
|
// no match in existing list, add it
|
||||||
mergedMap.insert(key, QVariant(rootObject[key]));
|
mergedMap.insert(key, QVariant(rootObject[key]));
|
||||||
|
@ -93,6 +92,6 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL
|
||||||
qDebug() << "Could not find JSON config file at" << configFilePath;
|
qDebug() << "Could not find JSON config file at" << configFilePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mergedMap;
|
return mergedMap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,9 @@ public:
|
||||||
|
|
||||||
void setShapes(QVector<ListShapeEntry>& shapes);
|
void setShapes(QVector<ListShapeEntry>& shapes);
|
||||||
|
|
||||||
|
// TODO: either implement this or remove ListShape altogether
|
||||||
|
bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { return false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void clear();
|
void clear();
|
||||||
void computeBoundingRadius();
|
void computeBoundingRadius();
|
||||||
|
|
|
@ -30,7 +30,28 @@ PlaneShape::PlaneShape(const glm::vec4& coefficients) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::vec3 PlaneShape::getNormal() const {
|
||||||
|
return _rotation * UNROTATED_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec4 PlaneShape::getCoefficients() const {
|
glm::vec4 PlaneShape::getCoefficients() const {
|
||||||
glm::vec3 normal = _rotation * UNROTATED_NORMAL;
|
glm::vec3 normal = _rotation * UNROTATED_NORMAL;
|
||||||
return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _position));
|
return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PlaneShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const {
|
||||||
|
glm::vec3 n = getNormal();
|
||||||
|
float denominator = glm::dot(n, rayDirection);
|
||||||
|
if (fabsf(denominator) < EPSILON) {
|
||||||
|
// line is parallel to plane
|
||||||
|
return glm::dot(_position - rayStart, n) < EPSILON;
|
||||||
|
} else {
|
||||||
|
float d = glm::dot(_position - rayStart, n) / denominator;
|
||||||
|
if (d > 0.0f) {
|
||||||
|
// ray points toward plane
|
||||||
|
distance = d;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,10 @@ class PlaneShape : public Shape {
|
||||||
public:
|
public:
|
||||||
PlaneShape(const glm::vec4& coefficients = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
|
PlaneShape(const glm::vec4& coefficients = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
|
glm::vec3 getNormal() const;
|
||||||
glm::vec4 getCoefficients() const;
|
glm::vec4 getCoefficients() const;
|
||||||
|
|
||||||
|
bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_PlaneShape_h
|
#endif // hifi_PlaneShape_h
|
||||||
|
|
|
@ -38,6 +38,8 @@ public:
|
||||||
virtual void setPosition(const glm::vec3& position) { _position = position; }
|
virtual void setPosition(const glm::vec3& position) { _position = position; }
|
||||||
virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; }
|
virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; }
|
||||||
|
|
||||||
|
virtual bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// these ctors are protected (used by derived classes only)
|
// these ctors are protected (used by derived classes only)
|
||||||
Shape(Type type) : _type(type), _boundingRadius(0.f), _position(0.f), _rotation() {}
|
Shape(Type type) : _type(type), _boundingRadius(0.f), _position(0.f), _rotation() {}
|
||||||
|
|
|
@ -765,5 +765,24 @@ bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, fl
|
||||||
return sphereAACube(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions);
|
return sphereAACube(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool findRayIntersectionWithShapes(const QVector<Shape*> shapes, const glm::vec3& rayStart, const glm::vec3& rayDirection, float& minDistance) {
|
||||||
|
float hitDistance = FLT_MAX;
|
||||||
|
int numShapes = shapes.size();
|
||||||
|
for (int i = 0; i < numShapes; ++i) {
|
||||||
|
Shape* shape = shapes.at(i);
|
||||||
|
if (shape) {
|
||||||
|
float distance;
|
||||||
|
if (shape->findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
if (distance < hitDistance) {
|
||||||
|
hitDistance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hitDistance < FLT_MAX) {
|
||||||
|
minDistance = hitDistance;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ShapeCollider
|
} // namespace ShapeCollider
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
|
|
||||||
namespace ShapeCollider {
|
namespace ShapeCollider {
|
||||||
|
|
||||||
/// \param shapeA pointer to first shape
|
/// \param shapeA pointer to first shape (cannot be NULL)
|
||||||
/// \param shapeB pointer to second shape
|
/// \param shapeB pointer to second shape (cannot be NULL)
|
||||||
/// \param collisions[out] collision details
|
/// \param collisions[out] collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
|
bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
|
||||||
|
@ -33,123 +33,130 @@ namespace ShapeCollider {
|
||||||
/// \return true if any shapes collide
|
/// \return true if any shapes collide
|
||||||
bool collideShapesCoarse(const QVector<const Shape*>& shapesA, const QVector<const Shape*>& shapesB, CollisionInfo& collision);
|
bool collideShapesCoarse(const QVector<const Shape*>& shapesA, const QVector<const Shape*>& shapesB, CollisionInfo& collision);
|
||||||
|
|
||||||
/// \param shapeA a pointer to a shape
|
/// \param shapeA a pointer to a shape (cannot be NULL)
|
||||||
/// \param cubeCenter center of cube
|
/// \param cubeCenter center of cube
|
||||||
/// \param cubeSide lenght of side of cube
|
/// \param cubeSide lenght of side of cube
|
||||||
/// \param collisions[out] average collision details
|
/// \param collisions[out] average collision details
|
||||||
/// \return true if shapeA collides with axis aligned cube
|
/// \return true if shapeA collides with axis aligned cube
|
||||||
bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param sphereA pointer to first shape
|
/// \param sphereA pointer to first shape (cannot be NULL)
|
||||||
/// \param sphereB pointer to second shape
|
/// \param sphereB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, CollisionList& collisions);
|
bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param sphereA pointer to first shape
|
/// \param sphereA pointer to first shape (cannot be NULL)
|
||||||
/// \param capsuleB pointer to second shape
|
/// \param capsuleB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, CollisionList& collisions);
|
bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param sphereA pointer to first shape
|
/// \param sphereA pointer to first shape (cannot be NULL)
|
||||||
/// \param planeB pointer to second shape
|
/// \param planeB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions);
|
bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param capsuleA pointer to first shape
|
/// \param capsuleA pointer to first shape (cannot be NULL)
|
||||||
/// \param sphereB pointer to second shape
|
/// \param sphereB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, CollisionList& collisions);
|
bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param capsuleA pointer to first shape
|
/// \param capsuleA pointer to first shape (cannot be NULL)
|
||||||
/// \param capsuleB pointer to second shape
|
/// \param capsuleB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, CollisionList& collisions);
|
bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param capsuleA pointer to first shape
|
/// \param capsuleA pointer to first shape (cannot be NULL)
|
||||||
/// \param planeB pointer to second shape
|
/// \param planeB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, CollisionList& collisions);
|
bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param planeA pointer to first shape
|
/// \param planeA pointer to first shape (cannot be NULL)
|
||||||
/// \param sphereB pointer to second shape
|
/// \param sphereB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions);
|
bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param planeA pointer to first shape
|
/// \param planeA pointer to first shape (cannot be NULL)
|
||||||
/// \param capsuleB pointer to second shape
|
/// \param capsuleB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, CollisionList& collisions);
|
bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param planeA pointer to first shape
|
/// \param planeA pointer to first shape (cannot be NULL)
|
||||||
/// \param planeB pointer to second shape
|
/// \param planeB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool planePlane(const PlaneShape* planeA, const PlaneShape* planeB, CollisionList& collisions);
|
bool planePlane(const PlaneShape* planeA, const PlaneShape* planeB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param sphereA pointer to first shape
|
/// \param sphereA pointer to first shape (cannot be NULL)
|
||||||
/// \param listB pointer to second shape
|
/// \param listB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool sphereList(const SphereShape* sphereA, const ListShape* listB, CollisionList& collisions);
|
bool sphereList(const SphereShape* sphereA, const ListShape* listB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param capuleA pointer to first shape
|
/// \param capuleA pointer to first shape (cannot be NULL)
|
||||||
/// \param listB pointer to second shape
|
/// \param listB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool capsuleList(const CapsuleShape* capsuleA, const ListShape* listB, CollisionList& collisions);
|
bool capsuleList(const CapsuleShape* capsuleA, const ListShape* listB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param planeA pointer to first shape
|
/// \param planeA pointer to first shape (cannot be NULL)
|
||||||
/// \param listB pointer to second shape
|
/// \param listB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool planeList(const PlaneShape* planeA, const ListShape* listB, CollisionList& collisions);
|
bool planeList(const PlaneShape* planeA, const ListShape* listB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param listA pointer to first shape
|
/// \param listA pointer to first shape (cannot be NULL)
|
||||||
/// \param sphereB pointer to second shape
|
/// \param sphereB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool listSphere(const ListShape* listA, const SphereShape* sphereB, CollisionList& collisions);
|
bool listSphere(const ListShape* listA, const SphereShape* sphereB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param listA pointer to first shape
|
/// \param listA pointer to first shape (cannot be NULL)
|
||||||
/// \param capsuleB pointer to second shape
|
/// \param capsuleB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool listCapsule(const ListShape* listA, const CapsuleShape* capsuleB, CollisionList& collisions);
|
bool listCapsule(const ListShape* listA, const CapsuleShape* capsuleB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param listA pointer to first shape
|
/// \param listA pointer to first shape (cannot be NULL)
|
||||||
/// \param planeB pointer to second shape
|
/// \param planeB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool listPlane(const ListShape* listA, const PlaneShape* planeB, CollisionList& collisions);
|
bool listPlane(const ListShape* listA, const PlaneShape* planeB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param listA pointer to first shape
|
/// \param listA pointer to first shape (cannot be NULL)
|
||||||
/// \param capsuleB pointer to second shape
|
/// \param capsuleB pointer to second shape (cannot be NULL)
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if shapes collide
|
/// \return true if shapes collide
|
||||||
bool listList(const ListShape* listA, const ListShape* listB, CollisionList& collisions);
|
bool listList(const ListShape* listA, const ListShape* listB, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param sphereA pointer to sphere
|
/// \param sphereA pointer to sphere (cannot be NULL)
|
||||||
/// \param cubeCenter center of cube
|
/// \param cubeCenter center of cube
|
||||||
/// \param cubeSide lenght of side of cube
|
/// \param cubeSide lenght of side of cube
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if sphereA collides with axis aligned cube
|
/// \return true if sphereA collides with axis aligned cube
|
||||||
bool sphereAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
bool sphereAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
||||||
|
|
||||||
/// \param capsuleA pointer to capsule
|
/// \param capsuleA pointer to capsule (cannot be NULL)
|
||||||
/// \param cubeCenter center of cube
|
/// \param cubeCenter center of cube
|
||||||
/// \param cubeSide lenght of side of cube
|
/// \param cubeSide lenght of side of cube
|
||||||
/// \param[out] collisions where to append collision details
|
/// \param[out] collisions where to append collision details
|
||||||
/// \return true if capsuleA collides with axis aligned cube
|
/// \return true if capsuleA collides with axis aligned cube
|
||||||
bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
||||||
|
|
||||||
|
/// \param shapes list of pointers to shapes (shape pointers may be NULL)
|
||||||
|
/// \param startPoint beginning of ray
|
||||||
|
/// \param direction direction of ray
|
||||||
|
/// \param minDistance[out] shortest distance to intersection of ray with a shapes
|
||||||
|
/// \return true if ray hits any shape in shapes
|
||||||
|
bool findRayIntersectionWithShapes(const QVector<Shape*> shapes, const glm::vec3& startPoint, const glm::vec3& direction, float& minDistance);
|
||||||
|
|
||||||
} // namespace ShapeCollider
|
} // namespace ShapeCollider
|
||||||
|
|
||||||
#endif // hifi_ShapeCollider_h
|
#endif // hifi_ShapeCollider_h
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
#include "OctalCode.h"
|
#include "OctalCode.h"
|
||||||
#include "SharedUtil.h"
|
#include "SharedUtil.h"
|
||||||
|
@ -415,13 +416,17 @@ void printVoxelCode(unsigned char* voxelCode) {
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void usleep(int waitTime) {
|
void usleep(int waitTime) {
|
||||||
__int64 time1 = 0, time2 = 0, sysFreq = 0;
|
const quint64 BUSY_LOOP_USECS = 2000;
|
||||||
|
quint64 compTime = waitTime + usecTimestampNow();
|
||||||
QueryPerformanceCounter((LARGE_INTEGER *)&time1);
|
quint64 compTimeSleep = compTime - BUSY_LOOP_USECS;
|
||||||
QueryPerformanceFrequency((LARGE_INTEGER *)&sysFreq);
|
while (true) {
|
||||||
do {
|
if (usecTimestampNow() < compTimeSleep) {
|
||||||
QueryPerformanceCounter((LARGE_INTEGER *)&time2);
|
QThread::msleep(1);
|
||||||
} while( (time2 - time1) < waitTime);
|
}
|
||||||
|
if (usecTimestampNow() >= compTime) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
44
libraries/shared/src/SphereShape.cpp
Normal file
44
libraries/shared/src/SphereShape.cpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// SphereShape.cpp
|
||||||
|
// libraries/shared/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows on 2014.06.17
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
|
#include "SphereShape.h"
|
||||||
|
|
||||||
|
bool SphereShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const {
|
||||||
|
float r2 = _boundingRadius * _boundingRadius;
|
||||||
|
|
||||||
|
// compute closest approach (CA)
|
||||||
|
float a = glm::dot(_position - rayStart, rayDirection); // a = distance from ray-start to CA
|
||||||
|
float b2 = glm::distance2(_position, rayStart + a * rayDirection); // b2 = squared distance from sphere-center to CA
|
||||||
|
if (b2 > r2) {
|
||||||
|
// ray does not hit sphere
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along rayDirection
|
||||||
|
float d2 = glm::distance2(rayStart, _position); // d2 = squared distance from sphere-center to ray-start
|
||||||
|
if (a < 0.0f) {
|
||||||
|
// ray points away from sphere-center
|
||||||
|
if (d2 > r2) {
|
||||||
|
// ray starts outside sphere
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// ray starts inside sphere
|
||||||
|
distance = c + a;
|
||||||
|
} else if (d2 > r2) {
|
||||||
|
// ray starts outside sphere
|
||||||
|
distance = a - c;
|
||||||
|
} else {
|
||||||
|
// ray starts inside sphere
|
||||||
|
distance = a + c;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -29,6 +29,8 @@ public:
|
||||||
float getRadius() const { return _boundingRadius; }
|
float getRadius() const { return _boundingRadius; }
|
||||||
|
|
||||||
void setRadius(float radius) { _boundingRadius = radius; }
|
void setRadius(float radius) { _boundingRadius = radius; }
|
||||||
|
|
||||||
|
bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_SphereShape_h
|
#endif // hifi_SphereShape_h
|
||||||
|
|
|
@ -214,6 +214,35 @@ static bool testSerialization(Bitstream::MetadataType metadataType) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// go back to the beginning and read everything as generics
|
||||||
|
inStream.device()->seek(0);
|
||||||
|
Bitstream genericIn(inStream, metadataType, Bitstream::ALL_GENERICS);
|
||||||
|
genericIn >> testObjectReadA;
|
||||||
|
genericIn >> testObjectReadB;
|
||||||
|
genericIn >> messageRead;
|
||||||
|
genericIn >> endRead;
|
||||||
|
|
||||||
|
// reassign the ids
|
||||||
|
testObjectReadA->setID(testObjectWrittenA->getID());
|
||||||
|
testObjectReadA->setOriginID(testObjectWrittenA->getOriginID());
|
||||||
|
testObjectReadB->setID(testObjectWrittenB->getID());
|
||||||
|
testObjectReadB->setOriginID(testObjectWrittenB->getOriginID());
|
||||||
|
|
||||||
|
// write it back out and compare
|
||||||
|
QByteArray compareArray;
|
||||||
|
QDataStream compareOutStream(&compareArray, QIODevice::WriteOnly);
|
||||||
|
Bitstream compareOut(compareOutStream, metadataType);
|
||||||
|
compareOut << testObjectReadA;
|
||||||
|
compareOut << testObjectReadB;
|
||||||
|
compareOut << messageRead;
|
||||||
|
compareOut << endRead;
|
||||||
|
compareOut.flush();
|
||||||
|
|
||||||
|
if (array != compareArray) {
|
||||||
|
qDebug() << "Mismatch between written/generic written streams.";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
100
tests/octree/src/AABoxCubeTests.cpp
Normal file
100
tests/octree/src/AABoxCubeTests.cpp
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
//
|
||||||
|
// AABoxCubeTests.h
|
||||||
|
// tests/octree/src
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 06/04/2014.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <AABox.h>
|
||||||
|
#include <AACube.h>
|
||||||
|
|
||||||
|
#include "AABoxCubeTests.h"
|
||||||
|
|
||||||
|
void AABoxCubeTests::AABoxCubeTests() {
|
||||||
|
qDebug() << "******************************************************************************************";
|
||||||
|
qDebug() << "AABoxCubeTests::AABoxCubeTests()";
|
||||||
|
|
||||||
|
{
|
||||||
|
qDebug() << "Test 1: AABox.findRayIntersection() inside out MIN_X_FACE";
|
||||||
|
|
||||||
|
glm::vec3 corner(0.0f, 0.0f, 0.0f);
|
||||||
|
float size = 1.0f;
|
||||||
|
|
||||||
|
AABox box(corner, size);
|
||||||
|
glm::vec3 origin(0.5f, 0.5f, 0.5f);
|
||||||
|
glm::vec3 direction(-1.0f, 0.0f, 0.0f);
|
||||||
|
float distance;
|
||||||
|
BoxFace face;
|
||||||
|
|
||||||
|
bool intersects = box.findRayIntersection(origin, direction, distance, face);
|
||||||
|
|
||||||
|
if (intersects && distance == 0.5f && face == MIN_X_FACE) {
|
||||||
|
qDebug() << "Test 1: PASSED";
|
||||||
|
} else {
|
||||||
|
qDebug() << "intersects=" << intersects << "expected=" << true;
|
||||||
|
qDebug() << "distance=" << distance << "expected=" << 0.5f;
|
||||||
|
qDebug() << "face=" << face << "expected=" << MIN_X_FACE;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
qDebug() << "Test 2: AABox.findRayIntersection() inside out MAX_X_FACE";
|
||||||
|
|
||||||
|
glm::vec3 corner(0.0f, 0.0f, 0.0f);
|
||||||
|
float size = 1.0f;
|
||||||
|
|
||||||
|
AABox box(corner, size);
|
||||||
|
glm::vec3 origin(0.5f, 0.5f, 0.5f);
|
||||||
|
glm::vec3 direction(1.0f, 0.0f, 0.0f);
|
||||||
|
float distance;
|
||||||
|
BoxFace face;
|
||||||
|
|
||||||
|
bool intersects = box.findRayIntersection(origin, direction, distance, face);
|
||||||
|
|
||||||
|
if (intersects && distance == 0.5f && face == MAX_X_FACE) {
|
||||||
|
qDebug() << "Test 2: PASSED";
|
||||||
|
} else {
|
||||||
|
qDebug() << "intersects=" << intersects << "expected=" << true;
|
||||||
|
qDebug() << "distance=" << distance << "expected=" << 0.5f;
|
||||||
|
qDebug() << "face=" << face << "expected=" << MAX_X_FACE;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
qDebug() << "Test 3: AABox.findRayIntersection() outside in";
|
||||||
|
|
||||||
|
glm::vec3 corner(0.5f, 0.0f, 0.0f);
|
||||||
|
float size = 0.5f;
|
||||||
|
|
||||||
|
AABox box(corner, size);
|
||||||
|
glm::vec3 origin(0.25f, 0.25f, 0.25f);
|
||||||
|
glm::vec3 direction(1.0f, 0.0f, 0.0f);
|
||||||
|
float distance;
|
||||||
|
BoxFace face;
|
||||||
|
|
||||||
|
bool intersects = box.findRayIntersection(origin, direction, distance, face);
|
||||||
|
|
||||||
|
if (intersects && distance == 0.25f && face == MIN_X_FACE) {
|
||||||
|
qDebug() << "Test 3: PASSED";
|
||||||
|
} else {
|
||||||
|
qDebug() << "intersects=" << intersects << "expected=" << true;
|
||||||
|
qDebug() << "distance=" << distance << "expected=" << 0.5f;
|
||||||
|
qDebug() << "face=" << face << "expected=" << MIN_X_FACE;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "******************************************************************************************";
|
||||||
|
}
|
||||||
|
|
||||||
|
void AABoxCubeTests::runAllTests() {
|
||||||
|
AABoxCubeTests();
|
||||||
|
}
|
20
tests/octree/src/AABoxCubeTests.h
Normal file
20
tests/octree/src/AABoxCubeTests.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// AABoxCubeTests.h
|
||||||
|
// tests/octree/src
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 06/04/2014.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AABoxCubeTests_h
|
||||||
|
#define hifi_AABoxCubeTests_h
|
||||||
|
|
||||||
|
namespace AABoxCubeTests {
|
||||||
|
void AABoxCubeTests();
|
||||||
|
void runAllTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // hifi_AABoxCubeTests_h
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// OctreeTests.h
|
// OctreeTests.h
|
||||||
// tests/physics/src
|
// tests/octree/src
|
||||||
//
|
//
|
||||||
// Created by Brad Hefta-Gaub on 06/04/2014.
|
// Created by Brad Hefta-Gaub on 06/04/2014.
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// OctreeTests.h
|
// OctreeTests.h
|
||||||
// tests/physics/src
|
// tests/octree/src
|
||||||
//
|
//
|
||||||
// Created by Brad Hefta-Gaub on 06/04/2014.
|
// Created by Brad Hefta-Gaub on 06/04/2014.
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "OctreeTests.h"
|
#include "OctreeTests.h"
|
||||||
|
#include "AABoxCubeTests.h"
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
OctreeTests::runAllTests();
|
OctreeTests::runAllTests();
|
||||||
|
AABoxCubeTests::runAllTests();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
//#include <stdio.h>
|
//#include <stdio.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtx/quaternion.hpp>
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
@ -897,6 +898,405 @@ void ShapeColliderTests::sphereMissesAACube() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShapeColliderTests::rayHitsSphere() {
|
||||||
|
float startDistance = 3.0f;
|
||||||
|
glm::vec3 rayStart(-startDistance, 0.0f, 0.0f);
|
||||||
|
glm::vec3 rayDirection(1.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
float radius = 1.0f;
|
||||||
|
glm::vec3 center(0.0f);
|
||||||
|
|
||||||
|
SphereShape sphere(radius, center);
|
||||||
|
|
||||||
|
// very simple ray along xAxis
|
||||||
|
{
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
float expectedDistance = startDistance - radius;
|
||||||
|
float relativeError = fabsf(distance - expectedDistance) / startDistance;
|
||||||
|
if (relativeError > EPSILON) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ray along a diagonal axis
|
||||||
|
{
|
||||||
|
rayStart = glm::vec3(startDistance, startDistance, 0.0f);
|
||||||
|
rayDirection = - glm::normalize(rayStart);
|
||||||
|
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
float expectedDistance = SQUARE_ROOT_OF_2 * startDistance - radius;
|
||||||
|
float relativeError = fabsf(distance - expectedDistance) / startDistance;
|
||||||
|
if (relativeError > EPSILON) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotated and displaced ray and sphere
|
||||||
|
{
|
||||||
|
startDistance = 7.41f;
|
||||||
|
radius = 3.917f;
|
||||||
|
|
||||||
|
glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f));
|
||||||
|
glm::quat rotation = glm::angleAxis(0.987654321f, axis);
|
||||||
|
glm::vec3 translation(35.7f, 2.46f, -1.97f);
|
||||||
|
|
||||||
|
glm::vec3 unrotatedRayDirection(-1.0f, 0.0f, 0.0f);
|
||||||
|
glm::vec3 untransformedRayStart(startDistance, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
rayStart = rotation * (untransformedRayStart + translation);
|
||||||
|
rayDirection = rotation * unrotatedRayDirection;
|
||||||
|
|
||||||
|
sphere.setRadius(radius);
|
||||||
|
sphere.setPosition(rotation * translation);
|
||||||
|
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
float expectedDistance = startDistance - radius;
|
||||||
|
float relativeError = fabsf(distance - expectedDistance) / startDistance;
|
||||||
|
if (relativeError > EPSILON) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeColliderTests::rayBarelyHitsSphere() {
|
||||||
|
float radius = 1.0f;
|
||||||
|
glm::vec3 center(0.0f);
|
||||||
|
float delta = 2.0f * EPSILON;
|
||||||
|
|
||||||
|
float startDistance = 3.0f;
|
||||||
|
glm::vec3 rayStart(-startDistance, radius - delta, 0.0f);
|
||||||
|
glm::vec3 rayDirection(1.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
SphereShape sphere(radius, center);
|
||||||
|
|
||||||
|
// very simple ray along xAxis
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate and rotate the whole system...
|
||||||
|
glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f));
|
||||||
|
glm::quat rotation = glm::angleAxis(0.987654321f, axis);
|
||||||
|
glm::vec3 translation(35.7f, 2.46f, -1.97f);
|
||||||
|
|
||||||
|
rayStart = rotation * (rayStart + translation);
|
||||||
|
rayDirection = rotation * rayDirection;
|
||||||
|
sphere.setPosition(rotation * translation);
|
||||||
|
|
||||||
|
// ...and test again
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ShapeColliderTests::rayBarelyMissesSphere() {
|
||||||
|
// same as the barely-hits case, but this time we move the ray away from sphere
|
||||||
|
float radius = 1.0f;
|
||||||
|
glm::vec3 center(0.0f);
|
||||||
|
float delta = 2.0f * EPSILON;
|
||||||
|
|
||||||
|
float startDistance = 3.0f;
|
||||||
|
glm::vec3 rayStart(-startDistance, radius + delta, 0.0f);
|
||||||
|
glm::vec3 rayDirection(1.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
SphereShape sphere(radius, center);
|
||||||
|
|
||||||
|
// very simple ray along xAxis
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
if (sphere.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl;
|
||||||
|
}
|
||||||
|
if (distance != FLT_MAX) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate and rotate the whole system...
|
||||||
|
glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f));
|
||||||
|
glm::quat rotation = glm::angleAxis(0.987654321f, axis);
|
||||||
|
glm::vec3 translation(35.7f, 2.46f, -1.97f);
|
||||||
|
|
||||||
|
rayStart = rotation * (rayStart + translation);
|
||||||
|
rayDirection = rotation * rayDirection;
|
||||||
|
sphere.setPosition(rotation * translation);
|
||||||
|
|
||||||
|
// ...and test again
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (sphere.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl;
|
||||||
|
}
|
||||||
|
if (distance != FLT_MAX) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeColliderTests::rayHitsCapsule() {
|
||||||
|
float startDistance = 3.0f;
|
||||||
|
float radius = 1.0f;
|
||||||
|
float halfHeight = 2.0f;
|
||||||
|
glm::vec3 center(0.0f);
|
||||||
|
CapsuleShape capsule(radius, halfHeight);
|
||||||
|
|
||||||
|
{ // simple test along xAxis
|
||||||
|
// toward capsule center
|
||||||
|
glm::vec3 rayStart(startDistance, 0.0f, 0.0f);
|
||||||
|
glm::vec3 rayDirection(-1.0f, 0.0f, 0.0f);
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl;
|
||||||
|
}
|
||||||
|
float expectedDistance = startDistance - radius;
|
||||||
|
float relativeError = fabsf(distance - expectedDistance) / startDistance;
|
||||||
|
if (relativeError > EPSILON) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// toward top of cylindrical wall
|
||||||
|
rayStart.y = halfHeight;
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl;
|
||||||
|
}
|
||||||
|
relativeError = fabsf(distance - expectedDistance) / startDistance;
|
||||||
|
if (relativeError > EPSILON) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// toward top cap
|
||||||
|
float delta = 2.0f * EPSILON;
|
||||||
|
rayStart.y = halfHeight + delta;
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl;
|
||||||
|
}
|
||||||
|
relativeError = fabsf(distance - expectedDistance) / startDistance;
|
||||||
|
if (relativeError > EPSILON) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float EDGE_CASE_SLOP_FACTOR = 20.0f;
|
||||||
|
|
||||||
|
// toward tip of top cap
|
||||||
|
rayStart.y = halfHeight + radius - delta;
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl;
|
||||||
|
}
|
||||||
|
expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine
|
||||||
|
relativeError = fabsf(distance - expectedDistance) / startDistance;
|
||||||
|
// for edge cases we allow a LOT of error
|
||||||
|
if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// toward tip of bottom cap
|
||||||
|
rayStart.y = - halfHeight - radius + delta;
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl;
|
||||||
|
}
|
||||||
|
expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine
|
||||||
|
relativeError = fabsf(distance - expectedDistance) / startDistance;
|
||||||
|
// for edge cases we allow a LOT of error
|
||||||
|
if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// toward edge of capsule cylindrical face
|
||||||
|
rayStart.y = 0.0f;
|
||||||
|
rayStart.z = radius - delta;
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl;
|
||||||
|
}
|
||||||
|
expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine
|
||||||
|
relativeError = fabsf(distance - expectedDistance) / startDistance;
|
||||||
|
// for edge cases we allow a LOT of error
|
||||||
|
if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: test at steep angles near cylinder/cap junction
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeColliderTests::rayMissesCapsule() {
|
||||||
|
// same as edge case hit tests, but shifted in the opposite direction
|
||||||
|
float startDistance = 3.0f;
|
||||||
|
float radius = 1.0f;
|
||||||
|
float halfHeight = 2.0f;
|
||||||
|
glm::vec3 center(0.0f);
|
||||||
|
CapsuleShape capsule(radius, halfHeight);
|
||||||
|
|
||||||
|
{ // simple test along xAxis
|
||||||
|
// toward capsule center
|
||||||
|
glm::vec3 rayStart(startDistance, 0.0f, 0.0f);
|
||||||
|
glm::vec3 rayDirection(-1.0f, 0.0f, 0.0f);
|
||||||
|
float delta = 2.0f * EPSILON;
|
||||||
|
|
||||||
|
// over top cap
|
||||||
|
rayStart.y = halfHeight + radius + delta;
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
if (capsule.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl;
|
||||||
|
}
|
||||||
|
if (distance != FLT_MAX) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// below bottom cap
|
||||||
|
rayStart.y = - halfHeight - radius - delta;
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (capsule.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl;
|
||||||
|
}
|
||||||
|
if (distance != FLT_MAX) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// past edge of capsule cylindrical face
|
||||||
|
rayStart.y = 0.0f;
|
||||||
|
rayStart.z = radius + delta;
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (capsule.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl;
|
||||||
|
}
|
||||||
|
if (distance != FLT_MAX) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: test at steep angles near edge
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeColliderTests::rayHitsPlane() {
|
||||||
|
// make a simple plane
|
||||||
|
float planeDistanceFromOrigin = 3.579;
|
||||||
|
glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f);
|
||||||
|
PlaneShape plane;
|
||||||
|
plane.setPosition(planePosition);
|
||||||
|
|
||||||
|
// make a simple ray
|
||||||
|
float startDistance = 1.234f;
|
||||||
|
glm::vec3 rayStart(-startDistance, 0.0f, 0.0f);
|
||||||
|
glm::vec3 rayDirection = glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f));
|
||||||
|
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
if (!plane.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
float expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin;
|
||||||
|
float relativeError = fabsf(distance - expectedDistance) / planeDistanceFromOrigin;
|
||||||
|
if (relativeError > EPSILON) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " << relativeError << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotate the whole system and try again
|
||||||
|
float angle = 37.8f;
|
||||||
|
glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) );
|
||||||
|
glm::quat rotation = glm::angleAxis(angle, axis);
|
||||||
|
|
||||||
|
plane.setPosition(rotation * planePosition);
|
||||||
|
plane.setRotation(rotation);
|
||||||
|
rayStart = rotation * rayStart;
|
||||||
|
rayDirection = rotation * rayDirection;
|
||||||
|
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (!plane.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin;
|
||||||
|
relativeError = fabsf(distance - expectedDistance) / planeDistanceFromOrigin;
|
||||||
|
if (relativeError > EPSILON) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " << relativeError << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeColliderTests::rayMissesPlane() {
|
||||||
|
// make a simple plane
|
||||||
|
float planeDistanceFromOrigin = 3.579;
|
||||||
|
glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f);
|
||||||
|
PlaneShape plane;
|
||||||
|
plane.setPosition(planePosition);
|
||||||
|
|
||||||
|
{ // parallel rays should miss
|
||||||
|
float startDistance = 1.234f;
|
||||||
|
glm::vec3 rayStart(-startDistance, 0.0f, 0.0f);
|
||||||
|
glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, 0.0f, -1.0f));
|
||||||
|
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
if (plane.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl;
|
||||||
|
}
|
||||||
|
if (distance != FLT_MAX) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotate the whole system and try again
|
||||||
|
float angle = 37.8f;
|
||||||
|
glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) );
|
||||||
|
glm::quat rotation = glm::angleAxis(angle, axis);
|
||||||
|
|
||||||
|
plane.setPosition(rotation * planePosition);
|
||||||
|
plane.setRotation(rotation);
|
||||||
|
rayStart = rotation * rayStart;
|
||||||
|
rayDirection = rotation * rayDirection;
|
||||||
|
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (plane.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl;
|
||||||
|
}
|
||||||
|
if (distance != FLT_MAX) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // make a simple ray that points away from plane
|
||||||
|
float startDistance = 1.234f;
|
||||||
|
glm::vec3 rayStart(-startDistance, 0.0f, 0.0f);
|
||||||
|
glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, -1.0f, -1.0f));
|
||||||
|
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
if (plane.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl;
|
||||||
|
}
|
||||||
|
if (distance != FLT_MAX) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotate the whole system and try again
|
||||||
|
float angle = 37.8f;
|
||||||
|
glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) );
|
||||||
|
glm::quat rotation = glm::angleAxis(angle, axis);
|
||||||
|
|
||||||
|
plane.setPosition(rotation * planePosition);
|
||||||
|
plane.setRotation(rotation);
|
||||||
|
rayStart = rotation * rayStart;
|
||||||
|
rayDirection = rotation * rayDirection;
|
||||||
|
|
||||||
|
distance = FLT_MAX;
|
||||||
|
if (plane.findRayIntersection(rayStart, rayDirection, distance)) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl;
|
||||||
|
}
|
||||||
|
if (distance != FLT_MAX) {
|
||||||
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ShapeColliderTests::runAllTests() {
|
void ShapeColliderTests::runAllTests() {
|
||||||
sphereMissesSphere();
|
sphereMissesSphere();
|
||||||
|
@ -911,4 +1311,12 @@ void ShapeColliderTests::runAllTests() {
|
||||||
sphereTouchesAACubeFaces();
|
sphereTouchesAACubeFaces();
|
||||||
sphereTouchesAACubeEdges();
|
sphereTouchesAACubeEdges();
|
||||||
sphereMissesAACube();
|
sphereMissesAACube();
|
||||||
|
|
||||||
|
rayHitsSphere();
|
||||||
|
rayBarelyHitsSphere();
|
||||||
|
rayBarelyMissesSphere();
|
||||||
|
rayHitsCapsule();
|
||||||
|
rayMissesCapsule();
|
||||||
|
rayHitsPlane();
|
||||||
|
rayMissesPlane();
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,14 @@ namespace ShapeColliderTests {
|
||||||
void sphereTouchesAACubeEdges();
|
void sphereTouchesAACubeEdges();
|
||||||
void sphereMissesAACube();
|
void sphereMissesAACube();
|
||||||
|
|
||||||
|
void rayHitsSphere();
|
||||||
|
void rayBarelyHitsSphere();
|
||||||
|
void rayBarelyMissesSphere();
|
||||||
|
void rayHitsCapsule();
|
||||||
|
void rayMissesCapsule();
|
||||||
|
void rayHitsPlane();
|
||||||
|
void rayMissesPlane();
|
||||||
|
|
||||||
void runAllTests();
|
void runAllTests();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue