mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 06:53:01 +02:00
Merge branch 'master' of ssh://github.com/highfidelity/hifi into cleanup
This commit is contained in:
commit
a5aff81881
69 changed files with 1241 additions and 852 deletions
11
README.md
11
README.md
|
@ -71,8 +71,15 @@ We have successfully built on OS X 10.8, Ubuntu and a few other modern Linux
|
||||||
distributions. A Windows build is planned for the future, but not currently in
|
distributions. A Windows build is planned for the future, but not currently in
|
||||||
development.
|
development.
|
||||||
|
|
||||||
On a fresh Ubuntu 13.10 install, these are all the packages you need to grab and build the hifi project:
|
On a fresh Ubuntu 13.10 install, get these requirements from Ubuntu repositories:
|
||||||
<pre>sudo apt-get install build-essential cmake git libcurl4-openssl-dev libqt5scripttools5 libqt5svg5-dev libqt5webkit5-dev libqt5location5 qtlocation5-dev qtdeclarative5-dev qtscript5-dev qtsensors5-dev qtmultimedia5-dev qtquick1-5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-dev</pre>
|
|
||||||
|
sudo apt-get install build-essential cmake git libcurl4-openssl-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-dev
|
||||||
|
|
||||||
|
Then [download lastest Qt packages](http://qt-project.org/downloads), untar/install to your prefered path
|
||||||
|
and set your `QT_CMAKE_PREFIX_PATH` environment variable as described above in the CMake section. It's
|
||||||
|
recommended to set the variable automatically on each shell instance to save this task in the future:
|
||||||
|
|
||||||
|
echo 'export QT_CMAKE_PREFIX_PATH=~/Qt5.2.0/5.2.0/gcc_64/lib/cmake' >> ~/.bashrc
|
||||||
|
|
||||||
Running Interface
|
Running Interface
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -101,6 +101,9 @@ void Agent::run() {
|
||||||
|
|
||||||
// give this AvatarData object to the script engine
|
// give this AvatarData object to the script engine
|
||||||
_scriptEngine.setAvatarData(&scriptedAvatar, "Avatar");
|
_scriptEngine.setAvatarData(&scriptedAvatar, "Avatar");
|
||||||
|
|
||||||
|
// register ourselves to the script engine
|
||||||
|
_scriptEngine.registerGlobalObject("Agent", this);
|
||||||
|
|
||||||
_scriptEngine.setScriptContents(scriptContents);
|
_scriptEngine.setScriptContents(scriptContents);
|
||||||
_scriptEngine.run();
|
_scriptEngine.run();
|
||||||
|
|
|
@ -21,9 +21,14 @@
|
||||||
|
|
||||||
class Agent : public ThreadedAssignment {
|
class Agent : public ThreadedAssignment {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
|
||||||
public:
|
public:
|
||||||
Agent(const QByteArray& packet);
|
Agent(const QByteArray& packet);
|
||||||
|
|
||||||
|
void setIsAvatar(bool isAvatar) { _scriptEngine.setIsAvatar(isAvatar); }
|
||||||
|
bool isAvatar() const { return _scriptEngine.isAvatar(); }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,8 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
||||||
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(), SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
|
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams,
|
||||||
|
Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssignmentClient::sendAssignmentRequest() {
|
void AssignmentClient::sendAssignmentRequest() {
|
||||||
|
|
|
@ -19,10 +19,11 @@
|
||||||
#include "metavoxels/MetavoxelServer.h"
|
#include "metavoxels/MetavoxelServer.h"
|
||||||
|
|
||||||
ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet) {
|
ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet) {
|
||||||
int headerBytes = numBytesForPacketHeader(packet);
|
QDataStream packetStream(packet);
|
||||||
|
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||||
|
|
||||||
quint8 packedType;
|
quint8 packedType;
|
||||||
memcpy(&packedType, packet.data() + headerBytes, sizeof(packedType));
|
packetStream >> packedType;
|
||||||
|
|
||||||
Assignment::Type unpackedType = (Assignment::Type) packedType;
|
Assignment::Type unpackedType = (Assignment::Type) packedType;
|
||||||
|
|
||||||
|
|
|
@ -70,8 +70,7 @@ void broadcastAvatarData() {
|
||||||
QByteArray avatarByteArray;
|
QByteArray avatarByteArray;
|
||||||
avatarByteArray.append(otherNode->getUUID().toRfc4122());
|
avatarByteArray.append(otherNode->getUUID().toRfc4122());
|
||||||
|
|
||||||
|
AvatarData* nodeData = (AvatarData*) otherNode->getLinkedData();
|
||||||
AvatarData *nodeData = (AvatarData *)otherNode->getLinkedData();
|
|
||||||
avatarByteArray.append(nodeData->toByteArray());
|
avatarByteArray.append(nodeData->toByteArray());
|
||||||
|
|
||||||
if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) {
|
if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) {
|
||||||
|
|
|
@ -8,9 +8,11 @@
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <QtCore/QDir>
|
||||||
#include <QtCore/QJsonDocument>
|
#include <QtCore/QJsonDocument>
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
#include <QtCore/QJsonArray>
|
#include <QtCore/QJsonArray>
|
||||||
|
#include <QtCore/QStandardPaths>
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
|
@ -27,65 +29,37 @@ const char* VOXEL_SERVER_CONFIG = "voxelServerConfig";
|
||||||
const char* PARTICLE_SERVER_CONFIG = "particleServerConfig";
|
const char* PARTICLE_SERVER_CONFIG = "particleServerConfig";
|
||||||
const char* METAVOXEL_SERVER_CONFIG = "metavoxelServerConfig";
|
const char* METAVOXEL_SERVER_CONFIG = "metavoxelServerConfig";
|
||||||
|
|
||||||
void signalhandler(int sig){
|
|
||||||
if (sig == SIGINT) {
|
|
||||||
qApp->quit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const quint16 DOMAIN_SERVER_HTTP_PORT = 8080;
|
const quint16 DOMAIN_SERVER_HTTP_PORT = 8080;
|
||||||
|
|
||||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
QCoreApplication(argc, argv),
|
QCoreApplication(argc, argv),
|
||||||
_HTTPManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
_HTTPManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||||
_assignmentQueueMutex(),
|
_staticAssignmentHash(),
|
||||||
_assignmentQueue(),
|
_assignmentQueue(),
|
||||||
_staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())),
|
|
||||||
_staticAssignmentFileData(NULL),
|
|
||||||
_voxelServerConfig(NULL),
|
|
||||||
_metavoxelServerConfig(NULL),
|
|
||||||
_hasCompletedRestartHold(false)
|
_hasCompletedRestartHold(false)
|
||||||
{
|
{
|
||||||
signal(SIGINT, signalhandler);
|
|
||||||
|
|
||||||
const char CUSTOM_PORT_OPTION[] = "-p";
|
const char CUSTOM_PORT_OPTION[] = "-p";
|
||||||
const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION);
|
const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION);
|
||||||
unsigned short domainServerPort = customPortString ? atoi(customPortString) : DEFAULT_DOMAIN_SERVER_PORT;
|
unsigned short domainServerPort = customPortString ? atoi(customPortString) : DEFAULT_DOMAIN_SERVER_PORT;
|
||||||
|
|
||||||
const char CONFIG_FILE_OPTION[] = "-c";
|
QStringList argumentList = arguments();
|
||||||
const char* configFilePath = getCmdOption(argc, (const char**) argv, CONFIG_FILE_OPTION);
|
int argumentIndex = 0;
|
||||||
|
|
||||||
if (!readConfigFile(configFilePath)) {
|
QSet<Assignment::Type> parsedTypes(QSet<Assignment::Type>() << Assignment::AgentType);
|
||||||
QByteArray voxelConfigOption = QString("--%1").arg(VOXEL_SERVER_CONFIG).toLocal8Bit();
|
parseCommandLineTypeConfigs(argumentList, parsedTypes);
|
||||||
_voxelServerConfig = getCmdOption(argc, (const char**) argv, voxelConfigOption.constData());
|
|
||||||
|
const QString CONFIG_FILE_OPTION = "--configFile";
|
||||||
QByteArray particleConfigOption = QString("--%1").arg(PARTICLE_SERVER_CONFIG).toLocal8Bit();
|
if ((argumentIndex = argumentList.indexOf(CONFIG_FILE_OPTION)) != -1) {
|
||||||
_particleServerConfig = getCmdOption(argc, (const char**) argv, particleConfigOption.constData());
|
QString configFilePath = argumentList.value(argumentIndex + 1);
|
||||||
|
readConfigFile(configFilePath, parsedTypes);
|
||||||
QByteArray metavoxelConfigOption = QString("--%1").arg(METAVOXEL_SERVER_CONFIG).toLocal8Bit();
|
|
||||||
_metavoxelServerConfig = getCmdOption(argc, (const char**) argv, metavoxelConfigOption.constData());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
populateDefaultStaticAssignmentsExcludingTypes(parsedTypes);
|
||||||
|
|
||||||
NodeList* nodeList = NodeList::createInstance(NodeType::DomainServer, domainServerPort);
|
NodeList* nodeList = NodeList::createInstance(NodeType::DomainServer, domainServerPort);
|
||||||
|
|
||||||
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), this, SLOT(nodeKilled(SharedNodePointer)));
|
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), this, SLOT(nodeKilled(SharedNodePointer)));
|
||||||
|
|
||||||
if (!_staticAssignmentFile.exists() || _voxelServerConfig) {
|
|
||||||
|
|
||||||
if (_voxelServerConfig) {
|
|
||||||
// we have a new VS config, clear the existing file to start fresh
|
|
||||||
_staticAssignmentFile.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
prepopulateStaticAssignmentFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
_staticAssignmentFile.open(QIODevice::ReadWrite);
|
|
||||||
|
|
||||||
_staticAssignmentFileData = _staticAssignmentFile.map(0, _staticAssignmentFile.size());
|
|
||||||
|
|
||||||
_staticAssignments = (Assignment*) _staticAssignmentFileData;
|
|
||||||
|
|
||||||
QTimer* silentNodeTimer = new QTimer(this);
|
QTimer* silentNodeTimer = new QTimer(this);
|
||||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||||
|
@ -94,8 +68,145 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
|
|
||||||
// fire a single shot timer to add static assignments back into the queue after a restart
|
// fire a single shot timer to add static assignments back into the queue after a restart
|
||||||
QTimer::singleShot(RESTART_HOLD_TIME_MSECS, this, SLOT(addStaticAssignmentsBackToQueueAfterRestart()));
|
QTimer::singleShot(RESTART_HOLD_TIME_MSECS, this, SLOT(addStaticAssignmentsBackToQueueAfterRestart()));
|
||||||
|
}
|
||||||
|
|
||||||
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
|
void DomainServer::parseCommandLineTypeConfigs(const QStringList& argumentList, QSet<Assignment::Type>& excludedTypes) {
|
||||||
|
// check for configs from the command line, these take precedence
|
||||||
|
const QString CONFIG_TYPE_OPTION = "--configType";
|
||||||
|
int clConfigIndex = argumentList.indexOf(CONFIG_TYPE_OPTION);
|
||||||
|
|
||||||
|
// enumerate all CL config overrides and parse them to files
|
||||||
|
while (clConfigIndex != -1) {
|
||||||
|
int clConfigType = argumentList.value(clConfigIndex + 1).toInt();
|
||||||
|
if (clConfigType < Assignment::AllTypes && !excludedTypes.contains((Assignment::Type) clConfigIndex)) {
|
||||||
|
Assignment::Type assignmentType = (Assignment::Type) clConfigType;
|
||||||
|
createStaticAssignmentsForTypeGivenConfigString((Assignment::Type) assignmentType,
|
||||||
|
argumentList.value(clConfigIndex + 2));
|
||||||
|
excludedTypes.insert(assignmentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
clConfigIndex = argumentList.indexOf(CONFIG_TYPE_OPTION, clConfigIndex + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempts to read configuration from specified path
|
||||||
|
// returns true on success, false otherwise
|
||||||
|
void DomainServer::readConfigFile(const QString& path, QSet<Assignment::Type>& excludedTypes) {
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
// config file not specified
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!QFile::exists(path)) {
|
||||||
|
qWarning("Specified configuration file does not exist!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile configFile(path);
|
||||||
|
if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
qWarning("Can't open specified configuration file!");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Reading configuration from" << path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream configStream(&configFile);
|
||||||
|
QByteArray configStringByteArray = configStream.readAll().toUtf8();
|
||||||
|
QJsonObject configDocObject = QJsonDocument::fromJson(configStringByteArray).object();
|
||||||
|
configFile.close();
|
||||||
|
|
||||||
|
QSet<Assignment::Type> appendedExcludedTypes = excludedTypes;
|
||||||
|
|
||||||
|
foreach (const QString& rootStringValue, configDocObject.keys()) {
|
||||||
|
int possibleConfigType = rootStringValue.toInt();
|
||||||
|
|
||||||
|
if (possibleConfigType < Assignment::AllTypes
|
||||||
|
&& !excludedTypes.contains((Assignment::Type) possibleConfigType)) {
|
||||||
|
// this is an appropriate config type and isn't already in our excluded types
|
||||||
|
// we are good to parse it
|
||||||
|
Assignment::Type assignmentType = (Assignment::Type) possibleConfigType;
|
||||||
|
QString configString = readServerAssignmentConfig(configDocObject, rootStringValue);
|
||||||
|
createStaticAssignmentsForTypeGivenConfigString(assignmentType, configString);
|
||||||
|
|
||||||
|
excludedTypes.insert(assignmentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find assignment configurations on the specified node name and json object
|
||||||
|
// returns a string in the form of its equivalent cmd line params
|
||||||
|
QString DomainServer::readServerAssignmentConfig(const QJsonObject& jsonObject, const QString& nodeName) {
|
||||||
|
QJsonArray nodeArray = jsonObject[nodeName].toArray();
|
||||||
|
|
||||||
|
QStringList serverConfig;
|
||||||
|
foreach (const QJsonValue& childValue, nodeArray) {
|
||||||
|
QString cmdParams;
|
||||||
|
QJsonObject childObject = childValue.toObject();
|
||||||
|
QStringList keys = childObject.keys();
|
||||||
|
for (int i = 0; i < keys.size(); i++) {
|
||||||
|
QString key = keys[i];
|
||||||
|
QString value = childObject[key].toString();
|
||||||
|
// both cmd line params and json keys are the same
|
||||||
|
cmdParams += QString("--%1 %2 ").arg(key, value);
|
||||||
|
}
|
||||||
|
serverConfig << cmdParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
// according to split() calls from DomainServer::prepopulateStaticAssignmentFile
|
||||||
|
// we shold simply join them with semicolons
|
||||||
|
return serverConfig.join(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainServer::addStaticAssignmentToAssignmentHash(Assignment* newAssignment) {
|
||||||
|
qDebug() << "Inserting assignment" << *newAssignment << "to static assignment hash.";
|
||||||
|
_staticAssignmentHash.insert(newAssignment->getUUID(), SharedAssignmentPointer(newAssignment));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainServer::createStaticAssignmentsForTypeGivenConfigString(Assignment::Type type, const QString& configString) {
|
||||||
|
// we have a string for config for this type
|
||||||
|
qDebug() << "Parsing command line config for assignment type" << type;
|
||||||
|
|
||||||
|
QStringList multiConfigList = configString.split(";", QString::SkipEmptyParts);
|
||||||
|
|
||||||
|
const QString ASSIGNMENT_CONFIG_POOL_REGEX = "--pool\\s*(\\w+)";
|
||||||
|
QRegExp poolRegex(ASSIGNMENT_CONFIG_POOL_REGEX);
|
||||||
|
|
||||||
|
// read each config to a payload for this type of assignment
|
||||||
|
for (int i = 0; i < multiConfigList.size(); i++) {
|
||||||
|
QString config = multiConfigList.at(i);
|
||||||
|
|
||||||
|
// check the config string for a pool
|
||||||
|
QString assignmentPool;
|
||||||
|
|
||||||
|
int poolIndex = poolRegex.indexIn(config);
|
||||||
|
|
||||||
|
if (poolIndex != -1) {
|
||||||
|
assignmentPool = poolRegex.cap(1);
|
||||||
|
|
||||||
|
// remove the pool from the config string, the assigned node doesn't need it
|
||||||
|
config.remove(poolIndex, poolRegex.matchedLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug("Type %d config[%d] = %s", type, i, config.toLocal8Bit().constData());
|
||||||
|
|
||||||
|
Assignment* configAssignment = new Assignment(Assignment::CreateCommand, type, assignmentPool);
|
||||||
|
|
||||||
|
configAssignment->setPayload(config.toUtf8());
|
||||||
|
|
||||||
|
addStaticAssignmentToAssignmentHash(configAssignment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes) {
|
||||||
|
// enumerate over all assignment types and see if we've already excluded it
|
||||||
|
for (int defaultedType = Assignment::AudioMixerType; defaultedType != Assignment::AllTypes; defaultedType++) {
|
||||||
|
if (!excludedTypes.contains((Assignment::Type) defaultedType)) {
|
||||||
|
// type has not been set from a command line or config file config, use the default
|
||||||
|
// by clearing whatever exists and writing a single default assignment with no payload
|
||||||
|
Assignment* newAssignment = new Assignment(Assignment::CreateCommand, (Assignment::Type) defaultedType);
|
||||||
|
addStaticAssignmentToAssignmentHash(newAssignment);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::readAvailableDatagrams() {
|
void DomainServer::readAvailableDatagrams() {
|
||||||
|
@ -148,13 +259,13 @@ void DomainServer::readAvailableDatagrams() {
|
||||||
<< NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer
|
<< NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer
|
||||||
<< NodeType::MetavoxelServer;
|
<< NodeType::MetavoxelServer;
|
||||||
|
|
||||||
Assignment* matchingStaticAssignment = NULL;
|
SharedAssignmentPointer matchingStaticAssignment;
|
||||||
|
|
||||||
|
// check if this is a non-statically assigned node, a node that is assigned and checking in for the first time
|
||||||
|
// or a node that has already checked in and is continuing to report for duty
|
||||||
if (!STATICALLY_ASSIGNED_NODES.contains(nodeType)
|
if (!STATICALLY_ASSIGNED_NODES.contains(nodeType)
|
||||||
|| ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType))
|
|| (matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType))
|
||||||
|| checkInWithUUIDMatchesExistingNode(nodePublicAddress,
|
|| nodeList->getInstance()->nodeWithUUID(nodeUUID))
|
||||||
nodeLocalAddress,
|
|
||||||
nodeUUID)))
|
|
||||||
{
|
{
|
||||||
SharedNodePointer checkInNode = nodeList->addOrUpdateNode(nodeUUID,
|
SharedNodePointer checkInNode = nodeList->addOrUpdateNode(nodeUUID,
|
||||||
nodeType,
|
nodeType,
|
||||||
|
@ -167,19 +278,13 @@ void DomainServer::readAvailableDatagrams() {
|
||||||
if (matchingStaticAssignment) {
|
if (matchingStaticAssignment) {
|
||||||
// this was a newly added node with a matching static assignment
|
// this was a newly added node with a matching static assignment
|
||||||
|
|
||||||
|
// remove the matching assignment from the assignment queue so we don't take the next check in
|
||||||
|
// (if it exists)
|
||||||
if (_hasCompletedRestartHold) {
|
if (_hasCompletedRestartHold) {
|
||||||
// remove the matching assignment from the assignment queue so we don't take the next check in
|
removeMatchingAssignmentFromQueue(matchingStaticAssignment);
|
||||||
removeAssignmentFromQueue(matchingStaticAssignment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the linked data for this node to a copy of the matching assignment
|
|
||||||
// so we can re-queue it should the node die
|
|
||||||
Assignment* nodeCopyOfMatchingAssignment = new Assignment(*matchingStaticAssignment);
|
|
||||||
|
|
||||||
checkInNode->setLinkedData(nodeCopyOfMatchingAssignment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
quint8 numInterestTypes = 0;
|
quint8 numInterestTypes = 0;
|
||||||
packetStream >> numInterestTypes;
|
packetStream >> numInterestTypes;
|
||||||
|
|
||||||
|
@ -210,34 +315,29 @@ void DomainServer::readAvailableDatagrams() {
|
||||||
}
|
}
|
||||||
} else if (requestType == PacketTypeRequestAssignment) {
|
} else if (requestType == PacketTypeRequestAssignment) {
|
||||||
|
|
||||||
if (_assignmentQueue.size() > 0) {
|
// construct the requested assignment from the packet data
|
||||||
// construct the requested assignment from the packet data
|
Assignment requestAssignment(receivedPacket);
|
||||||
Assignment requestAssignment(receivedPacket);
|
|
||||||
|
qDebug() << "Received a request for assignment type" << requestAssignment.getType()
|
||||||
|
<< "from" << senderSockAddr;
|
||||||
|
|
||||||
|
SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
|
||||||
|
|
||||||
|
if (assignmentToDeploy) {
|
||||||
|
qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << senderSockAddr;
|
||||||
|
|
||||||
qDebug("Received a request for assignment type %i from %s.",
|
// give this assignment out, either the type matches or the requestor said they will take any
|
||||||
requestAssignment.getType(), qPrintable(senderSockAddr.getAddress().toString()));
|
assignmentPacket.resize(numAssignmentPacketHeaderBytes);
|
||||||
|
|
||||||
Assignment* assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
|
QDataStream assignmentStream(&assignmentPacket, QIODevice::Append);
|
||||||
|
|
||||||
if (assignmentToDeploy) {
|
assignmentStream << *assignmentToDeploy.data();
|
||||||
// give this assignment out, either the type matches or the requestor said they will take any
|
|
||||||
assignmentPacket.resize(numAssignmentPacketHeaderBytes);
|
|
||||||
|
|
||||||
QDataStream assignmentStream(&assignmentPacket, QIODevice::Append);
|
|
||||||
|
|
||||||
assignmentStream << *assignmentToDeploy;
|
|
||||||
|
|
||||||
nodeList->getNodeSocket().writeDatagram(assignmentPacket,
|
|
||||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
|
||||||
|
|
||||||
if (assignmentToDeploy->getNumberOfInstances() == 0) {
|
|
||||||
// there are no more instances of this script to send out, delete it
|
|
||||||
delete assignmentToDeploy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
nodeList->getNodeSocket().writeDatagram(assignmentPacket,
|
||||||
|
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Received an invalid assignment request from" << senderSockAddr.getAddress();
|
qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType()
|
||||||
|
<< "from" << senderSockAddr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,70 +381,6 @@ QJsonObject jsonObjectForNode(Node* node) {
|
||||||
return nodeJson;
|
return nodeJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempts to read configuration from specified path
|
|
||||||
// returns true on success, false otherwise
|
|
||||||
bool DomainServer::readConfigFile(const char* path) {
|
|
||||||
if (!path) {
|
|
||||||
// config file not specified
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!QFile::exists(path)) {
|
|
||||||
qWarning("Specified configuration file does not exist!\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile configFile(path);
|
|
||||||
if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
||||||
qWarning("Can't open specified configuration file!\n");
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
qDebug("Reading configuration from %s\n", path);
|
|
||||||
}
|
|
||||||
QTextStream configStream(&configFile);
|
|
||||||
QByteArray configStringByteArray = configStream.readAll().toUtf8();
|
|
||||||
QJsonObject configDocObject = QJsonDocument::fromJson(configStringByteArray).object();
|
|
||||||
configFile.close();
|
|
||||||
|
|
||||||
QString voxelServerConfig = readServerAssignmentConfig(configDocObject, VOXEL_SERVER_CONFIG);
|
|
||||||
_voxelServerConfig = new char[strlen(voxelServerConfig.toLocal8Bit().constData()) +1];
|
|
||||||
_voxelServerConfig = strcpy((char *) _voxelServerConfig, voxelServerConfig.toLocal8Bit().constData() + '\0');
|
|
||||||
|
|
||||||
QString particleServerConfig = readServerAssignmentConfig(configDocObject, PARTICLE_SERVER_CONFIG);
|
|
||||||
_particleServerConfig = new char[strlen(particleServerConfig.toLocal8Bit().constData()) +1];
|
|
||||||
_particleServerConfig = strcpy((char *) _particleServerConfig, particleServerConfig.toLocal8Bit().constData() + '\0');
|
|
||||||
|
|
||||||
QString metavoxelServerConfig = readServerAssignmentConfig(configDocObject, METAVOXEL_SERVER_CONFIG);
|
|
||||||
_metavoxelServerConfig = new char[strlen(metavoxelServerConfig.toLocal8Bit().constData()) +1];
|
|
||||||
_metavoxelServerConfig = strcpy((char *) _metavoxelServerConfig, metavoxelServerConfig.toLocal8Bit().constData() + '\0');
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find assignment configurations on the specified node name and json object
|
|
||||||
// returns a string in the form of its equivalent cmd line params
|
|
||||||
QString DomainServer::readServerAssignmentConfig(QJsonObject jsonObject, const char* nodeName) {
|
|
||||||
QJsonArray nodeArray = jsonObject[nodeName].toArray();
|
|
||||||
|
|
||||||
QStringList serverConfig;
|
|
||||||
foreach (const QJsonValue & childValue, nodeArray) {
|
|
||||||
QString cmdParams;
|
|
||||||
QJsonObject childObject = childValue.toObject();
|
|
||||||
QStringList keys = childObject.keys();
|
|
||||||
for (int i = 0; i < keys.size(); i++) {
|
|
||||||
QString key = keys[i];
|
|
||||||
QString value = childObject[key].toString();
|
|
||||||
// both cmd line params and json keys are the same
|
|
||||||
cmdParams += QString("--%1 %2 ").arg(key, value);
|
|
||||||
}
|
|
||||||
serverConfig << cmdParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
// according to split() calls from DomainServer::prepopulateStaticAssignmentFile
|
|
||||||
// we shold simply join them with semicolons
|
|
||||||
return serverConfig.join(';');
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& path) {
|
bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& path) {
|
||||||
const QString JSON_MIME_TYPE = "application/json";
|
const QString JSON_MIME_TYPE = "application/json";
|
||||||
|
|
||||||
|
@ -373,24 +409,19 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
||||||
QJsonObject queuedAssignmentsJSON;
|
QJsonObject queuedAssignmentsJSON;
|
||||||
|
|
||||||
// add the queued but unfilled assignments to the json
|
// add the queued but unfilled assignments to the json
|
||||||
std::deque<Assignment*>::iterator assignment = _assignmentQueue.begin();
|
foreach(const SharedAssignmentPointer& assignment, _assignmentQueue) {
|
||||||
|
|
||||||
while (assignment != _assignmentQueue.end()) {
|
|
||||||
QJsonObject queuedAssignmentJSON;
|
QJsonObject queuedAssignmentJSON;
|
||||||
|
|
||||||
QString uuidString = uuidStringWithoutCurlyBraces((*assignment)->getUUID());
|
QString uuidString = uuidStringWithoutCurlyBraces(assignment->getUUID());
|
||||||
queuedAssignmentJSON[JSON_KEY_TYPE] = QString((*assignment)->getTypeName());
|
queuedAssignmentJSON[JSON_KEY_TYPE] = QString(assignment->getTypeName());
|
||||||
|
|
||||||
// if the assignment has a pool, add it
|
// if the assignment has a pool, add it
|
||||||
if (!(*assignment)->getPool().isEmpty()) {
|
if (!assignment->getPool().isEmpty()) {
|
||||||
queuedAssignmentJSON[JSON_KEY_POOL] = (*assignment)->getPool();
|
queuedAssignmentJSON[JSON_KEY_POOL] = assignment->getPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
// add this queued assignment to the JSON
|
// add this queued assignment to the JSON
|
||||||
queuedAssignmentsJSON[uuidString] = queuedAssignmentJSON;
|
queuedAssignmentsJSON[uuidString] = queuedAssignmentJSON;
|
||||||
|
|
||||||
// push forward the iterator to check the next assignment
|
|
||||||
assignment++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assignmentJSON["queued"] = queuedAssignmentsJSON;
|
assignmentJSON["queued"] = queuedAssignmentsJSON;
|
||||||
|
@ -428,11 +459,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
||||||
// this is a script upload - ask the HTTPConnection to parse the form data
|
// this is a script upload - ask the HTTPConnection to parse the form data
|
||||||
QList<FormData> formData = connection->parseFormData();
|
QList<FormData> formData = connection->parseFormData();
|
||||||
|
|
||||||
// create an assignment for this saved script, for now make it local only
|
// create an assignment for this saved script
|
||||||
Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand,
|
Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType);
|
||||||
Assignment::AgentType,
|
|
||||||
NULL,
|
|
||||||
Assignment::LocalLocation);
|
|
||||||
|
|
||||||
// check how many instances of this assignment the user wants by checking the ASSIGNMENT-INSTANCES header
|
// check how many instances of this assignment the user wants by checking the ASSIGNMENT-INSTANCES header
|
||||||
const QString ASSIGNMENT_INSTANCES_HEADER = "ASSIGNMENT-INSTANCES";
|
const QString ASSIGNMENT_INSTANCES_HEADER = "ASSIGNMENT-INSTANCES";
|
||||||
|
@ -466,10 +494,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
||||||
connection->respond(HTTPConnection::StatusCode200);
|
connection->respond(HTTPConnection::StatusCode200);
|
||||||
|
|
||||||
// add the script assigment to the assignment queue
|
// add the script assigment to the assignment queue
|
||||||
// lock the assignment queue mutex since we're operating on a different thread than DS main
|
_assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment));
|
||||||
_assignmentQueueMutex.lock();
|
|
||||||
_assignmentQueue.push_back(scriptAssignment);
|
|
||||||
_assignmentQueueMutex.unlock();
|
|
||||||
}
|
}
|
||||||
} else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) {
|
} else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) {
|
||||||
if (path.startsWith(URI_NODE)) {
|
if (path.startsWith(URI_NODE)) {
|
||||||
|
@ -504,307 +529,137 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::addReleasedAssignmentBackToQueue(Assignment* releasedAssignment) {
|
void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) {
|
||||||
qDebug() << "Adding assignment" << *releasedAssignment << " back to queue.";
|
QUuid oldUUID = assignment->getUUID();
|
||||||
|
assignment->resetUUID();
|
||||||
// find this assignment in the static file
|
|
||||||
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
|
qDebug() << "Reset UUID for assignment -" << *assignment.data() << "- and added to queue. Old UUID was"
|
||||||
if (_staticAssignments[i].getUUID() == releasedAssignment->getUUID()) {
|
<< uuidStringWithoutCurlyBraces(oldUUID);
|
||||||
// reset the UUID on the static assignment
|
|
||||||
_staticAssignments[i].resetUUID();
|
// add the static assignment back under the right UUID, and to the queue
|
||||||
|
_staticAssignmentHash.insert(assignment->getUUID(), assignment);
|
||||||
// put this assignment back in the queue so it goes out
|
|
||||||
_assignmentQueueMutex.lock();
|
_assignmentQueue.enqueue(assignment);
|
||||||
_assignmentQueue.push_back(&_staticAssignments[i]);
|
|
||||||
_assignmentQueueMutex.unlock();
|
// remove the old assignment from the _staticAssignmentHash
|
||||||
|
// this must be done last so copies are created before the assignment passed by reference is killed
|
||||||
} else if (_staticAssignments[i].getUUID().isNull()) {
|
_staticAssignmentHash.remove(oldUUID);
|
||||||
// we are at the blank part of the static assignments - break out
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::nodeKilled(SharedNodePointer node) {
|
void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||||
// if this node has linked data it was from an assignment
|
// if this node's UUID matches a static assignment we need to throw it back in the assignment queue
|
||||||
if (node->getLinkedData()) {
|
SharedAssignmentPointer matchedAssignment = _staticAssignmentHash.value(node->getUUID());
|
||||||
Assignment* nodeAssignment = (Assignment*) node->getLinkedData();
|
|
||||||
|
if (matchedAssignment) {
|
||||||
addReleasedAssignmentBackToQueue(nodeAssignment);
|
refreshStaticAssignmentAndAddToQueue(matchedAssignment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::prepopulateStaticAssignmentFile() {
|
SharedAssignmentPointer DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) {
|
||||||
int numFreshStaticAssignments = 0;
|
|
||||||
|
|
||||||
// write a fresh static assignment array to file
|
|
||||||
|
|
||||||
Assignment freshStaticAssignments[MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS];
|
|
||||||
|
|
||||||
// pre-populate the first static assignment list with assignments for root AuM, AvM, VS
|
|
||||||
freshStaticAssignments[numFreshStaticAssignments++] = Assignment(Assignment::CreateCommand, Assignment::AudioMixerType);
|
|
||||||
freshStaticAssignments[numFreshStaticAssignments++] = Assignment(Assignment::CreateCommand, Assignment::AvatarMixerType);
|
|
||||||
|
|
||||||
// Handle Domain/Voxel Server configuration command line arguments
|
|
||||||
if (_voxelServerConfig) {
|
|
||||||
qDebug("Reading Voxel Server Configuration.");
|
|
||||||
qDebug() << "config: " << _voxelServerConfig;
|
|
||||||
|
|
||||||
QString multiConfig((const char*) _voxelServerConfig);
|
|
||||||
QStringList multiConfigList = multiConfig.split(";");
|
|
||||||
|
|
||||||
// read each config to a payload for a VS assignment
|
|
||||||
for (int i = 0; i < multiConfigList.size(); i++) {
|
|
||||||
QString config = multiConfigList.at(i);
|
|
||||||
|
|
||||||
qDebug("config[%d]=%s", i, config.toLocal8Bit().constData());
|
|
||||||
|
|
||||||
// Now, parse the config to check for a pool
|
|
||||||
const char ASSIGNMENT_CONFIG_POOL_OPTION[] = "--pool";
|
|
||||||
QString assignmentPool;
|
|
||||||
|
|
||||||
int poolIndex = config.indexOf(ASSIGNMENT_CONFIG_POOL_OPTION);
|
|
||||||
|
|
||||||
if (poolIndex >= 0) {
|
|
||||||
int spaceBeforePoolIndex = config.indexOf(' ', poolIndex);
|
|
||||||
int spaceAfterPoolIndex = config.indexOf(' ', spaceBeforePoolIndex);
|
|
||||||
|
|
||||||
assignmentPool = config.mid(spaceBeforePoolIndex + 1, spaceAfterPoolIndex);
|
|
||||||
qDebug() << "The pool for this voxel-assignment is" << assignmentPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assignment voxelServerAssignment(Assignment::CreateCommand,
|
|
||||||
Assignment::VoxelServerType,
|
|
||||||
(assignmentPool.isEmpty() ? NULL : assignmentPool.toLocal8Bit().constData()));
|
|
||||||
|
|
||||||
voxelServerAssignment.setPayload(config.toUtf8());
|
|
||||||
|
|
||||||
freshStaticAssignments[numFreshStaticAssignments++] = voxelServerAssignment;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Assignment rootVoxelServerAssignment(Assignment::CreateCommand, Assignment::VoxelServerType);
|
|
||||||
freshStaticAssignments[numFreshStaticAssignments++] = rootVoxelServerAssignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Domain/Particle Server configuration command line arguments
|
|
||||||
if (_particleServerConfig) {
|
|
||||||
qDebug("Reading Particle Server Configuration.");
|
|
||||||
qDebug() << "config: " << _particleServerConfig;
|
|
||||||
|
|
||||||
QString multiConfig((const char*) _particleServerConfig);
|
|
||||||
QStringList multiConfigList = multiConfig.split(";");
|
|
||||||
|
|
||||||
// read each config to a payload for a VS assignment
|
|
||||||
for (int i = 0; i < multiConfigList.size(); i++) {
|
|
||||||
QString config = multiConfigList.at(i);
|
|
||||||
|
|
||||||
qDebug("config[%d]=%s", i, config.toLocal8Bit().constData());
|
|
||||||
|
|
||||||
// Now, parse the config to check for a pool
|
|
||||||
const char ASSIGNMENT_CONFIG_POOL_OPTION[] = "--pool";
|
|
||||||
QString assignmentPool;
|
|
||||||
|
|
||||||
int poolIndex = config.indexOf(ASSIGNMENT_CONFIG_POOL_OPTION);
|
|
||||||
|
|
||||||
if (poolIndex >= 0) {
|
|
||||||
int spaceBeforePoolIndex = config.indexOf(' ', poolIndex);
|
|
||||||
int spaceAfterPoolIndex = config.indexOf(' ', spaceBeforePoolIndex);
|
|
||||||
|
|
||||||
assignmentPool = config.mid(spaceBeforePoolIndex + 1, spaceAfterPoolIndex);
|
|
||||||
qDebug() << "The pool for this particle-assignment is" << assignmentPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assignment particleServerAssignment(Assignment::CreateCommand,
|
|
||||||
Assignment::ParticleServerType,
|
|
||||||
(assignmentPool.isEmpty() ? NULL : assignmentPool.toLocal8Bit().constData()));
|
|
||||||
|
|
||||||
particleServerAssignment.setPayload(config.toLocal8Bit());
|
|
||||||
|
|
||||||
freshStaticAssignments[numFreshStaticAssignments++] = particleServerAssignment;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Assignment rootParticleServerAssignment(Assignment::CreateCommand, Assignment::ParticleServerType);
|
|
||||||
freshStaticAssignments[numFreshStaticAssignments++] = rootParticleServerAssignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle metavoxel configuration command line argument
|
|
||||||
Assignment& metavoxelAssignment = (freshStaticAssignments[numFreshStaticAssignments++] =
|
|
||||||
Assignment(Assignment::CreateCommand, Assignment::MetavoxelServerType));
|
|
||||||
if (_metavoxelServerConfig) {
|
|
||||||
metavoxelAssignment.setPayload(QByteArray(_metavoxelServerConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "Adding" << numFreshStaticAssignments << "static assignments to fresh file.";
|
|
||||||
|
|
||||||
_staticAssignmentFile.open(QIODevice::WriteOnly);
|
|
||||||
_staticAssignmentFile.write((char*) &freshStaticAssignments, sizeof(freshStaticAssignments));
|
|
||||||
_staticAssignmentFile.resize(MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS * sizeof(Assignment));
|
|
||||||
_staticAssignmentFile.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
Assignment* DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) {
|
|
||||||
// pull the UUID passed with the check in
|
|
||||||
|
|
||||||
if (_hasCompletedRestartHold) {
|
if (_hasCompletedRestartHold) {
|
||||||
_assignmentQueueMutex.lock();
|
// look for a match in the assignment hash
|
||||||
|
|
||||||
// iterate the assignment queue to check for a match
|
QQueue<SharedAssignmentPointer>::iterator i = _assignmentQueue.begin();
|
||||||
std::deque<Assignment*>::iterator assignment = _assignmentQueue.begin();
|
|
||||||
while (assignment != _assignmentQueue.end()) {
|
while (i != _assignmentQueue.end()) {
|
||||||
if ((*assignment)->getUUID() == checkInUUID) {
|
if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == checkInUUID) {
|
||||||
// return the matched assignment
|
return _assignmentQueue.takeAt(i - _assignmentQueue.begin());
|
||||||
_assignmentQueueMutex.unlock();
|
|
||||||
return *assignment;
|
|
||||||
} else {
|
} else {
|
||||||
// no match, push deque iterator forwards
|
++i;
|
||||||
assignment++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_assignmentQueueMutex.unlock();
|
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
|
SharedAssignmentPointer matchingStaticAssignment = _staticAssignmentHash.value(checkInUUID);
|
||||||
if (_staticAssignments[i].getUUID() == checkInUUID) {
|
if (matchingStaticAssignment && matchingStaticAssignment->getType() == nodeType) {
|
||||||
// return matched assignment
|
return matchingStaticAssignment;
|
||||||
return &_staticAssignments[i];
|
|
||||||
} else if (_staticAssignments[i].getUUID().isNull()) {
|
|
||||||
// end of static assignments, no match - return NULL
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return SharedAssignmentPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assignment* DomainServer::deployableAssignmentForRequest(Assignment& requestAssignment) {
|
SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assignment& requestAssignment) {
|
||||||
_assignmentQueueMutex.lock();
|
|
||||||
|
|
||||||
// this is an unassigned client talking to us directly for an assignment
|
// this is an unassigned client talking to us directly for an assignment
|
||||||
// go through our queue and see if there are any assignments to give out
|
// go through our queue and see if there are any assignments to give out
|
||||||
std::deque<Assignment*>::iterator assignment = _assignmentQueue.begin();
|
QQueue<SharedAssignmentPointer>::iterator sharedAssignment = _assignmentQueue.begin();
|
||||||
|
|
||||||
while (assignment != _assignmentQueue.end()) {
|
while (sharedAssignment != _assignmentQueue.end()) {
|
||||||
|
Assignment* assignment = sharedAssignment->data();
|
||||||
bool requestIsAllTypes = requestAssignment.getType() == Assignment::AllTypes;
|
bool requestIsAllTypes = requestAssignment.getType() == Assignment::AllTypes;
|
||||||
bool assignmentTypesMatch = (*assignment)->getType() == requestAssignment.getType();
|
bool assignmentTypesMatch = assignment->getType() == requestAssignment.getType();
|
||||||
bool nietherHasPool = (*assignment)->getPool().isEmpty() && requestAssignment.getPool().isEmpty();
|
bool nietherHasPool = assignment->getPool().isEmpty() && requestAssignment.getPool().isEmpty();
|
||||||
bool assignmentPoolsMatch = (*assignment)->getPool() == requestAssignment.getPool();
|
bool assignmentPoolsMatch = assignment->getPool() == requestAssignment.getPool();
|
||||||
|
|
||||||
if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) {
|
if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) {
|
||||||
|
|
||||||
Assignment* deployableAssignment = *assignment;
|
if (assignment->getType() == Assignment::AgentType) {
|
||||||
|
|
||||||
if ((*assignment)->getType() == Assignment::AgentType) {
|
|
||||||
// if there is more than one instance to send out, simply decrease the number of instances
|
// if there is more than one instance to send out, simply decrease the number of instances
|
||||||
|
|
||||||
if ((*assignment)->getNumberOfInstances() == 1) {
|
if (assignment->getNumberOfInstances() == 1) {
|
||||||
_assignmentQueue.erase(assignment);
|
return _assignmentQueue.takeAt(sharedAssignment - _assignmentQueue.begin());
|
||||||
|
} else {
|
||||||
|
assignment->decrementNumberOfInstances();
|
||||||
|
return *sharedAssignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
deployableAssignment->decrementNumberOfInstances();
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// remove the assignment from the queue
|
// remove the assignment from the queue
|
||||||
_assignmentQueue.erase(assignment);
|
SharedAssignmentPointer deployableAssignment = _assignmentQueue.takeAt(sharedAssignment
|
||||||
|
- _assignmentQueue.begin());
|
||||||
|
|
||||||
// until we get a check-in from that GUID
|
// until we get a check-in from that GUID
|
||||||
// put assignment back in queue but stick it at the back so the others have a chance to go out
|
// put assignment back in queue but stick it at the back so the others have a chance to go out
|
||||||
_assignmentQueue.push_back(deployableAssignment);
|
_assignmentQueue.enqueue(deployableAssignment);
|
||||||
|
|
||||||
|
// stop looping, we've handed out an assignment
|
||||||
|
return deployableAssignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop looping, we've handed out an assignment
|
|
||||||
_assignmentQueueMutex.unlock();
|
|
||||||
return deployableAssignment;
|
|
||||||
} else {
|
} else {
|
||||||
// push forward the iterator to check the next assignment
|
// push forward the iterator to check the next assignment
|
||||||
assignment++;
|
++sharedAssignment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_assignmentQueueMutex.unlock();
|
return SharedAssignmentPointer();
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::removeAssignmentFromQueue(Assignment* removableAssignment) {
|
void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment) {
|
||||||
|
QQueue<SharedAssignmentPointer>::iterator potentialMatchingAssignment = _assignmentQueue.begin();
|
||||||
_assignmentQueueMutex.lock();
|
while (potentialMatchingAssignment != _assignmentQueue.end()) {
|
||||||
|
if (potentialMatchingAssignment->data()->getUUID() == removableAssignment->getUUID()) {
|
||||||
std::deque<Assignment*>::iterator assignment = _assignmentQueue.begin();
|
_assignmentQueue.erase(potentialMatchingAssignment);
|
||||||
|
|
||||||
while (assignment != _assignmentQueue.end()) {
|
// we matched and removed an assignment, bail out
|
||||||
if ((*assignment)->getUUID() == removableAssignment->getUUID()) {
|
|
||||||
_assignmentQueue.erase(assignment);
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// push forward the iterator to check the next assignment
|
++potentialMatchingAssignment;
|
||||||
assignment++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_assignmentQueueMutex.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DomainServer::checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
|
|
||||||
const HifiSockAddr& nodeLocalSocket,
|
|
||||||
const QUuid& checkInUUID) {
|
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
|
||||||
|
|
||||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
|
||||||
if (node->getLinkedData()
|
|
||||||
&& nodePublicSocket == node->getPublicSocket()
|
|
||||||
&& nodeLocalSocket == node->getLocalSocket()
|
|
||||||
&& node->getUUID() == checkInUUID) {
|
|
||||||
// this is a matching existing node if the public socket, local socket, and UUID match
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() {
|
void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() {
|
||||||
_hasCompletedRestartHold = true;
|
_hasCompletedRestartHold = true;
|
||||||
|
|
||||||
// if the domain-server has just restarted,
|
// if the domain-server has just restarted,
|
||||||
// check if there are static assignments in the file that we need to
|
// check if there are static assignments that we need to throw into the assignment queue
|
||||||
// throw into the assignment queue
|
QHash<QUuid, SharedAssignmentPointer> staticHashCopy = _staticAssignmentHash;
|
||||||
|
QHash<QUuid, SharedAssignmentPointer>::iterator staticAssignment = staticHashCopy.begin();
|
||||||
// pull anything in the static assignment file that isn't spoken for and add to the assignment queue
|
while (staticAssignment != staticHashCopy.end()) {
|
||||||
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
|
// add any of the un-matched static assignments to the queue
|
||||||
if (_staticAssignments[i].getUUID().isNull()) {
|
|
||||||
// reached the end of static assignments, bail
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool foundMatchingAssignment = false;
|
bool foundMatchingAssignment = false;
|
||||||
|
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
|
||||||
|
|
||||||
// enumerate the nodes and check if there is one with an attached assignment with matching UUID
|
// enumerate the nodes and check if there is one with an attached assignment with matching UUID
|
||||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||||
if (node->getLinkedData()) {
|
if (node->getUUID() == staticAssignment->data()->getUUID()) {
|
||||||
Assignment* linkedAssignment = (Assignment*) node->getLinkedData();
|
foundMatchingAssignment = true;
|
||||||
if (linkedAssignment->getUUID() == _staticAssignments[i].getUUID()) {
|
|
||||||
foundMatchingAssignment = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!foundMatchingAssignment) {
|
if (!foundMatchingAssignment) {
|
||||||
// this assignment has not been fulfilled - reset the UUID and add it to the assignment queue
|
// this assignment has not been fulfilled - reset the UUID and add it to the assignment queue
|
||||||
_staticAssignments[i].resetUUID();
|
refreshStaticAssignmentAndAddToQueue(*staticAssignment);
|
||||||
|
|
||||||
qDebug() << "Adding static assignment to queue -" << _staticAssignments[i];
|
|
||||||
|
|
||||||
_assignmentQueueMutex.lock();
|
|
||||||
_assignmentQueue.push_back(&_staticAssignments[i]);
|
|
||||||
_assignmentQueueMutex.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++staticAssignment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::cleanup() {
|
|
||||||
_staticAssignmentFile.unmap(_staticAssignmentFileData);
|
|
||||||
_staticAssignmentFile.close();
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,17 +9,16 @@
|
||||||
#ifndef __hifi__DomainServer__
|
#ifndef __hifi__DomainServer__
|
||||||
#define __hifi__DomainServer__
|
#define __hifi__DomainServer__
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QHash>
|
||||||
#include <QtCore/QMutex>
|
#include <QtCore/QQueue>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
#include <Assignment.h>
|
#include <Assignment.h>
|
||||||
#include <HTTPManager.h>
|
#include <HTTPManager.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
|
|
||||||
const int MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS = 1000;
|
typedef QSharedPointer<Assignment> SharedAssignmentPointer;
|
||||||
|
|
||||||
class DomainServer : public QCoreApplication, public HTTPRequestHandler {
|
class DomainServer : public QCoreApplication, public HTTPRequestHandler {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -35,37 +34,27 @@ public slots:
|
||||||
void nodeKilled(SharedNodePointer node);
|
void nodeKilled(SharedNodePointer node);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool readConfigFile(const char* path);
|
void parseCommandLineTypeConfigs(const QStringList& argumentList, QSet<Assignment::Type>& excludedTypes);
|
||||||
QString readServerAssignmentConfig(QJsonObject jsonObj, const char* nodeName);
|
void readConfigFile(const QString& path, QSet<Assignment::Type>& excludedTypes);
|
||||||
|
QString readServerAssignmentConfig(const QJsonObject& jsonObject, const QString& nodeName);
|
||||||
void prepopulateStaticAssignmentFile();
|
void addStaticAssignmentToAssignmentHash(Assignment* newAssignment);
|
||||||
Assignment* matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType);
|
void createStaticAssignmentsForTypeGivenConfigString(Assignment::Type type, const QString& configString);
|
||||||
Assignment* deployableAssignmentForRequest(Assignment& requestAssignment);
|
void populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes);
|
||||||
void removeAssignmentFromQueue(Assignment* removableAssignment);
|
|
||||||
bool checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
|
SharedAssignmentPointer matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType);
|
||||||
const HifiSockAddr& nodeLocalSocket,
|
SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment);
|
||||||
const QUuid& checkInUUI);
|
void removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment);
|
||||||
void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment);
|
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
|
||||||
|
|
||||||
HTTPManager _HTTPManager;
|
HTTPManager _HTTPManager;
|
||||||
|
|
||||||
QMutex _assignmentQueueMutex;
|
QHash<QUuid, SharedAssignmentPointer> _staticAssignmentHash;
|
||||||
std::deque<Assignment*> _assignmentQueue;
|
QQueue<SharedAssignmentPointer> _assignmentQueue;
|
||||||
|
|
||||||
QFile _staticAssignmentFile;
|
|
||||||
uchar* _staticAssignmentFileData;
|
|
||||||
|
|
||||||
Assignment* _staticAssignments;
|
|
||||||
|
|
||||||
const char* _voxelServerConfig;
|
|
||||||
const char* _particleServerConfig;
|
|
||||||
const char* _metavoxelServerConfig;
|
|
||||||
|
|
||||||
bool _hasCompletedRestartHold;
|
bool _hasCompletedRestartHold;
|
||||||
private slots:
|
private slots:
|
||||||
void readAvailableDatagrams();
|
void readAvailableDatagrams();
|
||||||
void addStaticAssignmentsBackToQueueAfterRestart();
|
void addStaticAssignmentsBackToQueueAfterRestart();
|
||||||
void cleanup();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__DomainServer__) */
|
#endif /* defined(__hifi__DomainServer__) */
|
||||||
|
|
|
@ -62,4 +62,4 @@ function maybePlaySound() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect a call back that happens every frame
|
// Connect a call back that happens every frame
|
||||||
Agent.willSendVisualDataCallback.connect(maybePlaySound);
|
Script.willSendVisualDataCallback.connect(maybePlaySound);
|
|
@ -132,7 +132,7 @@ function draw() {
|
||||||
print(scriptB);
|
print(scriptB);
|
||||||
numberParticlesAdded++;
|
numberParticlesAdded++;
|
||||||
} else {
|
} else {
|
||||||
Agent.stop();
|
Script.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
print("Particles Stats: " + Particles.getLifetimeInSeconds() + " seconds," +
|
print("Particles Stats: " + Particles.getLifetimeInSeconds() + " seconds," +
|
||||||
|
@ -150,5 +150,5 @@ function draw() {
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
print("here...\n");
|
print("here...\n");
|
||||||
Particles.setPacketsPerSecond(40000);
|
Particles.setPacketsPerSecond(40000);
|
||||||
Agent.willSendVisualDataCallback.connect(draw);
|
Script.willSendVisualDataCallback.connect(draw);
|
||||||
print("and here...\n");
|
print("and here...\n");
|
||||||
|
|
|
@ -48,6 +48,15 @@ function checkController() {
|
||||||
|
|
||||||
function keyPressEvent(event) {
|
function keyPressEvent(event) {
|
||||||
print("keyPressEvent event.key=" + event.key);
|
print("keyPressEvent event.key=" + event.key);
|
||||||
|
print("keyPressEvent event.text=" + event.text);
|
||||||
|
|
||||||
|
print("keyPressEvent event.isShifted=" + event.isShifted);
|
||||||
|
print("keyPressEvent event.isControl=" + event.isControl);
|
||||||
|
print("keyPressEvent event.isMeta=" + event.isMeta);
|
||||||
|
print("keyPressEvent event.isAlt=" + event.isAlt);
|
||||||
|
print("keyPressEvent event.isKeypad=" + event.isKeypad);
|
||||||
|
|
||||||
|
|
||||||
if (event.key == "A".charCodeAt(0)) {
|
if (event.key == "A".charCodeAt(0)) {
|
||||||
print("the A key was pressed");
|
print("the A key was pressed");
|
||||||
}
|
}
|
||||||
|
@ -73,18 +82,106 @@ function touchEndEvent(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
Agent.willSendVisualDataCallback.connect(checkController);
|
Script.willSendVisualDataCallback.connect(checkController);
|
||||||
|
|
||||||
// Map keyPress and mouse move events to our callbacks
|
// Map keyPress and mouse move events to our callbacks
|
||||||
Controller.keyPressEvent.connect(keyPressEvent);
|
Controller.keyPressEvent.connect(keyPressEvent);
|
||||||
var AKeyEvent = {
|
var KeyEvent_A = {
|
||||||
key: "A".charCodeAt(0),
|
key: "A".charCodeAt(0),
|
||||||
|
text: "A",
|
||||||
isShifted: false,
|
isShifted: false,
|
||||||
isMeta: false
|
isMeta: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var KeyEvent_a = {
|
||||||
|
text: "a",
|
||||||
|
isShifted: false,
|
||||||
|
isMeta: false
|
||||||
|
};
|
||||||
|
|
||||||
|
var KeyEvent_a2 = {
|
||||||
|
key: "a".charCodeAt(0),
|
||||||
|
isShifted: false,
|
||||||
|
isMeta: false
|
||||||
|
};
|
||||||
|
|
||||||
|
var KeyEvent_a3 = {
|
||||||
|
text: "a"
|
||||||
|
};
|
||||||
|
|
||||||
|
var KeyEvent_A2 = {
|
||||||
|
text: "A"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var KeyEvent_9 = {
|
||||||
|
text: "9"
|
||||||
|
};
|
||||||
|
|
||||||
|
var KeyEvent_Num = {
|
||||||
|
text: "#"
|
||||||
|
};
|
||||||
|
|
||||||
|
var KeyEvent_At = {
|
||||||
|
text: "@"
|
||||||
|
};
|
||||||
|
|
||||||
|
var KeyEvent_MetaAt = {
|
||||||
|
text: "@",
|
||||||
|
isMeta: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var KeyEvent_Up = {
|
||||||
|
text: "up"
|
||||||
|
};
|
||||||
|
var KeyEvent_Down = {
|
||||||
|
text: "down"
|
||||||
|
};
|
||||||
|
var KeyEvent_Left = {
|
||||||
|
text: "left"
|
||||||
|
};
|
||||||
|
var KeyEvent_Right = {
|
||||||
|
text: "right"
|
||||||
|
};
|
||||||
|
|
||||||
// prevent the A key from going through to the application
|
// prevent the A key from going through to the application
|
||||||
Controller.captureKeyEvents(AKeyEvent);
|
print("KeyEvent_A");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_A);
|
||||||
|
|
||||||
|
print("KeyEvent_A2");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_A2);
|
||||||
|
|
||||||
|
print("KeyEvent_a");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_a);
|
||||||
|
|
||||||
|
print("KeyEvent_a2");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_a2);
|
||||||
|
|
||||||
|
print("KeyEvent_a3");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_a3);
|
||||||
|
|
||||||
|
print("KeyEvent_9");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_9);
|
||||||
|
|
||||||
|
print("KeyEvent_Num");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_Num);
|
||||||
|
|
||||||
|
print("KeyEvent_At");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_At);
|
||||||
|
|
||||||
|
print("KeyEvent_MetaAt");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_MetaAt);
|
||||||
|
|
||||||
|
print("KeyEvent_Up");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_Up);
|
||||||
|
print("KeyEvent_Down");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_Down);
|
||||||
|
print("KeyEvent_Left");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_Left);
|
||||||
|
print("KeyEvent_Right");
|
||||||
|
Controller.captureKeyEvents(KeyEvent_Right);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||||
|
@ -102,4 +199,4 @@ function scriptEnding() {
|
||||||
Controller.releaseTouchEvents();
|
Controller.releaseTouchEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
Agent.scriptEnding.connect(scriptEnding);
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
|
|
@ -20,7 +20,7 @@ function scriptEnding() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
Agent.willSendVisualDataCallback.connect(displayCount);
|
Script.willSendVisualDataCallback.connect(displayCount);
|
||||||
|
|
||||||
// register our scriptEnding callback
|
// register our scriptEnding callback
|
||||||
Agent.scriptEnding.connect(scriptEnding);
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
|
|
@ -69,4 +69,4 @@ function checkSticks() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect a call back that happens every frame
|
// Connect a call back that happens every frame
|
||||||
Agent.willSendVisualDataCallback.connect(checkSticks);
|
Script.willSendVisualDataCallback.connect(checkSticks);
|
|
@ -44,7 +44,6 @@ var particleID = Particles.addParticle(originalProperties);
|
||||||
|
|
||||||
function moveParticle() {
|
function moveParticle() {
|
||||||
if (count >= moveUntil) {
|
if (count >= moveUntil) {
|
||||||
//Agent.stop();
|
|
||||||
|
|
||||||
// delete it...
|
// delete it...
|
||||||
if (count == moveUntil) {
|
if (count == moveUntil) {
|
||||||
|
@ -54,8 +53,8 @@ function moveParticle() {
|
||||||
|
|
||||||
// stop it...
|
// stop it...
|
||||||
if (count >= stopAfter) {
|
if (count >= stopAfter) {
|
||||||
print("calling Agent.stop()");
|
print("calling Script.stop()");
|
||||||
Agent.stop();
|
Script.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
|
@ -86,5 +85,5 @@ function moveParticle() {
|
||||||
|
|
||||||
|
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
Agent.willSendVisualDataCallback.connect(moveParticle);
|
Script.willSendVisualDataCallback.connect(moveParticle);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// editParticleExample.js
|
// findParticleExample.js
|
||||||
// hifi
|
// hifi
|
||||||
//
|
//
|
||||||
// Created by Brad Hefta-Gaub on 1/24/14.
|
// Created by Brad Hefta-Gaub on 1/24/14.
|
||||||
|
@ -65,8 +65,8 @@ function findParticles() {
|
||||||
// run for a while, then clean up
|
// run for a while, then clean up
|
||||||
// stop it...
|
// stop it...
|
||||||
if (iteration >= 100) {
|
if (iteration >= 100) {
|
||||||
print("calling Agent.stop()");
|
print("calling Script.stop()");
|
||||||
Agent.stop();
|
Script.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
print("--------------------------");
|
print("--------------------------");
|
||||||
|
@ -122,7 +122,7 @@ function findParticles() {
|
||||||
|
|
||||||
|
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
Agent.willSendVisualDataCallback.connect(findParticles);
|
Script.willSendVisualDataCallback.connect(findParticles);
|
||||||
|
|
||||||
// register our scriptEnding callback
|
// register our scriptEnding callback
|
||||||
Agent.scriptEnding.connect(scriptEnding);
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
|
|
@ -60,8 +60,8 @@ function makeFountain() {
|
||||||
totalParticles++;
|
totalParticles++;
|
||||||
}
|
}
|
||||||
if (totalParticles > 100) {
|
if (totalParticles > 100) {
|
||||||
Agent.stop();
|
Script.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
Agent.willSendVisualDataCallback.connect(makeFountain);
|
Script.willSendVisualDataCallback.connect(makeFountain);
|
133
examples/gameoflife.js
Normal file
133
examples/gameoflife.js
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
// Add your JavaScript for assignment below this line
|
||||||
|
|
||||||
|
// The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life)
|
||||||
|
|
||||||
|
var NUMBER_OF_CELLS_EACH_DIMENSION = 64;
|
||||||
|
var NUMBER_OF_CELLS = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION;
|
||||||
|
|
||||||
|
var currentCells = [];
|
||||||
|
var nextCells = [];
|
||||||
|
|
||||||
|
var METER_LENGTH = 1;
|
||||||
|
var cellScale = (NUMBER_OF_CELLS_EACH_DIMENSION * METER_LENGTH) / NUMBER_OF_CELLS_EACH_DIMENSION;
|
||||||
|
|
||||||
|
// randomly populate the cell start values
|
||||||
|
for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
|
||||||
|
// create the array to hold this row
|
||||||
|
currentCells[i] = [];
|
||||||
|
|
||||||
|
// create the array to hold this row in the nextCells array
|
||||||
|
nextCells[i] = [];
|
||||||
|
|
||||||
|
for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
|
||||||
|
currentCells[i][j] = Math.floor(Math.random() * 2);
|
||||||
|
|
||||||
|
// put the same value in the nextCells array for first board draw
|
||||||
|
nextCells[i][j] = currentCells[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNeighbourAlive(i, j) {
|
||||||
|
if (i < 0 || i >= NUMBER_OF_CELLS_EACH_DIMENSION
|
||||||
|
|| i < 0 || j >= NUMBER_OF_CELLS_EACH_DIMENSION) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return currentCells[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCells() {
|
||||||
|
var i = 0;
|
||||||
|
var j = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
|
||||||
|
for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
|
||||||
|
// figure out the number of live neighbours for the i-j cell
|
||||||
|
var liveNeighbours =
|
||||||
|
isNeighbourAlive(i + 1, j - 1) + isNeighbourAlive(i + 1, j) + isNeighbourAlive(i + 1, j + 1) +
|
||||||
|
isNeighbourAlive(i, j - 1) + isNeighbourAlive(i, j + 1) +
|
||||||
|
isNeighbourAlive(i - 1, j - 1) + isNeighbourAlive(i - 1, j) + isNeighbourAlive(i - 1, j + 1);
|
||||||
|
|
||||||
|
if (currentCells[i][j]) {
|
||||||
|
// live cell
|
||||||
|
|
||||||
|
if (liveNeighbours < 2) {
|
||||||
|
// rule #1 - under-population - this cell will die
|
||||||
|
// mark it zero to mark the change
|
||||||
|
nextCells[i][j] = 0;
|
||||||
|
} else if (liveNeighbours < 4) {
|
||||||
|
// rule #2 - this cell lives
|
||||||
|
// mark it -1 to mark no change
|
||||||
|
nextCells[i][j] = -1;
|
||||||
|
} else {
|
||||||
|
// rule #3 - overcrowding - this cell dies
|
||||||
|
// mark it zero to mark the change
|
||||||
|
nextCells[i][j] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// dead cell
|
||||||
|
if (liveNeighbours == 3) {
|
||||||
|
// rule #4 - reproduction - this cell revives
|
||||||
|
// mark it one to mark the change
|
||||||
|
nextCells[i][j] = 1;
|
||||||
|
} else {
|
||||||
|
// this cell stays dead
|
||||||
|
// mark it -1 for no change
|
||||||
|
nextCells[i][j] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math.random() < 0.001) {
|
||||||
|
// Random mutation to keep things interesting in there.
|
||||||
|
nextCells[i][j] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
|
||||||
|
for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
|
||||||
|
if (nextCells[i][j] != -1) {
|
||||||
|
// there has been a change to this cell, change the value in the currentCells array
|
||||||
|
currentCells[i][j] = nextCells[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendNextCells() {
|
||||||
|
for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
|
||||||
|
for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
|
||||||
|
if (nextCells[i][j] != -1) {
|
||||||
|
// there has been a change to the state of this cell, send it
|
||||||
|
|
||||||
|
// find the x and y position for this voxel, z = 0
|
||||||
|
var x = j * cellScale;
|
||||||
|
var y = i * cellScale;
|
||||||
|
|
||||||
|
// queue a packet to add a voxel for the new cell
|
||||||
|
var color = (nextCells[i][j] == 1) ? 255 : 1;
|
||||||
|
Voxels.setVoxel(x, y, 0, cellScale, color, color, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sentFirstBoard = false;
|
||||||
|
|
||||||
|
function step() {
|
||||||
|
print("step()...");
|
||||||
|
if (sentFirstBoard) {
|
||||||
|
// we've already sent the first full board, perform a step in time
|
||||||
|
updateCells();
|
||||||
|
} else {
|
||||||
|
// this will be our first board send
|
||||||
|
sentFirstBoard = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendNextCells();
|
||||||
|
}
|
||||||
|
|
||||||
|
print("here");
|
||||||
|
Script.willSendVisualDataCallback.connect(step);
|
||||||
|
Voxels.setPacketsPerSecond(200);
|
||||||
|
print("now here");
|
26
examples/globalCollisionsExample.js
Normal file
26
examples/globalCollisionsExample.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// globalCollisionsExample.js
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 1/29/14.
|
||||||
|
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// This is an example script that demonstrates use of the Controller class
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
function particleCollisionWithVoxel(particle, voxel) {
|
||||||
|
print("particleCollisionWithVoxel()..");
|
||||||
|
print(" particle.getID()=" + particle.id);
|
||||||
|
print(" voxel color...=" + voxel.red + ", " + voxel.green + ", " + voxel.blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function particleCollisionWithParticle(particleA, particleB) {
|
||||||
|
print("particleCollisionWithParticle()..");
|
||||||
|
print(" particleA.getID()=" + particleA.id);
|
||||||
|
print(" particleB.getID()=" + particleB.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Particles.particleCollisionWithVoxel.connect(particleCollisionWithVoxel);
|
||||||
|
Particles.particleCollisionWithParticle.connect(particleCollisionWithParticle);
|
|
@ -72,13 +72,13 @@ function checkController() {
|
||||||
" function collisionWithVoxel(voxel) { " +
|
" function collisionWithVoxel(voxel) { " +
|
||||||
" print('collisionWithVoxel(voxel)... '); " +
|
" print('collisionWithVoxel(voxel)... '); " +
|
||||||
" print('myID=' + Particle.getID() + '\\n'); " +
|
" print('myID=' + Particle.getID() + '\\n'); " +
|
||||||
" var voxelColor = voxel.getColor();" +
|
" var voxelColor = { red: voxel.red, green: voxel.green, blue: voxel.blue };" +
|
||||||
" print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " +
|
" var voxelAt = { x: voxel.x, y: voxel.y, z: voxel.z };" +
|
||||||
|
" var voxelScale = voxel.s;" +
|
||||||
|
" print('voxelColor=' + voxel.red + ', ' + voxel.green + ', ' + voxel.blue + '\\n'); " +
|
||||||
" var myColor = Particle.getColor();" +
|
" var myColor = Particle.getColor();" +
|
||||||
" print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " +
|
" print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " +
|
||||||
" Particle.setColor(voxelColor); " +
|
" Particle.setColor(voxelColor); " +
|
||||||
" var voxelAt = voxel.getPosition();" +
|
|
||||||
" var voxelScale = voxel.getScale();" +
|
|
||||||
" Voxels.eraseVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale); " +
|
" Voxels.eraseVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale); " +
|
||||||
" print('Voxels.eraseVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " +
|
" print('Voxels.eraseVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " +
|
||||||
" } " +
|
" } " +
|
||||||
|
@ -99,4 +99,4 @@ function checkController() {
|
||||||
|
|
||||||
|
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
Agent.willSendVisualDataCallback.connect(checkController);
|
Script.willSendVisualDataCallback.connect(checkController);
|
||||||
|
|
|
@ -70,5 +70,5 @@ MyAvatar.bodyPitch = 0;
|
||||||
MyAvatar.bodyRoll = 0;
|
MyAvatar.bodyRoll = 0;
|
||||||
|
|
||||||
// would be nice to change to update
|
// would be nice to change to update
|
||||||
Agent.willSendVisualDataCallback.connect(update);
|
Script.willSendVisualDataCallback.connect(update);
|
||||||
Agent.scriptEnding.connect(scriptEnding);
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
|
|
@ -41,4 +41,4 @@ function moveVoxel() {
|
||||||
|
|
||||||
Voxels.setPacketsPerSecond(300);
|
Voxels.setPacketsPerSecond(300);
|
||||||
// Connect a call back that happens every frame
|
// Connect a call back that happens every frame
|
||||||
Agent.willSendVisualDataCallback.connect(moveVoxel);
|
Script.willSendVisualDataCallback.connect(moveVoxel);
|
|
@ -65,13 +65,13 @@ function checkController() {
|
||||||
" function collisionWithVoxel(voxel) { " +
|
" function collisionWithVoxel(voxel) { " +
|
||||||
" print('collisionWithVoxel(voxel)... '); " +
|
" print('collisionWithVoxel(voxel)... '); " +
|
||||||
" print('myID=' + Particle.getID() + '\\n'); " +
|
" print('myID=' + Particle.getID() + '\\n'); " +
|
||||||
" var voxelColor = voxel.getColor();" +
|
" var voxelColor = { red: voxel.red, green: voxel.green, blue: voxel.blue };" +
|
||||||
|
" var voxelAt = { x: voxel.x, y: voxel.y, z: voxel.z };" +
|
||||||
|
" var voxelScale = voxel.s;" +
|
||||||
" print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " +
|
" print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " +
|
||||||
" var myColor = Particle.getColor();" +
|
" var myColor = Particle.getColor();" +
|
||||||
" print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " +
|
" print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " +
|
||||||
" Particle.setColor(voxelColor); " +
|
" Particle.setColor(voxelColor); " +
|
||||||
" var voxelAt = voxel.getPosition();" +
|
|
||||||
" var voxelScale = voxel.getScale();" +
|
|
||||||
" Voxels.setVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale, 255, 255, 0); " +
|
" Voxels.setVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale, 255, 255, 0); " +
|
||||||
" print('Voxels.setVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " +
|
" print('Voxels.setVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " +
|
||||||
" } " +
|
" } " +
|
||||||
|
@ -93,4 +93,4 @@ function checkController() {
|
||||||
|
|
||||||
|
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
Agent.willSendVisualDataCallback.connect(checkController);
|
Script.willSendVisualDataCallback.connect(checkController);
|
||||||
|
|
|
@ -95,7 +95,7 @@ function moveBird() {
|
||||||
var nowTimeInSeconds = new Date().getTime() / 1000;
|
var nowTimeInSeconds = new Date().getTime() / 1000;
|
||||||
if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) {
|
if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) {
|
||||||
print("our bird is dying, stop our script");
|
print("our bird is dying, stop our script");
|
||||||
Agent.stop();
|
Script.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,4 +171,4 @@ function moveBird() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
Agent.willSendVisualDataCallback.connect(moveBird);
|
Script.willSendVisualDataCallback.connect(moveBird);
|
||||||
|
|
|
@ -37,8 +37,8 @@ var ballParticleID = Particles.addParticle(ballProperties);
|
||||||
function endAfterAWhile() {
|
function endAfterAWhile() {
|
||||||
// stop it...
|
// stop it...
|
||||||
if (count >= stopAfter) {
|
if (count >= stopAfter) {
|
||||||
print("calling Agent.stop()");
|
print("calling Script.stop()");
|
||||||
Agent.stop();
|
Script.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
print("count =" + count);
|
print("count =" + count);
|
||||||
|
@ -47,5 +47,5 @@ function endAfterAWhile() {
|
||||||
|
|
||||||
|
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
Agent.willSendVisualDataCallback.connect(endAfterAWhile);
|
Script.willSendVisualDataCallback.connect(endAfterAWhile);
|
||||||
|
|
||||||
|
|
|
@ -17,4 +17,4 @@ function maybePlaySound() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect a call back that happens every frame
|
// Connect a call back that happens every frame
|
||||||
Agent.willSendVisualDataCallback.connect(maybePlaySound);
|
Script.willSendVisualDataCallback.connect(maybePlaySound);
|
50
examples/rideAlongWithAParticleExample.js
Normal file
50
examples/rideAlongWithAParticleExample.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
//
|
||||||
|
// rideAlongWithAParticleExample.js
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 1/24/14.
|
||||||
|
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// This is an example script that demonstrates "finding" particles
|
||||||
|
//
|
||||||
|
|
||||||
|
var iteration = 0;
|
||||||
|
var lengthOfRide = 2000; // in iterations
|
||||||
|
|
||||||
|
var particleA = Particles.addParticle(
|
||||||
|
{
|
||||||
|
position: { x: 10, y: 0, z: 10 },
|
||||||
|
velocity: { x: 5, y: 0, z: 5 },
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
radius : 0.1,
|
||||||
|
color: { red: 0, green: 255, blue: 0 },
|
||||||
|
damping: 0,
|
||||||
|
lifetime: (lengthOfRide * 60) + 1
|
||||||
|
});
|
||||||
|
|
||||||
|
function rideWithParticle() {
|
||||||
|
|
||||||
|
if (iteration <= lengthOfRide) {
|
||||||
|
|
||||||
|
// Check to see if we've been notified of the actual identity of the particles we created
|
||||||
|
if (!particleA.isKnownID) {
|
||||||
|
particleA = Particles.identifyParticle(particleA);
|
||||||
|
}
|
||||||
|
|
||||||
|
var propertiesA = Particles.getParticleProperties(particleA);
|
||||||
|
var newPosition = propertiesA.position;
|
||||||
|
MyAvatar.position = { x: propertiesA.position.x,
|
||||||
|
y: propertiesA.position.y + 2,
|
||||||
|
z: propertiesA.position.z };
|
||||||
|
} else {
|
||||||
|
Script.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
iteration++;
|
||||||
|
print("iteration="+iteration);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// register the call back so it fires before each data send
|
||||||
|
Script.willSendVisualDataCallback.connect(rideWithParticle);
|
||||||
|
|
|
@ -226,4 +226,4 @@ function checkController() {
|
||||||
|
|
||||||
|
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
Agent.willSendVisualDataCallback.connect(checkController);
|
Script.willSendVisualDataCallback.connect(checkController);
|
||||||
|
|
|
@ -130,4 +130,4 @@ function moveBird() {
|
||||||
|
|
||||||
Voxels.setPacketsPerSecond(10000);
|
Voxels.setPacketsPerSecond(10000);
|
||||||
// Connect a call back that happens every frame
|
// Connect a call back that happens every frame
|
||||||
Agent.willSendVisualDataCallback.connect(moveBird);
|
Script.willSendVisualDataCallback.connect(moveBird);
|
|
@ -117,11 +117,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
||||||
_frameCount(0),
|
_frameCount(0),
|
||||||
_fps(120.0f),
|
_fps(120.0f),
|
||||||
_justStarted(true),
|
_justStarted(true),
|
||||||
_voxelImporter(_window),
|
_voxelImporter(NULL),
|
||||||
_wantToKillLocalVoxels(false),
|
_wantToKillLocalVoxels(false),
|
||||||
_audioScope(256, 200, true),
|
_audioScope(256, 200, true),
|
||||||
_avatarManager(),
|
_myAvatar(),
|
||||||
_myAvatar(NULL),
|
|
||||||
_profile(QString()),
|
_profile(QString()),
|
||||||
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
|
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
|
||||||
_mouseX(0),
|
_mouseX(0),
|
||||||
|
@ -1090,7 +1089,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
_swatch.handleEvent(event->key(), Menu::getInstance()->isOptionChecked(MenuOption::VoxelGetColorMode));
|
_swatch.handleEvent(event->key(), Menu::getInstance()->isOptionChecked(MenuOption::VoxelGetColorMode));
|
||||||
break;
|
break;
|
||||||
case Qt::Key_At:
|
case Qt::Key_At:
|
||||||
Menu::getInstance()->goToUser();
|
Menu::getInstance()->goTo();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
event->ignore();
|
event->ignore();
|
||||||
|
@ -1410,10 +1409,10 @@ void Application::wheelEvent(QWheelEvent* event) {
|
||||||
|
|
||||||
void Application::sendPingPackets() {
|
void Application::sendPingPackets() {
|
||||||
QByteArray pingPacket = NodeList::getInstance()->constructPingPacket();
|
QByteArray pingPacket = NodeList::getInstance()->constructPingPacket();
|
||||||
getInstance()->controlledBroadcastToNodes(pingPacket, NodeSet() << NodeType::VoxelServer
|
controlledBroadcastToNodes(pingPacket, NodeSet() << NodeType::VoxelServer
|
||||||
<< NodeType::ParticleServer
|
<< NodeType::ParticleServer
|
||||||
<< NodeType::AudioMixer << NodeType::AvatarMixer
|
<< NodeType::AudioMixer << NodeType::AvatarMixer
|
||||||
<< NodeType::MetavoxelServer);
|
<< NodeType::MetavoxelServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every second, check the frame rates and other stuff
|
// Every second, check the frame rates and other stuff
|
||||||
|
@ -1670,7 +1669,12 @@ void Application::exportVoxels() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::importVoxels() {
|
void Application::importVoxels() {
|
||||||
if (_voxelImporter.exec()) {
|
if (!_voxelImporter) {
|
||||||
|
_voxelImporter = new VoxelImporter(_window);
|
||||||
|
_voxelImporter->init(_settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_voxelImporter->exec()) {
|
||||||
qDebug("[DEBUG] Import succeeded.");
|
qDebug("[DEBUG] Import succeeded.");
|
||||||
} else {
|
} else {
|
||||||
qDebug("[DEBUG] Import failed.");
|
qDebug("[DEBUG] Import failed.");
|
||||||
|
@ -1810,8 +1814,6 @@ void Application::init() {
|
||||||
_sharedVoxelSystem.changeTree(&_clipboard);
|
_sharedVoxelSystem.changeTree(&_clipboard);
|
||||||
delete tmpTree;
|
delete tmpTree;
|
||||||
|
|
||||||
_voxelImporter.init(_settings);
|
|
||||||
|
|
||||||
_environment.init();
|
_environment.init();
|
||||||
|
|
||||||
_glowEffect.init();
|
_glowEffect.init();
|
||||||
|
@ -1875,6 +1877,17 @@ void Application::init() {
|
||||||
|
|
||||||
_particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_avatarManager);
|
_particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_avatarManager);
|
||||||
|
|
||||||
|
// connect the _particleCollisionSystem to our script engine's ParticleScriptingInterface
|
||||||
|
connect(&_particleCollisionSystem,
|
||||||
|
SIGNAL(particleCollisionWithVoxel(const ParticleID&, const VoxelDetail&)),
|
||||||
|
ScriptEngine::getParticlesScriptingInterface(),
|
||||||
|
SLOT(forwardParticleCollisionWithVoxel(const ParticleID&, const VoxelDetail&)));
|
||||||
|
|
||||||
|
connect(&_particleCollisionSystem,
|
||||||
|
SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&)),
|
||||||
|
ScriptEngine::getParticlesScriptingInterface(),
|
||||||
|
SLOT(forwardParticleCollisionWithParticle(const ParticleID&, const ParticleID&)));
|
||||||
|
|
||||||
_palette.init(_glWidget->width(), _glWidget->height());
|
_palette.init(_glWidget->width(), _glWidget->height());
|
||||||
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0);
|
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0);
|
||||||
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1);
|
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1);
|
||||||
|
@ -3842,18 +3855,12 @@ void Application::setMenuShortcutsEnabled(bool enabled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::updateWindowTitle(){
|
void Application::updateWindowTitle(){
|
||||||
QString title = "";
|
|
||||||
|
|
||||||
QString buildVersion = " (build " + applicationVersion() + ")";
|
|
||||||
|
|
||||||
QString username = _profile.getUsername();
|
|
||||||
if(!username.isEmpty()){
|
|
||||||
title += username;
|
|
||||||
title += " @ ";
|
|
||||||
}
|
|
||||||
|
|
||||||
title += NodeList::getInstance()->getDomainHostname();
|
QString buildVersion = " (build " + applicationVersion() + ")";
|
||||||
title += buildVersion;
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
|
QString title = QString() + _profile.getUsername() + " " + nodeList->getOwnerUUID().toString()
|
||||||
|
+ " @ " + nodeList->getDomainHostname() + buildVersion;
|
||||||
|
|
||||||
qDebug("Application title set to: %s", title.toStdString().c_str());
|
qDebug("Application title set to: %s", title.toStdString().c_str());
|
||||||
_window->setWindowTitle(title);
|
_window->setWindowTitle(title);
|
||||||
|
|
|
@ -264,7 +264,7 @@ private:
|
||||||
void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true);
|
void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true);
|
||||||
|
|
||||||
static bool sendVoxelsOperation(OctreeElement* node, void* extraData);
|
static bool sendVoxelsOperation(OctreeElement* node, void* extraData);
|
||||||
static void sendPingPackets();
|
void sendPingPackets();
|
||||||
|
|
||||||
void initDisplay();
|
void initDisplay();
|
||||||
void init();
|
void init();
|
||||||
|
@ -353,7 +353,7 @@ private:
|
||||||
|
|
||||||
VoxelSystem _voxels;
|
VoxelSystem _voxels;
|
||||||
VoxelTree _clipboard; // if I copy/paste
|
VoxelTree _clipboard; // if I copy/paste
|
||||||
VoxelImporter _voxelImporter;
|
VoxelImporter* _voxelImporter;
|
||||||
VoxelSystem _sharedVoxelSystem;
|
VoxelSystem _sharedVoxelSystem;
|
||||||
ViewFrustum _sharedVoxelSystemViewFrustum;
|
ViewFrustum _sharedVoxelSystemViewFrustum;
|
||||||
|
|
||||||
|
|
|
@ -539,7 +539,7 @@ void Audio::toggleMute() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::render(int screenWidth, int screenHeight) {
|
void Audio::render(int screenWidth, int screenHeight) {
|
||||||
if (_audioInput) {
|
if (_audioInput && _audioOutput) {
|
||||||
glLineWidth(2.0);
|
glLineWidth(2.0);
|
||||||
glBegin(GL_LINES);
|
glBegin(GL_LINES);
|
||||||
glColor3f(.93f, .93f, .93f);
|
glColor3f(.93f, .93f, .93f);
|
||||||
|
|
|
@ -231,10 +231,6 @@ void ImportDialog::setLayout() {
|
||||||
widget = findChild<QWidget*>("treeView");
|
widget = findChild<QWidget*>("treeView");
|
||||||
widget->setAttribute(Qt::WA_MacShowFocusRect, false);
|
widget->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||||
|
|
||||||
// remove reference to treeView
|
|
||||||
widget = NULL;
|
|
||||||
widget->deleteLater();
|
|
||||||
|
|
||||||
switchToResourcesParentIfRequired();
|
switchToResourcesParentIfRequired();
|
||||||
QFile styleSheet("resources/styles/import_dialog.qss");
|
QFile styleSheet("resources/styles/import_dialog.qss");
|
||||||
if (styleSheet.open(QIODevice::ReadOnly)) {
|
if (styleSheet.open(QIODevice::ReadOnly)) {
|
||||||
|
|
|
@ -116,10 +116,10 @@ Menu::Menu() :
|
||||||
this,
|
this,
|
||||||
SLOT(goToLocation()));
|
SLOT(goToLocation()));
|
||||||
addActionToQMenuAndActionHash(fileMenu,
|
addActionToQMenuAndActionHash(fileMenu,
|
||||||
MenuOption::GoToUser,
|
MenuOption::GoTo,
|
||||||
Qt::Key_At,
|
Qt::Key_At,
|
||||||
this,
|
this,
|
||||||
SLOT(goToUser()));
|
SLOT(goTo()));
|
||||||
|
|
||||||
|
|
||||||
addDisabledActionAndSeparator(fileMenu, "Settings");
|
addDisabledActionAndSeparator(fileMenu, "Settings");
|
||||||
|
@ -910,6 +910,60 @@ void Menu::goToDomain() {
|
||||||
sendFakeEnterEvent();
|
sendFakeEnterEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Menu::goTo() {
|
||||||
|
|
||||||
|
QInputDialog gotoDialog(Application::getInstance()->getWindow());
|
||||||
|
gotoDialog.setWindowTitle("Go to");
|
||||||
|
gotoDialog.setLabelText("Destination:");
|
||||||
|
QString destination = Application::getInstance()->getProfile()->getUsername();
|
||||||
|
gotoDialog.setTextValue(destination);
|
||||||
|
gotoDialog.setWindowFlags(Qt::Sheet);
|
||||||
|
gotoDialog.resize(gotoDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, gotoDialog.size().height());
|
||||||
|
|
||||||
|
int dialogReturn = gotoDialog.exec();
|
||||||
|
if (dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty()) {
|
||||||
|
|
||||||
|
destination = gotoDialog.textValue();
|
||||||
|
|
||||||
|
QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts);
|
||||||
|
|
||||||
|
const int NUMBER_OF_COORDINATE_ITEMS = 3;
|
||||||
|
const int X_ITEM = 0;
|
||||||
|
const int Y_ITEM = 1;
|
||||||
|
const int Z_ITEM = 2;
|
||||||
|
if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) {
|
||||||
|
|
||||||
|
double x = replaceLastOccurrence('-', '.', coordinateItems[X_ITEM].trimmed()).toDouble();
|
||||||
|
double y = replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM].trimmed()).toDouble();
|
||||||
|
double z = replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM].trimmed()).toDouble();
|
||||||
|
|
||||||
|
glm::vec3 newAvatarPos(x, y, z);
|
||||||
|
|
||||||
|
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||||
|
glm::vec3 avatarPos = myAvatar->getPosition();
|
||||||
|
if (newAvatarPos != avatarPos) {
|
||||||
|
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||||
|
MyAvatar::sendKillAvatar();
|
||||||
|
|
||||||
|
qDebug("Going To Location: %f, %f, %f...", x, y, z);
|
||||||
|
myAvatar->setPosition(newAvatarPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// there's a username entered by the user, make a request to the data-server
|
||||||
|
DataServerClient::getValuesForKeysAndUserString(
|
||||||
|
QStringList()
|
||||||
|
<< DataServerKey::Domain
|
||||||
|
<< DataServerKey::Position
|
||||||
|
<< DataServerKey::Orientation,
|
||||||
|
destination, Application::getInstance()->getProfile());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendFakeEnterEvent();
|
||||||
|
}
|
||||||
|
|
||||||
void Menu::goToLocation() {
|
void Menu::goToLocation() {
|
||||||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||||
glm::vec3 avatarPos = myAvatar->getPosition();
|
glm::vec3 avatarPos = myAvatar->getPosition();
|
||||||
|
@ -954,26 +1008,6 @@ void Menu::goToLocation() {
|
||||||
sendFakeEnterEvent();
|
sendFakeEnterEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::goToUser() {
|
|
||||||
QInputDialog userDialog(Application::getInstance()->getWindow());
|
|
||||||
userDialog.setWindowTitle("Go to User");
|
|
||||||
userDialog.setLabelText("Destination user:");
|
|
||||||
QString username = Application::getInstance()->getProfile()->getUsername();
|
|
||||||
userDialog.setTextValue(username);
|
|
||||||
userDialog.setWindowFlags(Qt::Sheet);
|
|
||||||
userDialog.resize(userDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, userDialog.size().height());
|
|
||||||
|
|
||||||
int dialogReturn = userDialog.exec();
|
|
||||||
if (dialogReturn == QDialog::Accepted && !userDialog.textValue().isEmpty()) {
|
|
||||||
// there's a username entered by the user, make a request to the data-server
|
|
||||||
DataServerClient::getValuesForKeysAndUserString(
|
|
||||||
QStringList() << DataServerKey::Domain << DataServerKey::Position << DataServerKey::Orientation,
|
|
||||||
userDialog.textValue(), Application::getInstance()->getProfile());
|
|
||||||
}
|
|
||||||
|
|
||||||
sendFakeEnterEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Menu::pasteToVoxel() {
|
void Menu::pasteToVoxel() {
|
||||||
QInputDialog pasteToOctalCodeDialog(Application::getInstance()->getWindow());
|
QInputDialog pasteToOctalCodeDialog(Application::getInstance()->getWindow());
|
||||||
pasteToOctalCodeDialog.setWindowTitle("Paste to Voxel");
|
pasteToOctalCodeDialog.setWindowTitle("Paste to Voxel");
|
||||||
|
@ -1137,3 +1171,14 @@ void Menu::updateFrustumRenderModeAction() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Menu::replaceLastOccurrence(QChar search, QChar replace, QString string) {
|
||||||
|
int lastIndex;
|
||||||
|
lastIndex = string.lastIndexOf(search);
|
||||||
|
if (lastIndex > 0) {
|
||||||
|
lastIndex = string.lastIndexOf(search);
|
||||||
|
string.replace(lastIndex, 1, replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ public slots:
|
||||||
void saveSettings(QSettings* settings = NULL);
|
void saveSettings(QSettings* settings = NULL);
|
||||||
void importSettings();
|
void importSettings();
|
||||||
void exportSettings();
|
void exportSettings();
|
||||||
void goToUser();
|
void goTo();
|
||||||
void pasteToVoxel();
|
void pasteToVoxel();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
@ -152,6 +152,7 @@ private:
|
||||||
QAction* _useVoxelShader;
|
QAction* _useVoxelShader;
|
||||||
int _maxVoxelPacketsPerSecond;
|
int _maxVoxelPacketsPerSecond;
|
||||||
QMenu* _activeScriptsMenu;
|
QMenu* _activeScriptsMenu;
|
||||||
|
QString replaceLastOccurrence(QChar search, QChar replace, QString string);
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace MenuOption {
|
namespace MenuOption {
|
||||||
|
@ -209,7 +210,7 @@ namespace MenuOption {
|
||||||
const QString GlowMode = "Cycle Glow Mode";
|
const QString GlowMode = "Cycle Glow Mode";
|
||||||
const QString GoToDomain = "Go To Domain...";
|
const QString GoToDomain = "Go To Domain...";
|
||||||
const QString GoToLocation = "Go To Location...";
|
const QString GoToLocation = "Go To Location...";
|
||||||
const QString GoToUser = "Go To User...";
|
const QString GoTo = "Go To...";
|
||||||
const QString ImportVoxels = "Import Voxels";
|
const QString ImportVoxels = "Import Voxels";
|
||||||
const QString ImportVoxelsClipboard = "Import Voxels to Clipboard";
|
const QString ImportVoxelsClipboard = "Import Voxels to Clipboard";
|
||||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||||
|
|
|
@ -24,15 +24,15 @@ private:
|
||||||
const QString SETTINGS_GROUP_NAME = "VoxelImport";
|
const QString SETTINGS_GROUP_NAME = "VoxelImport";
|
||||||
const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings";
|
const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings";
|
||||||
|
|
||||||
VoxelImporter::VoxelImporter(QWidget* parent)
|
VoxelImporter::VoxelImporter(QWidget* parent) :
|
||||||
: QObject(parent),
|
QObject(parent),
|
||||||
_voxelTree(true),
|
_voxelTree(true),
|
||||||
_importDialog(parent),
|
_importDialog(parent),
|
||||||
_currentTask(NULL),
|
_currentTask(NULL),
|
||||||
_nextTask(NULL) {
|
_nextTask(NULL)
|
||||||
|
{
|
||||||
connect(&_importDialog, SIGNAL(currentChanged(QString)), SLOT(preImport()));
|
connect(&_importDialog, &QFileDialog::currentChanged, this, &VoxelImporter::preImport);
|
||||||
connect(&_importDialog, SIGNAL(accepted()), SLOT(import()));
|
connect(&_importDialog, &QFileDialog::accepted, this, &VoxelImporter::import);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelImporter::saveSettings(QSettings* settings) {
|
void VoxelImporter::saveSettings(QSettings* settings) {
|
||||||
|
|
|
@ -162,13 +162,13 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const
|
||||||
int bytesRead = numBytesForPacketHeader(datagram);
|
int bytesRead = numBytesForPacketHeader(datagram);
|
||||||
|
|
||||||
QByteArray dummyAvatarByteArray = byteArrayWithPopluatedHeader(PacketTypeAvatarData);
|
QByteArray dummyAvatarByteArray = byteArrayWithPopluatedHeader(PacketTypeAvatarData);
|
||||||
int numDummyByteArrayHeaderBytes = dummyAvatarByteArray.size();
|
int numDummyHeaderBytes = dummyAvatarByteArray.size();
|
||||||
|
int numDummyHeaderBytesWithoutUUID = numDummyHeaderBytes - NUM_BYTES_RFC4122_UUID;
|
||||||
|
|
||||||
// enumerate over all of the avatars in this packet
|
// enumerate over all of the avatars in this packet
|
||||||
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
|
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
|
||||||
while (bytesRead < datagram.size() && mixerWeakPointer.data()) {
|
while (bytesRead < datagram.size() && mixerWeakPointer.data()) {
|
||||||
QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID));
|
QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID));
|
||||||
// TODO: skip the data if nodeUUID is same as MY_AVATAR_KEY
|
|
||||||
|
|
||||||
AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID);
|
AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID);
|
||||||
|
|
||||||
|
@ -189,15 +189,13 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy the rest of the packet to the avatarData holder so we can read the next Avatar from there
|
// copy the rest of the packet to the avatarData holder so we can read the next Avatar from there
|
||||||
dummyAvatarByteArray.resize(numDummyByteArrayHeaderBytes);
|
dummyAvatarByteArray.resize(numDummyHeaderBytesWithoutUUID);
|
||||||
|
|
||||||
// make this Avatar's UUID the UUID in the packet and tack the remaining data onto the end
|
// make this Avatar's UUID the UUID in the packet and tack the remaining data onto the end
|
||||||
dummyAvatarByteArray.replace(numDummyByteArrayHeaderBytes - NUM_BYTES_RFC4122_UUID,
|
dummyAvatarByteArray.append(datagram.mid(bytesRead));
|
||||||
NUM_BYTES_RFC4122_UUID + datagram.size() - bytesRead,
|
|
||||||
datagram.mid(bytesRead));
|
|
||||||
|
|
||||||
// have the matching (or new) avatar parse the data from the packet
|
// have the matching (or new) avatar parse the data from the packet
|
||||||
bytesRead += matchingAvatar->parseData(dummyAvatarByteArray);
|
bytesRead += matchingAvatar->parseData(dummyAvatarByteArray) - numDummyHeaderBytesWithoutUUID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,10 @@ void Profile::setUUID(const QUuid& uuid) {
|
||||||
|
|
||||||
// when the UUID is changed we need set it appropriately on the NodeList instance
|
// when the UUID is changed we need set it appropriately on the NodeList instance
|
||||||
NodeList::getInstance()->setOwnerUUID(uuid);
|
NodeList::getInstance()->setOwnerUUID(uuid);
|
||||||
}
|
|
||||||
|
// ask for a window title update so the new UUID is presented
|
||||||
|
Application::getInstance()->updateWindowTitle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profile::setFaceModelURL(const QUrl& faceModelURL) {
|
void Profile::setFaceModelURL(const QUrl& faceModelURL) {
|
||||||
|
|
|
@ -170,6 +170,7 @@ public:
|
||||||
const QByteArray& getDatum() const { return _datum; }
|
const QByteArray& getDatum() const { return _datum; }
|
||||||
|
|
||||||
void pushBackToken(int token) { _pushedBackToken = token; }
|
void pushBackToken(int token) { _pushedBackToken = token; }
|
||||||
|
void ungetChar(char ch) { _device->ungetChar(ch); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -221,7 +222,7 @@ int Tokenizer::nextToken() {
|
||||||
_datum.append(ch);
|
_datum.append(ch);
|
||||||
while (_device->getChar(&ch)) {
|
while (_device->getChar(&ch)) {
|
||||||
if (QChar(ch).isSpace() || ch == ';' || ch == ':' || ch == '{' || ch == '}' || ch == ',' || ch == '\"') {
|
if (QChar(ch).isSpace() || ch == ';' || ch == ':' || ch == '{' || ch == '}' || ch == ',' || ch == '\"') {
|
||||||
_device->ungetChar(ch); // read until we encounter a special character, then replace it
|
ungetChar(ch); // read until we encounter a special character, then replace it
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_datum.append(ch);
|
_datum.append(ch);
|
||||||
|
@ -257,9 +258,17 @@ FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
|
||||||
expectingDatum = true;
|
expectingDatum = true;
|
||||||
|
|
||||||
} else if (token == Tokenizer::DATUM_TOKEN && expectingDatum) {
|
} else if (token == Tokenizer::DATUM_TOKEN && expectingDatum) {
|
||||||
node.properties.append(tokenizer.getDatum());
|
QByteArray datum = tokenizer.getDatum();
|
||||||
expectingDatum = false;
|
if ((token = tokenizer.nextToken()) == ':') {
|
||||||
|
tokenizer.ungetChar(':');
|
||||||
|
tokenizer.pushBackToken(Tokenizer::DATUM_TOKEN);
|
||||||
|
return node;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tokenizer.pushBackToken(token);
|
||||||
|
node.properties.append(datum);
|
||||||
|
expectingDatum = false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tokenizer.pushBackToken(token);
|
tokenizer.pushBackToken(token);
|
||||||
return node;
|
return node;
|
||||||
|
@ -377,6 +386,9 @@ glm::mat4 createMat4(const QVector<double>& doubleVector) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<int> getIntVector(const QVariantList& properties, int index) {
|
QVector<int> getIntVector(const QVariantList& properties, int index) {
|
||||||
|
if (index >= properties.size()) {
|
||||||
|
return QVector<int>();
|
||||||
|
}
|
||||||
QVector<int> vector = properties.at(index).value<QVector<int> >();
|
QVector<int> vector = properties.at(index).value<QVector<int> >();
|
||||||
if (!vector.isEmpty()) {
|
if (!vector.isEmpty()) {
|
||||||
return vector;
|
return vector;
|
||||||
|
@ -388,6 +400,9 @@ QVector<int> getIntVector(const QVariantList& properties, int index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<double> getDoubleVector(const QVariantList& properties, int index) {
|
QVector<double> getDoubleVector(const QVariantList& properties, int index) {
|
||||||
|
if (index >= properties.size()) {
|
||||||
|
return QVector<double>();
|
||||||
|
}
|
||||||
QVector<double> vector = properties.at(index).value<QVector<double> >();
|
QVector<double> vector = properties.at(index).value<QVector<double> >();
|
||||||
if (!vector.isEmpty()) {
|
if (!vector.isEmpty()) {
|
||||||
return vector;
|
return vector;
|
||||||
|
@ -1106,6 +1121,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
QVector<QString> modelIDs;
|
QVector<QString> modelIDs;
|
||||||
QSet<QString> remainingModels;
|
QSet<QString> remainingModels;
|
||||||
for (QHash<QString, FBXModel>::const_iterator model = models.constBegin(); model != models.constEnd(); model++) {
|
for (QHash<QString, FBXModel>::const_iterator model = models.constBegin(); model != models.constEnd(); model++) {
|
||||||
|
// make sure the parent is in the child map
|
||||||
|
QString parent = parentMap.value(model.key());
|
||||||
|
if (!childMap.contains(parent, model.key())) {
|
||||||
|
childMap.insert(parent, model.key());
|
||||||
|
}
|
||||||
remainingModels.insert(model.key());
|
remainingModels.insert(model.key());
|
||||||
}
|
}
|
||||||
while (!remainingModels.isEmpty()) {
|
while (!remainingModels.isEmpty()) {
|
||||||
|
|
|
@ -172,105 +172,102 @@ int AvatarData::parseData(const QByteArray& packet) {
|
||||||
_handData = new HandData(this);
|
_handData = new HandData(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
QDataStream packetStream(packet);
|
// increment to push past the packet header
|
||||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(packet.data());
|
||||||
|
const unsigned char* sourceBuffer = startPosition + numBytesForPacketHeader(packet);
|
||||||
|
|
||||||
packetStream.readRawData(reinterpret_cast<char*>(&_position), sizeof(_position));
|
// Body world position
|
||||||
|
memcpy(&_position, sourceBuffer, sizeof(float) * 3);
|
||||||
|
sourceBuffer += sizeof(float) * 3;
|
||||||
|
|
||||||
// Body rotation (NOTE: This needs to become a quaternion to save two bytes)
|
// Body rotation (NOTE: This needs to become a quaternion to save two bytes)
|
||||||
uint16_t twoByteHolder;
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyYaw);
|
||||||
packetStream >> twoByteHolder;
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyPitch);
|
||||||
unpackFloatAngleFromTwoByte(&twoByteHolder, &_bodyYaw);
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyRoll);
|
||||||
|
|
||||||
packetStream >> twoByteHolder;
|
// Body scale
|
||||||
unpackFloatAngleFromTwoByte(&twoByteHolder, &_bodyPitch);
|
sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, _targetScale);
|
||||||
|
|
||||||
packetStream >> twoByteHolder;
|
|
||||||
unpackFloatAngleFromTwoByte(&twoByteHolder, &_bodyRoll);
|
|
||||||
|
|
||||||
// body scale
|
|
||||||
packetStream >> twoByteHolder;
|
|
||||||
unpackFloatRatioFromTwoByte(reinterpret_cast<const unsigned char*>(&twoByteHolder), _targetScale);
|
|
||||||
|
|
||||||
// Head rotation (NOTE: This needs to become a quaternion to save two bytes)
|
// Head rotation (NOTE: This needs to become a quaternion to save two bytes)
|
||||||
float headYaw, headPitch, headRoll;
|
float headYaw, headPitch, headRoll;
|
||||||
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw);
|
||||||
packetStream >> twoByteHolder;
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headPitch);
|
||||||
unpackFloatAngleFromTwoByte(&twoByteHolder, &headYaw);
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headRoll);
|
||||||
|
|
||||||
packetStream >> twoByteHolder;
|
|
||||||
unpackFloatAngleFromTwoByte(&twoByteHolder, &headPitch);
|
|
||||||
|
|
||||||
packetStream >> twoByteHolder;
|
|
||||||
unpackFloatAngleFromTwoByte(&twoByteHolder, &headRoll);
|
|
||||||
|
|
||||||
_headData->setYaw(headYaw);
|
_headData->setYaw(headYaw);
|
||||||
_headData->setPitch(headPitch);
|
_headData->setPitch(headPitch);
|
||||||
_headData->setRoll(headRoll);
|
_headData->setRoll(headRoll);
|
||||||
|
|
||||||
// Head position relative to pelvis
|
// Head position relative to pelvis
|
||||||
packetStream >> _headData->_leanSideways;
|
memcpy(&_headData->_leanSideways, sourceBuffer, sizeof(_headData->_leanSideways));
|
||||||
packetStream >> _headData->_leanForward;
|
sourceBuffer += sizeof(float);
|
||||||
|
memcpy(&_headData->_leanForward, sourceBuffer, sizeof(_headData->_leanForward));
|
||||||
|
sourceBuffer += sizeof(_headData->_leanForward);
|
||||||
|
|
||||||
// Hand Position - is relative to body position
|
// Hand Position - is relative to body position
|
||||||
glm::vec3 handPositionRelative;
|
glm::vec3 handPositionRelative;
|
||||||
packetStream.readRawData(reinterpret_cast<char*>(&handPositionRelative), sizeof(handPositionRelative));
|
memcpy(&handPositionRelative, sourceBuffer, sizeof(float) * 3);
|
||||||
_handPosition = _position + handPositionRelative;
|
_handPosition = _position + handPositionRelative;
|
||||||
|
sourceBuffer += sizeof(float) * 3;
|
||||||
|
|
||||||
|
// Lookat Position
|
||||||
|
memcpy(&_headData->_lookAtPosition, sourceBuffer, sizeof(_headData->_lookAtPosition));
|
||||||
|
sourceBuffer += sizeof(_headData->_lookAtPosition);
|
||||||
|
|
||||||
packetStream.readRawData(reinterpret_cast<char*>(&_headData->_lookAtPosition), sizeof(_headData->_lookAtPosition));
|
|
||||||
|
|
||||||
// Instantaneous audio loudness (used to drive facial animation)
|
// Instantaneous audio loudness (used to drive facial animation)
|
||||||
//sourceBuffer += unpackFloatFromByte(sourceBuffer, _audioLoudness, MAX_AUDIO_LOUDNESS);
|
//sourceBuffer += unpackFloatFromByte(sourceBuffer, _audioLoudness, MAX_AUDIO_LOUDNESS);
|
||||||
packetStream >> _headData->_audioLoudness;
|
memcpy(&_headData->_audioLoudness, sourceBuffer, sizeof(float));
|
||||||
|
sourceBuffer += sizeof(float);
|
||||||
// the rest is a chat message
|
|
||||||
|
// the rest is a chat message
|
||||||
|
int chatMessageSize = *sourceBuffer++;
|
||||||
|
_chatMessage = string((char*)sourceBuffer, chatMessageSize);
|
||||||
|
sourceBuffer += chatMessageSize * sizeof(char);
|
||||||
|
|
||||||
quint8 chatMessageSize;
|
|
||||||
packetStream >> chatMessageSize;
|
|
||||||
_chatMessage = string(packet.data() + packetStream.device()->pos(), chatMessageSize);
|
|
||||||
packetStream.skipRawData(chatMessageSize);
|
|
||||||
|
|
||||||
// voxel sending features...
|
// voxel sending features...
|
||||||
unsigned char bitItems = 0;
|
unsigned char bitItems = 0;
|
||||||
packetStream >> bitItems;
|
bitItems = (unsigned char)*sourceBuffer++;
|
||||||
|
|
||||||
// key state, stored as a semi-nibble in the bitItems
|
// key state, stored as a semi-nibble in the bitItems
|
||||||
_keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT);
|
_keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT);
|
||||||
|
|
||||||
// hand state, stored as a semi-nibble in the bitItems
|
// hand state, stored as a semi-nibble in the bitItems
|
||||||
_handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT);
|
_handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT);
|
||||||
|
|
||||||
_headData->_isFaceshiftConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED);
|
_headData->_isFaceshiftConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED);
|
||||||
|
|
||||||
_isChatCirclingEnabled = oneAtBit(bitItems, IS_CHAT_CIRCLING_ENABLED);
|
_isChatCirclingEnabled = oneAtBit(bitItems, IS_CHAT_CIRCLING_ENABLED);
|
||||||
|
|
||||||
// If it is connected, pack up the data
|
// If it is connected, pack up the data
|
||||||
if (_headData->_isFaceshiftConnected) {
|
if (_headData->_isFaceshiftConnected) {
|
||||||
packetStream >> _headData->_leftEyeBlink;
|
memcpy(&_headData->_leftEyeBlink, sourceBuffer, sizeof(float));
|
||||||
packetStream >> _headData->_rightEyeBlink;
|
sourceBuffer += sizeof(float);
|
||||||
packetStream >> _headData->_averageLoudness;
|
|
||||||
packetStream >> _headData->_browAudioLift;
|
|
||||||
|
|
||||||
quint8 numBlendshapeCoefficients;
|
memcpy(&_headData->_rightEyeBlink, sourceBuffer, sizeof(float));
|
||||||
packetStream >> numBlendshapeCoefficients;
|
sourceBuffer += sizeof(float);
|
||||||
|
|
||||||
_headData->_blendshapeCoefficients.resize(numBlendshapeCoefficients);
|
memcpy(&_headData->_averageLoudness, sourceBuffer, sizeof(float));
|
||||||
packetStream.readRawData(reinterpret_cast<char*>(_headData->_blendshapeCoefficients.data()),
|
sourceBuffer += sizeof(float);
|
||||||
numBlendshapeCoefficients * sizeof(float));
|
|
||||||
|
memcpy(&_headData->_browAudioLift, sourceBuffer, sizeof(float));
|
||||||
|
sourceBuffer += sizeof(float);
|
||||||
|
|
||||||
|
_headData->_blendshapeCoefficients.resize(*sourceBuffer++);
|
||||||
|
memcpy(_headData->_blendshapeCoefficients.data(), sourceBuffer,
|
||||||
|
_headData->_blendshapeCoefficients.size() * sizeof(float));
|
||||||
|
sourceBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pupil dilation
|
// pupil dilation
|
||||||
quint8 pupilByte;
|
sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f);
|
||||||
packetStream >> pupilByte;
|
|
||||||
unpackFloatFromByte(&pupilByte, _headData->_pupilDilation, 1.0f);
|
|
||||||
|
|
||||||
// leap hand data
|
// leap hand data
|
||||||
if (packetStream.device()->pos() < packet.size()) {
|
if (sourceBuffer - startPosition < packet.size()) {
|
||||||
// check passed, bytes match
|
// check passed, bytes match
|
||||||
packetStream.skipRawData(_handData->decodeRemoteData(packet.mid(packetStream.device()->pos())));
|
sourceBuffer += _handData->decodeRemoteData(packet.mid(sourceBuffer - startPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
return packetStream.device()->pos();
|
return sourceBuffer - startPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarData::setClampedTargetScale(float targetScale) {
|
void AvatarData::setClampedTargetScale(float targetScale) {
|
||||||
|
|
|
@ -160,31 +160,21 @@ int HandData::encodeRemoteData(unsigned char* destinationBuffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
|
int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
|
||||||
QDataStream packetStream(dataByteArray);
|
const unsigned char* startPosition;
|
||||||
|
const unsigned char* sourceBuffer = startPosition = reinterpret_cast<const unsigned char*>(dataByteArray.data());
|
||||||
quint8 numHands;
|
unsigned int numHands = *sourceBuffer++;
|
||||||
packetStream >> numHands;
|
|
||||||
|
|
||||||
for (unsigned int handIndex = 0; handIndex < numHands; ++handIndex) {
|
for (unsigned int handIndex = 0; handIndex < numHands; ++handIndex) {
|
||||||
if (handIndex >= getNumPalms())
|
if (handIndex >= getNumPalms())
|
||||||
addNewPalm();
|
addNewPalm();
|
||||||
PalmData& palm = getPalms()[handIndex];
|
PalmData& palm = getPalms()[handIndex];
|
||||||
|
|
||||||
glm::vec3 handPosition;
|
glm::vec3 handPosition;
|
||||||
glm::vec3 handNormal;
|
glm::vec3 handNormal;
|
||||||
qint16 twoByteHolder;
|
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, handPosition, fingerVectorRadix);
|
||||||
|
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, handNormal, fingerVectorRadix);
|
||||||
|
unsigned int numFingers = *sourceBuffer++;
|
||||||
|
|
||||||
packetStream >> twoByteHolder;
|
|
||||||
unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast<const unsigned char*>(&twoByteHolder),
|
|
||||||
handPosition, fingerVectorRadix);
|
|
||||||
|
|
||||||
packetStream >> twoByteHolder;
|
|
||||||
unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast<const unsigned char*>(&twoByteHolder),
|
|
||||||
handNormal, fingerVectorRadix);
|
|
||||||
|
|
||||||
quint8 numFingers;
|
|
||||||
packetStream >> numFingers;
|
|
||||||
|
|
||||||
palm.setRawPosition(handPosition);
|
palm.setRawPosition(handPosition);
|
||||||
palm.setRawNormal(handNormal);
|
palm.setRawNormal(handNormal);
|
||||||
palm.setActive(true);
|
palm.setActive(true);
|
||||||
|
@ -195,18 +185,12 @@ int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
|
||||||
for (unsigned int fingerIndex = 0; fingerIndex < numFingers; ++fingerIndex) {
|
for (unsigned int fingerIndex = 0; fingerIndex < numFingers; ++fingerIndex) {
|
||||||
if (fingerIndex < palm.getNumFingers()) {
|
if (fingerIndex < palm.getNumFingers()) {
|
||||||
FingerData& finger = palm.getFingers()[fingerIndex];
|
FingerData& finger = palm.getFingers()[fingerIndex];
|
||||||
|
|
||||||
glm::vec3 tipPosition;
|
glm::vec3 tipPosition;
|
||||||
glm::vec3 rootPosition;
|
glm::vec3 rootPosition;
|
||||||
|
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, tipPosition, fingerVectorRadix);
|
||||||
|
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, rootPosition, fingerVectorRadix);
|
||||||
|
|
||||||
packetStream >> twoByteHolder;
|
|
||||||
unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast<const unsigned char*>(&twoByteHolder), tipPosition,
|
|
||||||
fingerVectorRadix);
|
|
||||||
|
|
||||||
packetStream >> twoByteHolder;
|
|
||||||
unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast<const unsigned char*>(&twoByteHolder), rootPosition,
|
|
||||||
fingerVectorRadix);
|
|
||||||
|
|
||||||
finger.setRawTipPosition(tipPosition);
|
finger.setRawTipPosition(tipPosition);
|
||||||
finger.setRawRootPosition(rootPosition);
|
finger.setRawRootPosition(rootPosition);
|
||||||
finger.setActive(true);
|
finger.setActive(true);
|
||||||
|
@ -225,14 +209,11 @@ int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// One byte for error checking safety.
|
// One byte for error checking safety.
|
||||||
unsigned char requiredLength = packetStream.device()->pos();
|
unsigned char requiredLength = (unsigned char)(sourceBuffer - startPosition);
|
||||||
|
unsigned char checkLength = *sourceBuffer++;
|
||||||
unsigned char checkLength;
|
|
||||||
packetStream >> checkLength;
|
|
||||||
|
|
||||||
assert(checkLength == requiredLength);
|
assert(checkLength == requiredLength);
|
||||||
|
|
||||||
return packetStream.device()->pos();
|
return sourceBuffer - startPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandData::setFingerTrailLength(unsigned int length) {
|
void HandData::setFingerTrailLength(unsigned int length) {
|
||||||
|
|
|
@ -63,7 +63,7 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p
|
||||||
|
|
||||||
QFile localFile(filePath);
|
QFile localFile(filePath);
|
||||||
localFile.open(QIODevice::ReadOnly);
|
localFile.open(QIODevice::ReadOnly);
|
||||||
QString localFileString(localFile.readAll());
|
QByteArray localFileData = localFile.readAll();
|
||||||
|
|
||||||
QFileInfo localFileInfo(filePath);
|
QFileInfo localFileInfo(filePath);
|
||||||
|
|
||||||
|
@ -77,6 +77,7 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p
|
||||||
|
|
||||||
int matchPosition = 0;
|
int matchPosition = 0;
|
||||||
|
|
||||||
|
QString localFileString(localFileData);
|
||||||
|
|
||||||
while ((matchPosition = includeRegExp.indexIn(localFileString, matchPosition)) != -1) {
|
while ((matchPosition = includeRegExp.indexIn(localFileString, matchPosition)) != -1) {
|
||||||
// check if this is a file or vitual include
|
// check if this is a file or vitual include
|
||||||
|
@ -105,9 +106,11 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p
|
||||||
// push the match position forward so we can check the next match
|
// push the match position forward so we can check the next match
|
||||||
matchPosition += includeRegExp.matchedLength();
|
matchPosition += includeRegExp.matchedLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
localFileData = localFileString.toLocal8Bit();
|
||||||
}
|
}
|
||||||
|
|
||||||
connection->respond(HTTPConnection::StatusCode200, localFileString.toLocal8Bit(),
|
connection->respond(HTTPConnection::StatusCode200, localFileData,
|
||||||
qPrintable(mimeDatabase.mimeTypeForFile(filePath).name()));
|
qPrintable(mimeDatabase.mimeTypeForFile(filePath).name()));
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
|
|
@ -433,7 +433,7 @@ void OctreeServer::setArguments(int argc, char** argv) {
|
||||||
void OctreeServer::parsePayload() {
|
void OctreeServer::parsePayload() {
|
||||||
|
|
||||||
if (getPayload().size() > 0) {
|
if (getPayload().size() > 0) {
|
||||||
QString config((const char*) _payload);
|
QString config(_payload);
|
||||||
|
|
||||||
// Now, parse the config
|
// Now, parse the config
|
||||||
QStringList configList = config.split(" ");
|
QStringList configList = config.split(" ");
|
||||||
|
|
|
@ -45,8 +45,7 @@ bool JurisdictionListener::queueJurisdictionRequest() {
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||||
if (nodeList->getNodeActiveSocketOrPing(node.data()) &&
|
if (nodeList->getNodeActiveSocketOrPing(node.data()) && node->getType() == getNodeType()) {
|
||||||
node->getType() == getNodeType()) {
|
|
||||||
const HifiSockAddr* nodeAddress = node->getActiveSocket();
|
const HifiSockAddr* nodeAddress = node->getActiveSocket();
|
||||||
_packetSender.queuePacketForSending(*nodeAddress, QByteArray(reinterpret_cast<char*>(bufferOut), sizeOut));
|
_packetSender.queuePacketForSending(*nodeAddress, QByteArray(reinterpret_cast<char*>(bufferOut), sizeOut));
|
||||||
nodeCount++;
|
nodeCount++;
|
||||||
|
|
|
@ -64,7 +64,7 @@ bool JurisdictionSender::process() {
|
||||||
_nodesRequestingJurisdictions.pop();
|
_nodesRequestingJurisdictions.pop();
|
||||||
SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(nodeUUID);
|
SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(nodeUUID);
|
||||||
|
|
||||||
if (node->getActiveSocket() != NULL) {
|
if (node && node->getActiveSocket() != NULL) {
|
||||||
const HifiSockAddr* nodeAddress = node->getActiveSocket();
|
const HifiSockAddr* nodeAddress = node->getActiveSocket();
|
||||||
_packetSender.queuePacketForSending(*nodeAddress, QByteArray(reinterpret_cast<char *>(bufferOut), sizeOut));
|
_packetSender.queuePacketForSending(*nodeAddress, QByteArray(reinterpret_cast<char *>(bufferOut), sizeOut));
|
||||||
nodeCount++;
|
nodeCount++;
|
||||||
|
|
|
@ -24,7 +24,6 @@ static const float fingerVectorRadix = 4; // bits of precision when converting f
|
||||||
|
|
||||||
OctreeQuery::OctreeQuery() :
|
OctreeQuery::OctreeQuery() :
|
||||||
NodeData(),
|
NodeData(),
|
||||||
_uuid(),
|
|
||||||
_cameraPosition(0,0,0),
|
_cameraPosition(0,0,0),
|
||||||
_cameraOrientation(),
|
_cameraOrientation(),
|
||||||
_cameraFov(0.0f),
|
_cameraFov(0.0f),
|
||||||
|
@ -53,11 +52,6 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
|
||||||
// that can pack any type given the number of bytes
|
// that can pack any type given the number of bytes
|
||||||
// and return the number of bytes to push the pointer
|
// and return the number of bytes to push the pointer
|
||||||
|
|
||||||
// UUID
|
|
||||||
QByteArray uuidByteArray = _uuid.toRfc4122();
|
|
||||||
memcpy(destinationBuffer, uuidByteArray.constData(), uuidByteArray.size());
|
|
||||||
destinationBuffer += uuidByteArray.size();
|
|
||||||
|
|
||||||
// camera details
|
// camera details
|
||||||
memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition));
|
memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition));
|
||||||
destinationBuffer += sizeof(_cameraPosition);
|
destinationBuffer += sizeof(_cameraPosition);
|
||||||
|
@ -103,13 +97,6 @@ int OctreeQuery::parseData(const QByteArray& packet) {
|
||||||
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(packet.data());
|
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(packet.data());
|
||||||
const unsigned char* sourceBuffer = startPosition + numBytesPacketHeader;
|
const unsigned char* sourceBuffer = startPosition + numBytesPacketHeader;
|
||||||
|
|
||||||
// push past the node session UUID
|
|
||||||
sourceBuffer += NUM_BYTES_RFC4122_UUID;
|
|
||||||
|
|
||||||
// user UUID
|
|
||||||
_uuid = QUuid::fromRfc4122(QByteArray((char*) sourceBuffer, NUM_BYTES_RFC4122_UUID));
|
|
||||||
sourceBuffer += NUM_BYTES_RFC4122_UUID;
|
|
||||||
|
|
||||||
// camera details
|
// camera details
|
||||||
memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));
|
memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));
|
||||||
sourceBuffer += sizeof(_cameraPosition);
|
sourceBuffer += sizeof(_cameraPosition);
|
||||||
|
|
|
@ -57,9 +57,6 @@ public:
|
||||||
int getBroadcastData(unsigned char* destinationBuffer);
|
int getBroadcastData(unsigned char* destinationBuffer);
|
||||||
int parseData(const QByteArray& packet);
|
int parseData(const QByteArray& packet);
|
||||||
|
|
||||||
QUuid& getUUID() { return _uuid; }
|
|
||||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
|
||||||
|
|
||||||
// getters for camera details
|
// getters for camera details
|
||||||
const glm::vec3& getCameraPosition() const { return _cameraPosition; }
|
const glm::vec3& getCameraPosition() const { return _cameraPosition; }
|
||||||
const glm::quat& getCameraOrientation() const { return _cameraOrientation; }
|
const glm::quat& getCameraOrientation() const { return _cameraOrientation; }
|
||||||
|
@ -101,8 +98,6 @@ public slots:
|
||||||
void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
|
void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QUuid _uuid;
|
|
||||||
|
|
||||||
// camera details for the avatar
|
// camera details for the avatar
|
||||||
glm::vec3 _cameraPosition;
|
glm::vec3 _cameraPosition;
|
||||||
glm::quat _cameraOrientation;
|
glm::quat _cameraOrientation;
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
#include <SharedUtil.h> // usecTimestampNow()
|
#include <SharedUtil.h> // usecTimestampNow()
|
||||||
#include <VoxelsScriptingInterface.h>
|
#include <VoxelsScriptingInterface.h>
|
||||||
|
#include <VoxelDetail.h>
|
||||||
|
|
||||||
|
|
||||||
// This is not ideal, but adding script-engine as a linked library, will cause a circular reference
|
// This is not ideal, but adding script-engine as a linked library, will cause a circular reference
|
||||||
// I'm open to other potential solutions. Could we change cmake to allow libraries to reference each others
|
// I'm open to other potential solutions. Could we change cmake to allow libraries to reference each others
|
||||||
|
@ -830,7 +832,7 @@ void Particle::update(const quint64& now) {
|
||||||
bool shouldDie = (getAge() > getLifetime()) || getShouldDie() || (!isInHand && isStopped && isReallyOld);
|
bool shouldDie = (getAge() > getLifetime()) || getShouldDie() || (!isInHand && isStopped && isReallyOld);
|
||||||
setShouldDie(shouldDie);
|
setShouldDie(shouldDie);
|
||||||
|
|
||||||
runUpdateScript(); // allow the javascript to alter our state
|
executeUpdateScripts(); // allow the javascript to alter our state
|
||||||
|
|
||||||
// If the ball is in hand, it doesn't move or have gravity effect it
|
// If the ball is in hand, it doesn't move or have gravity effect it
|
||||||
if (!isInHand) {
|
if (!isInHand) {
|
||||||
|
@ -852,106 +854,62 @@ void Particle::update(const quint64& now) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Particle::runUpdateScript() {
|
void Particle::startParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable) {
|
||||||
|
if (_voxelEditSender) {
|
||||||
|
engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender);
|
||||||
|
}
|
||||||
|
if (_particleEditSender) {
|
||||||
|
engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the "this" Particle object
|
||||||
|
engine.registerGlobalObject("Particle", &particleScriptable);
|
||||||
|
engine.evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Particle::endParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable) {
|
||||||
|
if (_voxelEditSender) {
|
||||||
|
_voxelEditSender->releaseQueuedMessages();
|
||||||
|
}
|
||||||
|
if (_particleEditSender) {
|
||||||
|
_particleEditSender->releaseQueuedMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Particle::executeUpdateScripts() {
|
||||||
|
// Only run this particle script if there's a script attached directly to the particle.
|
||||||
if (!_script.isEmpty()) {
|
if (!_script.isEmpty()) {
|
||||||
ScriptEngine engine(_script); // no menu or controller interface...
|
ScriptEngine engine(_script);
|
||||||
|
|
||||||
if (_voxelEditSender) {
|
|
||||||
engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender);
|
|
||||||
}
|
|
||||||
if (_particleEditSender) {
|
|
||||||
engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the Particle object
|
|
||||||
ParticleScriptObject particleScriptable(this);
|
ParticleScriptObject particleScriptable(this);
|
||||||
engine.registerGlobalObject("Particle", &particleScriptable);
|
startParticleScriptContext(engine, particleScriptable);
|
||||||
|
|
||||||
// init and evaluate the script, but return so we can emit the collision
|
|
||||||
engine.evaluate();
|
|
||||||
|
|
||||||
particleScriptable.emitUpdate();
|
particleScriptable.emitUpdate();
|
||||||
|
endParticleScriptContext(engine, particleScriptable);
|
||||||
// it seems like we may need to send out particle edits if the state of our particle was changed.
|
|
||||||
|
|
||||||
if (_voxelEditSender) {
|
|
||||||
_voxelEditSender->releaseQueuedMessages();
|
|
||||||
}
|
|
||||||
if (_particleEditSender) {
|
|
||||||
_particleEditSender->releaseQueuedMessages();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Particle::collisionWithParticle(Particle* other) {
|
void Particle::collisionWithParticle(Particle* other) {
|
||||||
|
// Only run this particle script if there's a script attached directly to the particle.
|
||||||
if (!_script.isEmpty()) {
|
if (!_script.isEmpty()) {
|
||||||
ScriptEngine engine(_script); // no menu or controller interface...
|
ScriptEngine engine(_script);
|
||||||
|
|
||||||
if (_voxelEditSender) {
|
|
||||||
engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender);
|
|
||||||
}
|
|
||||||
if (_particleEditSender) {
|
|
||||||
engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the Particle object
|
|
||||||
ParticleScriptObject particleScriptable(this);
|
ParticleScriptObject particleScriptable(this);
|
||||||
engine.registerGlobalObject("Particle", &particleScriptable);
|
startParticleScriptContext(engine, particleScriptable);
|
||||||
|
|
||||||
// init and evaluate the script, but return so we can emit the collision
|
|
||||||
engine.evaluate();
|
|
||||||
|
|
||||||
ParticleScriptObject otherParticleScriptable(other);
|
ParticleScriptObject otherParticleScriptable(other);
|
||||||
particleScriptable.emitCollisionWithParticle(&otherParticleScriptable);
|
particleScriptable.emitCollisionWithParticle(&otherParticleScriptable);
|
||||||
|
endParticleScriptContext(engine, particleScriptable);
|
||||||
// it seems like we may need to send out particle edits if the state of our particle was changed.
|
|
||||||
|
|
||||||
if (_voxelEditSender) {
|
|
||||||
_voxelEditSender->releaseQueuedMessages();
|
|
||||||
}
|
|
||||||
if (_particleEditSender) {
|
|
||||||
_particleEditSender->releaseQueuedMessages();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Particle::collisionWithVoxel(VoxelDetail* voxelDetails) {
|
void Particle::collisionWithVoxel(VoxelDetail* voxelDetails) {
|
||||||
|
// Only run this particle script if there's a script attached directly to the particle.
|
||||||
if (!_script.isEmpty()) {
|
if (!_script.isEmpty()) {
|
||||||
|
ScriptEngine engine(_script);
|
||||||
ScriptEngine engine(_script); // no menu or controller interface...
|
|
||||||
|
|
||||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
|
||||||
// we can use the same ones as our context.
|
|
||||||
if (_voxelEditSender) {
|
|
||||||
engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender);
|
|
||||||
}
|
|
||||||
if (_particleEditSender) {
|
|
||||||
engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the Particle object
|
|
||||||
ParticleScriptObject particleScriptable(this);
|
ParticleScriptObject particleScriptable(this);
|
||||||
engine.registerGlobalObject("Particle", &particleScriptable);
|
startParticleScriptContext(engine, particleScriptable);
|
||||||
|
particleScriptable.emitCollisionWithVoxel(*voxelDetails);
|
||||||
// init and evaluate the script, but return so we can emit the collision
|
endParticleScriptContext(engine, particleScriptable);
|
||||||
engine.evaluate();
|
|
||||||
|
|
||||||
VoxelDetailScriptObject voxelDetailsScriptable(voxelDetails);
|
|
||||||
particleScriptable.emitCollisionWithVoxel(&voxelDetailsScriptable);
|
|
||||||
|
|
||||||
// it seems like we may need to send out particle edits if the state of our particle was changed.
|
|
||||||
|
|
||||||
if (_voxelEditSender) {
|
|
||||||
_voxelEditSender->releaseQueuedMessages();
|
|
||||||
}
|
|
||||||
if (_particleEditSender) {
|
|
||||||
_particleEditSender->releaseQueuedMessages();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Particle::setAge(float age) {
|
void Particle::setAge(float age) {
|
||||||
quint64 ageInUsecs = age * USECS_PER_SECOND;
|
quint64 ageInUsecs = age * USECS_PER_SECOND;
|
||||||
_created = usecTimestampNow() - ageInUsecs;
|
_created = usecTimestampNow() - ageInUsecs;
|
||||||
|
|
|
@ -20,13 +20,16 @@
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <OctreePacketData.h>
|
#include <OctreePacketData.h>
|
||||||
|
|
||||||
class VoxelsScriptingInterface;
|
class Particle;
|
||||||
class ParticlesScriptingInterface;
|
|
||||||
class VoxelEditPacketSender;
|
|
||||||
class ParticleEditPacketSender;
|
class ParticleEditPacketSender;
|
||||||
class ParticleProperties;
|
class ParticleProperties;
|
||||||
class Particle;
|
class ParticlesScriptingInterface;
|
||||||
|
class ParticleScriptObject;
|
||||||
class ParticleTree;
|
class ParticleTree;
|
||||||
|
class ScriptEngine;
|
||||||
|
class VoxelEditPacketSender;
|
||||||
|
class VoxelsScriptingInterface;
|
||||||
|
struct VoxelDetail;
|
||||||
|
|
||||||
const uint32_t NEW_PARTICLE = 0xFFFFFFFF;
|
const uint32_t NEW_PARTICLE = 0xFFFFFFFF;
|
||||||
const uint32_t UNKNOWN_TOKEN = 0xFFFFFFFF;
|
const uint32_t UNKNOWN_TOKEN = 0xFFFFFFFF;
|
||||||
|
@ -227,7 +230,8 @@ public:
|
||||||
const glm::vec3& getModelTranslation() const { return _modelTranslation; }
|
const glm::vec3& getModelTranslation() const { return _modelTranslation; }
|
||||||
const glm::quat& getModelRotation() const { return _modelRotation; }
|
const glm::quat& getModelRotation() const { return _modelRotation; }
|
||||||
float getModelScale() const { return _modelScale; }
|
float getModelScale() const { return _modelScale; }
|
||||||
|
|
||||||
|
ParticleID getParticleID() const { return ParticleID(getID(), getCreatorTokenID(), getID() != UNKNOWN_PARTICLE_ID); }
|
||||||
ParticleProperties getProperties() const;
|
ParticleProperties getProperties() const;
|
||||||
|
|
||||||
/// The last updated/simulated time of this particle from the time perspective of the authoritative server/source
|
/// The last updated/simulated time of this particle from the time perspective of the authoritative server/source
|
||||||
|
@ -318,11 +322,9 @@ protected:
|
||||||
static VoxelEditPacketSender* _voxelEditSender;
|
static VoxelEditPacketSender* _voxelEditSender;
|
||||||
static ParticleEditPacketSender* _particleEditSender;
|
static ParticleEditPacketSender* _particleEditSender;
|
||||||
|
|
||||||
void runUpdateScript();
|
void startParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable);
|
||||||
static QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3);
|
void endParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable);
|
||||||
static void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3);
|
void executeUpdateScripts();
|
||||||
static QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color);
|
|
||||||
static void xColorFromScriptValue(const QScriptValue &object, xColor& color);
|
|
||||||
|
|
||||||
void setAge(float age);
|
void setAge(float age);
|
||||||
|
|
||||||
|
@ -366,10 +368,11 @@ class ParticleScriptObject : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ParticleScriptObject(Particle* particle) { _particle = particle; }
|
ParticleScriptObject(Particle* particle) { _particle = particle; }
|
||||||
|
//~ParticleScriptObject() { qDebug() << "~ParticleScriptObject() this=" << this; }
|
||||||
|
|
||||||
void emitUpdate() { emit update(); }
|
void emitUpdate() { emit update(); }
|
||||||
void emitCollisionWithParticle(QObject* other) { emit collisionWithParticle(other); }
|
void emitCollisionWithParticle(QObject* other) { emit collisionWithParticle(other); }
|
||||||
void emitCollisionWithVoxel(QObject* voxel) { emit collisionWithVoxel(voxel); }
|
void emitCollisionWithVoxel(const VoxelDetail& voxel) { emit collisionWithVoxel(voxel); }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
unsigned int getID() const { return _particle->getID(); }
|
unsigned int getID() const { return _particle->getID(); }
|
||||||
|
@ -414,7 +417,7 @@ public slots:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void update();
|
void update();
|
||||||
void collisionWithVoxel(QObject* voxel);
|
void collisionWithVoxel(const VoxelDetail& voxel);
|
||||||
void collisionWithParticle(QObject* other);
|
void collisionWithParticle(QObject* other);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -68,6 +68,17 @@ void ParticleCollisionSystem::checkParticle(Particle* particle) {
|
||||||
updateCollisionWithAvatars(particle);
|
updateCollisionWithAvatars(particle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParticleCollisionSystem::emitGlobalParicleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails) {
|
||||||
|
ParticleID particleID = particle->getParticleID();
|
||||||
|
emit particleCollisionWithVoxel(particleID, *voxelDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleCollisionSystem::emitGlobalParicleCollisionWithParticle(Particle* particleA, Particle* particleB) {
|
||||||
|
ParticleID idA = particleA->getParticleID();
|
||||||
|
ParticleID idB = particleB->getParticleID();
|
||||||
|
emit particleCollisionWithParticle(idA, idB);
|
||||||
|
}
|
||||||
|
|
||||||
void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) {
|
void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) {
|
||||||
glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE);
|
glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE);
|
||||||
float radius = particle->getRadius() * (float)(TREE_SCALE);
|
float radius = particle->getRadius() * (float)(TREE_SCALE);
|
||||||
|
@ -83,6 +94,9 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) {
|
||||||
// let the particles run their collision scripts if they have them
|
// let the particles run their collision scripts if they have them
|
||||||
particle->collisionWithVoxel(voxelDetails);
|
particle->collisionWithVoxel(voxelDetails);
|
||||||
|
|
||||||
|
// let the global script run their collision scripts for particles if they have them
|
||||||
|
emitGlobalParicleCollisionWithVoxel(particle, voxelDetails);
|
||||||
|
|
||||||
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
|
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
|
||||||
collisionInfo._penetration /= (float)(TREE_SCALE);
|
collisionInfo._penetration /= (float)(TREE_SCALE);
|
||||||
particle->applyHardCollision(collisionInfo);
|
particle->applyHardCollision(collisionInfo);
|
||||||
|
@ -110,6 +124,7 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
|
||||||
if (glm::dot(relativeVelocity, penetration) > 0.0f) {
|
if (glm::dot(relativeVelocity, penetration) > 0.0f) {
|
||||||
particleA->collisionWithParticle(particleB);
|
particleA->collisionWithParticle(particleB);
|
||||||
particleB->collisionWithParticle(particleA);
|
particleB->collisionWithParticle(particleA);
|
||||||
|
emitGlobalParicleCollisionWithParticle(particleA, particleB);
|
||||||
|
|
||||||
glm::vec3 axis = glm::normalize(penetration);
|
glm::vec3 axis = glm::normalize(penetration);
|
||||||
glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis;
|
glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis;
|
||||||
|
|
|
@ -31,7 +31,8 @@ class VoxelTree;
|
||||||
|
|
||||||
const glm::vec3 NO_ADDED_VELOCITY = glm::vec3(0);
|
const glm::vec3 NO_ADDED_VELOCITY = glm::vec3(0);
|
||||||
|
|
||||||
class ParticleCollisionSystem {
|
class ParticleCollisionSystem : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ParticleCollisionSystem(ParticleEditPacketSender* packetSender = NULL, ParticleTree* particles = NULL,
|
ParticleCollisionSystem(ParticleEditPacketSender* packetSender = NULL, ParticleTree* particles = NULL,
|
||||||
VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL,
|
VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL,
|
||||||
|
@ -51,9 +52,14 @@ public:
|
||||||
void queueParticlePropertiesUpdate(Particle* particle);
|
void queueParticlePropertiesUpdate(Particle* particle);
|
||||||
void updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency);
|
void updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void particleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel);
|
||||||
|
void particleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool updateOperation(OctreeElement* element, void* extraData);
|
static bool updateOperation(OctreeElement* element, void* extraData);
|
||||||
|
void emitGlobalParicleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails);
|
||||||
|
void emitGlobalParicleCollisionWithParticle(Particle* particleA, Particle* particleB);
|
||||||
|
|
||||||
ParticleEditPacketSender* _packetSender;
|
ParticleEditPacketSender* _packetSender;
|
||||||
ParticleTree* _particles;
|
ParticleTree* _particles;
|
||||||
|
|
|
@ -190,7 +190,6 @@ void ParticleTree::handleAddParticleResponse(const QByteArray& packet) {
|
||||||
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
||||||
|
|
||||||
const unsigned char* dataAt = reinterpret_cast<const unsigned char*>(packet.data()) + numBytesPacketHeader;
|
const unsigned char* dataAt = reinterpret_cast<const unsigned char*>(packet.data()) + numBytesPacketHeader;
|
||||||
dataAt += numBytesPacketHeader;
|
|
||||||
|
|
||||||
uint32_t creatorTokenID;
|
uint32_t creatorTokenID;
|
||||||
memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID));
|
memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID));
|
||||||
|
|
|
@ -53,7 +53,19 @@ public slots:
|
||||||
/// finds particles within the search sphere specified by the center point and radius
|
/// finds particles within the search sphere specified by the center point and radius
|
||||||
/// this function will not find any particles in script engine contexts which don't have access to particles
|
/// this function will not find any particles in script engine contexts which don't have access to particles
|
||||||
QVector<ParticleID> findParticles(const glm::vec3& center, float radius) const;
|
QVector<ParticleID> findParticles(const glm::vec3& center, float radius) const;
|
||||||
|
|
||||||
|
/// inbound slots for external collision systems
|
||||||
|
void forwardParticleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel) {
|
||||||
|
emit particleCollisionWithVoxel(particleID, voxel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardParticleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB) {
|
||||||
|
emit particleCollisionWithParticle(idA, idB);
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void particleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel);
|
||||||
|
void particleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void queueParticleMessage(PacketType packetType, ParticleID particleID, const ParticleProperties& properties);
|
void queueParticleMessage(PacketType packetType, ParticleID particleID, const ParticleProperties& properties);
|
||||||
|
|
|
@ -8,22 +8,72 @@
|
||||||
// Used to register meta-types with Qt for very various event types so that they can be exposed to our
|
// Used to register meta-types with Qt for very various event types so that they can be exposed to our
|
||||||
// scripting engine
|
// scripting engine
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
#include "EventTypes.h"
|
#include "EventTypes.h"
|
||||||
|
|
||||||
|
|
||||||
KeyEvent::KeyEvent() {
|
KeyEvent::KeyEvent() {
|
||||||
key = 0;
|
key = 0;
|
||||||
|
text = QString("");
|
||||||
isShifted = false;
|
isShifted = false;
|
||||||
isMeta = false;
|
isMeta = false;
|
||||||
|
isControl = false;
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
KeyEvent::KeyEvent(const QKeyEvent& event) {
|
KeyEvent::KeyEvent(const QKeyEvent& event) {
|
||||||
key = event.key();
|
key = event.key();
|
||||||
|
text = event.text();
|
||||||
isShifted = event.modifiers().testFlag(Qt::ShiftModifier);
|
isShifted = event.modifiers().testFlag(Qt::ShiftModifier);
|
||||||
isMeta = event.modifiers().testFlag(Qt::ControlModifier);
|
isMeta = event.modifiers().testFlag(Qt::MetaModifier);
|
||||||
|
isControl = event.modifiers().testFlag(Qt::ControlModifier);
|
||||||
|
isAlt = event.modifiers().testFlag(Qt::AltModifier);
|
||||||
|
isKeypad = event.modifiers().testFlag(Qt::KeypadModifier);
|
||||||
isValid = true;
|
isValid = true;
|
||||||
|
|
||||||
|
// handle special text for special characters...
|
||||||
|
if (key == Qt::Key_F1) {
|
||||||
|
text = "F1";
|
||||||
|
} else if (key == Qt::Key_F2) {
|
||||||
|
text = "F2";
|
||||||
|
} else if (key == Qt::Key_F3) {
|
||||||
|
text = "F3";
|
||||||
|
} else if (key == Qt::Key_F4) {
|
||||||
|
text = "F4";
|
||||||
|
} else if (key == Qt::Key_F5) {
|
||||||
|
text = "F5";
|
||||||
|
} else if (key == Qt::Key_F6) {
|
||||||
|
text = "F6";
|
||||||
|
} else if (key == Qt::Key_F7) {
|
||||||
|
text = "F7";
|
||||||
|
} else if (key == Qt::Key_F8) {
|
||||||
|
text = "F8";
|
||||||
|
} else if (key == Qt::Key_F9) {
|
||||||
|
text = "F9";
|
||||||
|
} else if (key == Qt::Key_F10) {
|
||||||
|
text = "F10";
|
||||||
|
} else if (key == Qt::Key_F11) {
|
||||||
|
text = "F11";
|
||||||
|
} else if (key == Qt::Key_F12) {
|
||||||
|
text = "F12";
|
||||||
|
} else if (key == Qt::Key_Up) {
|
||||||
|
text = "UP";
|
||||||
|
} else if (key == Qt::Key_Down) {
|
||||||
|
text = "DOWN";
|
||||||
|
} else if (key == Qt::Key_Left) {
|
||||||
|
text = "LEFT";
|
||||||
|
} else if (key == Qt::Key_Right) {
|
||||||
|
text = "RIGHT";
|
||||||
|
} else if (key == Qt::Key_Escape) {
|
||||||
|
text = "ESC";
|
||||||
|
} else if (key == Qt::Key_Tab) {
|
||||||
|
text = "TAB";
|
||||||
|
} else if (key == Qt::Key_Delete) {
|
||||||
|
text = "DELETE";
|
||||||
|
} else if (key == Qt::Key_Backspace) {
|
||||||
|
text = "BACKSPACE";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseEvent::MouseEvent(const QMouseEvent& event) {
|
MouseEvent::MouseEvent(const QMouseEvent& event) {
|
||||||
|
@ -65,16 +115,113 @@ void registerEventTypes(QScriptEngine* engine) {
|
||||||
QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event) {
|
QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event) {
|
||||||
QScriptValue obj = engine->newObject();
|
QScriptValue obj = engine->newObject();
|
||||||
obj.setProperty("key", event.key);
|
obj.setProperty("key", event.key);
|
||||||
|
obj.setProperty("text", event.text);
|
||||||
obj.setProperty("isShifted", event.isShifted);
|
obj.setProperty("isShifted", event.isShifted);
|
||||||
obj.setProperty("isMeta", event.isMeta);
|
obj.setProperty("isMeta", event.isMeta);
|
||||||
|
obj.setProperty("isControl", event.isControl);
|
||||||
|
obj.setProperty("isAlt", event.isAlt);
|
||||||
|
obj.setProperty("isKeypad", event.isKeypad);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) {
|
void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) {
|
||||||
event.key = object.property("key").toVariant().toInt();
|
|
||||||
event.isShifted = object.property("isShifted").toVariant().toBool();
|
event.isValid = false; // assume the worst
|
||||||
event.isMeta = object.property("isMeta").toVariant().toBool();
|
event.isMeta = object.property("isMeta").toVariant().toBool();
|
||||||
event.isValid = object.property("key").isValid();
|
event.isControl = object.property("isControl").toVariant().toBool();
|
||||||
|
event.isAlt = object.property("isAlt").toVariant().toBool();
|
||||||
|
event.isKeypad = object.property("isKeypad").toVariant().toBool();
|
||||||
|
|
||||||
|
QScriptValue key = object.property("key");
|
||||||
|
if (key.isValid()) {
|
||||||
|
event.key = key.toVariant().toInt();
|
||||||
|
event.text = QString(QChar(event.key));
|
||||||
|
event.isValid = true;
|
||||||
|
} else {
|
||||||
|
QScriptValue text = object.property("text");
|
||||||
|
if (text.isValid()) {
|
||||||
|
event.text = object.property("text").toVariant().toString();
|
||||||
|
|
||||||
|
// if the text is a special command, then map it here...
|
||||||
|
// TODO: come up with more elegant solution here, a map? is there a Qt function that gives nice names for keys?
|
||||||
|
if (event.text.toUpper() == "F1") {
|
||||||
|
event.key = Qt::Key_F1;
|
||||||
|
} else if (event.text.toUpper() == "F2") {
|
||||||
|
event.key = Qt::Key_F2;
|
||||||
|
} else if (event.text.toUpper() == "F3") {
|
||||||
|
event.key = Qt::Key_F3;
|
||||||
|
} else if (event.text.toUpper() == "F4") {
|
||||||
|
event.key = Qt::Key_F4;
|
||||||
|
} else if (event.text.toUpper() == "F5") {
|
||||||
|
event.key = Qt::Key_F5;
|
||||||
|
} else if (event.text.toUpper() == "F6") {
|
||||||
|
event.key = Qt::Key_F6;
|
||||||
|
} else if (event.text.toUpper() == "F7") {
|
||||||
|
event.key = Qt::Key_F7;
|
||||||
|
} else if (event.text.toUpper() == "F8") {
|
||||||
|
event.key = Qt::Key_F8;
|
||||||
|
} else if (event.text.toUpper() == "F9") {
|
||||||
|
event.key = Qt::Key_F9;
|
||||||
|
} else if (event.text.toUpper() == "F10") {
|
||||||
|
event.key = Qt::Key_F10;
|
||||||
|
} else if (event.text.toUpper() == "F11") {
|
||||||
|
event.key = Qt::Key_F11;
|
||||||
|
} else if (event.text.toUpper() == "F12") {
|
||||||
|
event.key = Qt::Key_F12;
|
||||||
|
} else if (event.text.toUpper() == "UP") {
|
||||||
|
event.key = Qt::Key_Up;
|
||||||
|
event.isKeypad = true;
|
||||||
|
} else if (event.text.toUpper() == "DOWN") {
|
||||||
|
event.key = Qt::Key_Down;
|
||||||
|
event.isKeypad = true;
|
||||||
|
} else if (event.text.toUpper() == "LEFT") {
|
||||||
|
event.key = Qt::Key_Left;
|
||||||
|
event.isKeypad = true;
|
||||||
|
} else if (event.text.toUpper() == "RIGHT") {
|
||||||
|
event.key = Qt::Key_Right;
|
||||||
|
event.isKeypad = true;
|
||||||
|
} else if (event.text.toUpper() == "ESC") {
|
||||||
|
event.key = Qt::Key_Escape;
|
||||||
|
} else if (event.text.toUpper() == "TAB") {
|
||||||
|
event.key = Qt::Key_Tab;
|
||||||
|
} else if (event.text.toUpper() == "DELETE") {
|
||||||
|
event.key = Qt::Key_Delete;
|
||||||
|
} else if (event.text.toUpper() == "BACKSPACE") {
|
||||||
|
event.key = Qt::Key_Backspace;
|
||||||
|
} else {
|
||||||
|
event.key = event.text.at(0).unicode();
|
||||||
|
}
|
||||||
|
event.isValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue isShifted = object.property("isShifted");
|
||||||
|
if (isShifted.isValid()) {
|
||||||
|
event.isShifted = isShifted.toVariant().toBool();
|
||||||
|
} else {
|
||||||
|
// if no isShifted was included, get it from the text
|
||||||
|
QChar character = event.text.at(0);
|
||||||
|
if (character.isLetter() && character.isUpper()) {
|
||||||
|
event.isShifted = true;
|
||||||
|
} else {
|
||||||
|
// if it's a symbol, then attempt to detect shifted-ness
|
||||||
|
if (QString("~!@#$%^&*()_+{}|:\"<>?").contains(character)) {
|
||||||
|
event.isShifted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const bool wantDebug = false;
|
||||||
|
if (wantDebug) {
|
||||||
|
qDebug() << "event.key=" << event.key
|
||||||
|
<< " event.text=" << event.text
|
||||||
|
<< " event.isShifted=" << event.isShifted
|
||||||
|
<< " event.isControl=" << event.isControl
|
||||||
|
<< " event.isMeta=" << event.isMeta
|
||||||
|
<< " event.isAlt=" << event.isAlt
|
||||||
|
<< " event.isKeypad=" << event.isKeypad;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event) {
|
QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event) {
|
||||||
|
|
|
@ -24,10 +24,19 @@ public:
|
||||||
KeyEvent();
|
KeyEvent();
|
||||||
KeyEvent(const QKeyEvent& event);
|
KeyEvent(const QKeyEvent& event);
|
||||||
inline bool operator==(const KeyEvent& other) const {
|
inline bool operator==(const KeyEvent& other) const {
|
||||||
return other.key == key && other.isShifted == isShifted && other.isMeta == isMeta; }
|
return other.key == key
|
||||||
|
&& other.isShifted == isShifted
|
||||||
|
&& other.isControl == isControl
|
||||||
|
&& other.isMeta == isMeta
|
||||||
|
&& other.isAlt == isAlt
|
||||||
|
&& other.isKeypad == isKeypad; }
|
||||||
int key;
|
int key;
|
||||||
|
QString text;
|
||||||
bool isShifted;
|
bool isShifted;
|
||||||
|
bool isControl;
|
||||||
bool isMeta;
|
bool isMeta;
|
||||||
|
bool isAlt;
|
||||||
|
bool isKeypad;
|
||||||
bool isValid;
|
bool isValid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
#include <VoxelConstants.h>
|
#include <VoxelConstants.h>
|
||||||
|
#include <VoxelDetail.h>
|
||||||
#include <ParticlesScriptingInterface.h>
|
#include <ParticlesScriptingInterface.h>
|
||||||
|
|
||||||
#include <Sound.h>
|
#include <Sound.h>
|
||||||
|
@ -41,6 +42,7 @@ static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* eng
|
||||||
|
|
||||||
ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, const QString& fileNameString, AbstractMenuInterface* menu,
|
ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, const QString& fileNameString, AbstractMenuInterface* menu,
|
||||||
AbstractControllerScriptingInterface* controllerScriptingInterface) :
|
AbstractControllerScriptingInterface* controllerScriptingInterface) :
|
||||||
|
_isAvatar(false),
|
||||||
_dataServerScriptingInterface(),
|
_dataServerScriptingInterface(),
|
||||||
_avatarData(NULL)
|
_avatarData(NULL)
|
||||||
{
|
{
|
||||||
|
@ -114,10 +116,12 @@ void ScriptEngine::init() {
|
||||||
_voxelsScriptingInterface.init();
|
_voxelsScriptingInterface.init();
|
||||||
_particlesScriptingInterface.init();
|
_particlesScriptingInterface.init();
|
||||||
|
|
||||||
// register meta-type for glm::vec3 conversions
|
// register various meta-types
|
||||||
registerMetaTypes(&_engine);
|
registerMetaTypes(&_engine);
|
||||||
|
registerVoxelMetaTypes(&_engine);
|
||||||
|
//registerParticleMetaTypes(&_engine);
|
||||||
registerEventTypes(&_engine);
|
registerEventTypes(&_engine);
|
||||||
|
|
||||||
qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue);
|
qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue);
|
||||||
qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue);
|
qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue);
|
||||||
qScriptRegisterSequenceMetaType<QVector<ParticleID> >(&_engine);
|
qScriptRegisterSequenceMetaType<QVector<ParticleID> >(&_engine);
|
||||||
|
@ -129,7 +133,7 @@ void ScriptEngine::init() {
|
||||||
QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject<AudioInjectorOptions>();
|
QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject<AudioInjectorOptions>();
|
||||||
_engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue);
|
_engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue);
|
||||||
|
|
||||||
registerGlobalObject("Agent", this);
|
registerGlobalObject("Script", this);
|
||||||
registerGlobalObject("Audio", &_audioScriptingInterface);
|
registerGlobalObject("Audio", &_audioScriptingInterface);
|
||||||
registerGlobalObject("Controller", _controllerScriptingInterface);
|
registerGlobalObject("Controller", _controllerScriptingInterface);
|
||||||
registerGlobalObject("Data", &_dataServerScriptingInterface);
|
registerGlobalObject("Data", &_dataServerScriptingInterface);
|
||||||
|
|
|
@ -31,8 +31,6 @@ const QString NO_SCRIPT("");
|
||||||
|
|
||||||
class ScriptEngine : public QObject {
|
class ScriptEngine : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
|
|
||||||
public:
|
public:
|
||||||
ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false,
|
ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false,
|
||||||
const QString& scriptMenuName = QString(""), AbstractMenuInterface* menu = NULL,
|
const QString& scriptMenuName = QString(""), AbstractMenuInterface* menu = NULL,
|
||||||
|
@ -41,10 +39,10 @@ public:
|
||||||
~ScriptEngine();
|
~ScriptEngine();
|
||||||
|
|
||||||
/// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
|
/// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
|
||||||
VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; }
|
static VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; }
|
||||||
|
|
||||||
/// Access the ParticlesScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
|
/// Access the ParticlesScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
|
||||||
ParticlesScriptingInterface* getParticlesScriptingInterface() { return &_particlesScriptingInterface; }
|
static ParticlesScriptingInterface* getParticlesScriptingInterface() { return &_particlesScriptingInterface; }
|
||||||
|
|
||||||
/// Access the DataServerScriptingInterface for access to its underlying UUID
|
/// Access the DataServerScriptingInterface for access to its underlying UUID
|
||||||
const DataServerScriptingInterface& getDataServerScriptingInterface() { return _dataServerScriptingInterface; }
|
const DataServerScriptingInterface& getDataServerScriptingInterface() { return _dataServerScriptingInterface; }
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
|
|
||||||
#include "Assignment.h"
|
#include "Assignment.h"
|
||||||
|
|
||||||
const char IPv4_ADDRESS_DESIGNATOR = 4;
|
|
||||||
const char IPv6_ADDRESS_DESIGNATOR = 6;
|
|
||||||
|
|
||||||
Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) {
|
Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) {
|
||||||
switch (nodeType) {
|
switch (nodeType) {
|
||||||
case NodeType::AudioMixer:
|
case NodeType::AudioMixer:
|
||||||
|
@ -55,6 +52,7 @@ Assignment::Assignment() :
|
||||||
}
|
}
|
||||||
|
|
||||||
Assignment::Assignment(Assignment::Command command, Assignment::Type type, const QString& pool, Assignment::Location location) :
|
Assignment::Assignment(Assignment::Command command, Assignment::Type type, const QString& pool, Assignment::Location location) :
|
||||||
|
_uuid(),
|
||||||
_command(command),
|
_command(command),
|
||||||
_type(type),
|
_type(type),
|
||||||
_pool(pool),
|
_pool(pool),
|
||||||
|
@ -85,20 +83,7 @@ Assignment::Assignment(const QByteArray& packet) :
|
||||||
QDataStream packetStream(packet);
|
QDataStream packetStream(packet);
|
||||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||||
|
|
||||||
uchar assignmentType;
|
packetStream >> *this;
|
||||||
packetStream >> assignmentType;
|
|
||||||
_type = (Assignment::Type) assignmentType;
|
|
||||||
|
|
||||||
if (_command != Assignment::RequestCommand) {
|
|
||||||
// read the GUID for this assignment
|
|
||||||
packetStream >> _uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
packetStream >> _pool;
|
|
||||||
|
|
||||||
if (!packetStream.atEnd()) {
|
|
||||||
_payload = packet.mid(packetStream.device()->pos());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
@ -159,18 +144,26 @@ const char* Assignment::getTypeName() const {
|
||||||
QDebug operator<<(QDebug debug, const Assignment &assignment) {
|
QDebug operator<<(QDebug debug, const Assignment &assignment) {
|
||||||
debug.nospace() << "UUID: " << qPrintable(assignment.getUUID().toString()) <<
|
debug.nospace() << "UUID: " << qPrintable(assignment.getUUID().toString()) <<
|
||||||
", Type: " << assignment.getType();
|
", Type: " << assignment.getType();
|
||||||
return debug.nospace();
|
|
||||||
|
if (!assignment.getPool().isEmpty()) {
|
||||||
|
debug << ", Pool: " << assignment.getPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
return debug.space();
|
||||||
}
|
}
|
||||||
|
|
||||||
QDataStream& operator<<(QDataStream &out, const Assignment& assignment) {
|
QDataStream& operator<<(QDataStream &out, const Assignment& assignment) {
|
||||||
out << (quint8) assignment._type;
|
out << (quint8) assignment._type << assignment._uuid << assignment._pool << assignment._payload;
|
||||||
|
|
||||||
// pack the UUID for this assignment, if this is an assignment create or deploy
|
|
||||||
if (assignment._command != Assignment::RequestCommand) {
|
|
||||||
out << assignment._uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
out << assignment._pool << assignment._payload;
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDataStream& operator>>(QDataStream &in, Assignment& assignment) {
|
||||||
|
quint8 packedType;
|
||||||
|
in >> packedType;
|
||||||
|
assignment._type = (Assignment::Type) packedType;
|
||||||
|
|
||||||
|
in >> assignment._uuid >> assignment._pool >> assignment._payload;
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#include "NodeList.h"
|
#include "NodeList.h"
|
||||||
|
|
||||||
const int MAX_PAYLOAD_BYTES = 1024;
|
const int MAX_PAYLOAD_BYTES = 1024;
|
||||||
const int MAX_ASSIGNMENT_POOL_BYTES = 64 + sizeof('\0');
|
|
||||||
|
|
||||||
const QString emptyPool = QString();
|
const QString emptyPool = QString();
|
||||||
|
|
||||||
|
@ -76,7 +75,7 @@ public:
|
||||||
Assignment::Location getLocation() const { return _location; }
|
Assignment::Location getLocation() const { return _location; }
|
||||||
|
|
||||||
const QByteArray& getPayload() const { return _payload; }
|
const QByteArray& getPayload() const { return _payload; }
|
||||||
void setPayload(const QByteArray& payload) { _payload = payload; }
|
void setPayload(const QByteArray& payload) { _payload = payload.left(MAX_PAYLOAD_BYTES); }
|
||||||
|
|
||||||
void setPool(const QString& pool) { _pool = pool; };
|
void setPool(const QString& pool) { _pool = pool; };
|
||||||
const QString& getPool() const { return _pool; }
|
const QString& getPool() const { return _pool; }
|
||||||
|
@ -92,6 +91,7 @@ public:
|
||||||
|
|
||||||
friend QDebug operator<<(QDebug debug, const Assignment& assignment);
|
friend QDebug operator<<(QDebug debug, const Assignment& assignment);
|
||||||
friend QDataStream& operator<<(QDataStream &out, const Assignment& assignment);
|
friend QDataStream& operator<<(QDataStream &out, const Assignment& assignment);
|
||||||
|
friend QDataStream& operator>>(QDataStream &in, Assignment& assignment);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QUuid _uuid; /// the 16 byte UUID for this assignment
|
QUuid _uuid; /// the 16 byte UUID for this assignment
|
||||||
|
|
|
@ -119,13 +119,13 @@ void NodeList::timePingReply(const QByteArray& packet) {
|
||||||
|
|
||||||
if (matchingNode) {
|
if (matchingNode) {
|
||||||
QDataStream packetStream(packet);
|
QDataStream packetStream(packet);
|
||||||
packetStream.device()->seek(numBytesForPacketHeader(packet));
|
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||||
|
|
||||||
qint64 ourOriginalTime, othersReplyTime;
|
quint64 ourOriginalTime, othersReplyTime;
|
||||||
|
|
||||||
packetStream >> ourOriginalTime >> othersReplyTime;
|
packetStream >> ourOriginalTime >> othersReplyTime;
|
||||||
|
|
||||||
qint64 now = usecTimestampNow();
|
quint64 now = usecTimestampNow();
|
||||||
int pingTime = now - ourOriginalTime;
|
int pingTime = now - ourOriginalTime;
|
||||||
int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight
|
int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight
|
||||||
|
|
||||||
|
@ -554,8 +554,11 @@ QByteArray NodeList::constructPingPacket() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) {
|
QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) {
|
||||||
|
QDataStream pingPacketStream(pingPacket);
|
||||||
|
pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket));
|
||||||
|
|
||||||
quint64 timeFromOriginalPing;
|
quint64 timeFromOriginalPing;
|
||||||
memcpy(&timeFromOriginalPing, pingPacket.data() + numBytesForPacketHeader(pingPacket), sizeof(timeFromOriginalPing));
|
pingPacketStream >> timeFromOriginalPing;
|
||||||
|
|
||||||
QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypePingReply);
|
QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypePingReply);
|
||||||
QDataStream packetStream(&replyPacket, QIODevice::Append);
|
QDataStream packetStream(&replyPacket, QIODevice::Append);
|
||||||
|
@ -621,8 +624,7 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned NodeList::broadcastToNodes(const QByteArray& packet,
|
unsigned NodeList::broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) {
|
||||||
const NodeSet& destinationNodeTypes) {
|
|
||||||
unsigned n = 0;
|
unsigned n = 0;
|
||||||
|
|
||||||
foreach (const SharedNodePointer& node, getNodeHash()) {
|
foreach (const SharedNodePointer& node, getNodeHash()) {
|
||||||
|
|
|
@ -80,7 +80,7 @@ bool packetVersionMatch(const QByteArray& packet) {
|
||||||
// currently this just checks if the version in the packet matches our return from versionForPacketType
|
// currently this just checks if the version in the packet matches our return from versionForPacketType
|
||||||
// may need to be expanded in the future for types and versions that take > than 1 byte
|
// may need to be expanded in the future for types and versions that take > than 1 byte
|
||||||
|
|
||||||
if (packet[1] == versionForPacketType(packetTypeForPacket(packet)) || packet[0] == PacketTypeStunResponse) {
|
if (packet[1] == versionForPacketType(packetTypeForPacket(packet)) || packetTypeForPacket(packet) == PacketTypeStunResponse) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
PacketType mismatchType = packetTypeForPacket(packet);
|
PacketType mismatchType = packetTypeForPacket(packet);
|
||||||
|
|
|
@ -22,6 +22,7 @@ Q_DECLARE_METATYPE(glm::vec2)
|
||||||
Q_DECLARE_METATYPE(glm::quat)
|
Q_DECLARE_METATYPE(glm::quat)
|
||||||
Q_DECLARE_METATYPE(xColor)
|
Q_DECLARE_METATYPE(xColor)
|
||||||
|
|
||||||
|
|
||||||
void registerMetaTypes(QScriptEngine* engine);
|
void registerMetaTypes(QScriptEngine* engine);
|
||||||
|
|
||||||
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3);
|
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3);
|
||||||
|
|
|
@ -231,7 +231,6 @@ void sharedMessageHandler(QtMsgType type, const QMessageLogContext& context, con
|
||||||
fprintf(stdout, "%s", message.toLocal8Bit().constData());
|
fprintf(stdout, "%s", message.toLocal8Bit().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned char* pointToOctalCode(float x, float y, float z, float s) {
|
unsigned char* pointToOctalCode(float x, float y, float z, float s) {
|
||||||
return pointToVoxel(x, y, z, s);
|
return pointToVoxel(x, y, z, s);
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,20 +96,9 @@ bool cmdOptionExists(int argc, const char * argv[],const char* option);
|
||||||
|
|
||||||
void sharedMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message);
|
void sharedMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message);
|
||||||
|
|
||||||
struct VoxelDetail {
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
float z;
|
|
||||||
float s;
|
|
||||||
unsigned char red;
|
|
||||||
unsigned char green;
|
|
||||||
unsigned char blue;
|
|
||||||
};
|
|
||||||
|
|
||||||
unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r = 0, unsigned char g = 0, unsigned char b = 0);
|
unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r = 0, unsigned char g = 0, unsigned char b = 0);
|
||||||
unsigned char* pointToOctalCode(float x, float y, float z, float s);
|
unsigned char* pointToOctalCode(float x, float y, float z, float s);
|
||||||
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void usleep(int waitTime);
|
void usleep(int waitTime);
|
||||||
#endif
|
#endif
|
||||||
|
|
37
libraries/voxels/src/VoxelDetail.cpp
Normal file
37
libraries/voxels/src/VoxelDetail.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
//
|
||||||
|
// VoxelDetail.cpp
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 1/29/2014
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
#include "VoxelDetail.h"
|
||||||
|
|
||||||
|
void registerVoxelMetaTypes(QScriptEngine* engine) {
|
||||||
|
qScriptRegisterMetaType(engine, voxelDetailToScriptValue, voxelDetailFromScriptValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue voxelDetailToScriptValue(QScriptEngine* engine, const VoxelDetail& voxelDetail) {
|
||||||
|
QScriptValue obj = engine->newObject();
|
||||||
|
obj.setProperty("x", voxelDetail.x * (float)TREE_SCALE);
|
||||||
|
obj.setProperty("y", voxelDetail.y * (float)TREE_SCALE);
|
||||||
|
obj.setProperty("z", voxelDetail.z * (float)TREE_SCALE);
|
||||||
|
obj.setProperty("s", voxelDetail.s * (float)TREE_SCALE);
|
||||||
|
obj.setProperty("red", voxelDetail.red);
|
||||||
|
obj.setProperty("green", voxelDetail.green);
|
||||||
|
obj.setProperty("blue", voxelDetail.blue);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void voxelDetailFromScriptValue(const QScriptValue &object, VoxelDetail& voxelDetail) {
|
||||||
|
voxelDetail.x = object.property("x").toVariant().toFloat() / (float)TREE_SCALE;
|
||||||
|
voxelDetail.y = object.property("y").toVariant().toFloat() / (float)TREE_SCALE;
|
||||||
|
voxelDetail.z = object.property("z").toVariant().toFloat() / (float)TREE_SCALE;
|
||||||
|
voxelDetail.s = object.property("s").toVariant().toFloat() / (float)TREE_SCALE;
|
||||||
|
voxelDetail.red = object.property("red").toVariant().toInt();
|
||||||
|
voxelDetail.green = object.property("green").toVariant().toInt();
|
||||||
|
voxelDetail.blue = object.property("blue").toVariant().toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
36
libraries/voxels/src/VoxelDetail.h
Normal file
36
libraries/voxels/src/VoxelDetail.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
//
|
||||||
|
// VoxelDetail.h
|
||||||
|
// hifi
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 1/29/2014
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __hifi__VoxelDetail__
|
||||||
|
#define __hifi__VoxelDetail__
|
||||||
|
|
||||||
|
#include <QtScript/QScriptEngine>
|
||||||
|
|
||||||
|
#include <SharedUtil.h>
|
||||||
|
#include "VoxelConstants.h"
|
||||||
|
|
||||||
|
struct VoxelDetail {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
float s;
|
||||||
|
unsigned char red;
|
||||||
|
unsigned char green;
|
||||||
|
unsigned char blue;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(VoxelDetail)
|
||||||
|
|
||||||
|
void registerVoxelMetaTypes(QScriptEngine* engine);
|
||||||
|
|
||||||
|
QScriptValue voxelDetailToScriptValue(QScriptEngine* engine, const VoxelDetail& color);
|
||||||
|
void voxelDetailFromScriptValue(const QScriptValue &object, VoxelDetail& color);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(__hifi__VoxelDetail__) */
|
|
@ -70,14 +70,14 @@ bool createVoxelEditMessage(PacketType command, short int sequence,
|
||||||
// cleanup
|
// cleanup
|
||||||
delete[] voxelData;
|
delete[] voxelData;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
// finally, copy the result to the output
|
// finally, copy the result to the output
|
||||||
bufferOut = new unsigned char[actualMessageSize];
|
bufferOut = new unsigned char[actualMessageSize];
|
||||||
sizeOut = actualMessageSize;
|
sizeOut = actualMessageSize;
|
||||||
memcpy(bufferOut, messageBuffer, actualMessageSize);
|
memcpy(bufferOut, messageBuffer, actualMessageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] messageBuffer; // clean up our temporary buffer
|
delete[] messageBuffer; // clean up our temporary buffer
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#define __shared__VoxelEditPacketSender__
|
#define __shared__VoxelEditPacketSender__
|
||||||
|
|
||||||
#include <OctreeEditPacketSender.h>
|
#include <OctreeEditPacketSender.h>
|
||||||
|
#include "VoxelDetail.h"
|
||||||
|
|
||||||
/// Utility for processing, packing, queueing and sending of outbound edit voxel messages.
|
/// Utility for processing, packing, queueing and sending of outbound edit voxel messages.
|
||||||
class VoxelEditPacketSender : public OctreeEditPacketSender {
|
class VoxelEditPacketSender : public OctreeEditPacketSender {
|
||||||
|
|
|
@ -91,5 +91,4 @@ protected:
|
||||||
nodeColor _currentColor; /// Client only, false color of this voxel, 4 bytes
|
nodeColor _currentColor; /// Client only, false color of this voxel, 4 bytes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif /* defined(__hifi__VoxelTreeElement__) */
|
#endif /* defined(__hifi__VoxelTreeElement__) */
|
|
@ -57,20 +57,4 @@ private:
|
||||||
void queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails);
|
void queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails);
|
||||||
};
|
};
|
||||||
|
|
||||||
class VoxelDetailScriptObject : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
VoxelDetailScriptObject(VoxelDetail* voxelDetail) { _voxelDetail = voxelDetail; }
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
/// position in meter units
|
|
||||||
glm::vec3 getPosition() const { return glm::vec3(_voxelDetail->x, _voxelDetail->y, _voxelDetail->z) * (float)TREE_SCALE; }
|
|
||||||
xColor getColor() const { xColor color = { _voxelDetail->red, _voxelDetail->green, _voxelDetail->blue }; return color; }
|
|
||||||
/// scale in meter units
|
|
||||||
float getScale() const { return _voxelDetail->s * (float)TREE_SCALE; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
VoxelDetail* _voxelDetail;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* defined(__hifi__VoxelsScriptingInterface__) */
|
#endif /* defined(__hifi__VoxelsScriptingInterface__) */
|
||||||
|
|
Loading…
Reference in a new issue