mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 03:58:15 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into controllers
This commit is contained in:
commit
d2f3b48637
41 changed files with 755 additions and 96 deletions
11
BUILD.md
11
BUILD.md
|
@ -1,7 +1,7 @@
|
|||
###Dependencies
|
||||
|
||||
* [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 3.3.2
|
||||
* [Qt](http://www.qt.io/download-open-source) ~> 5.4.1
|
||||
* [Qt](http://www.qt.io/download-open-source) ~> 5.5.1
|
||||
* [OpenSSL](https://www.openssl.org/community/binaries.html) ~> 1.0.1m
|
||||
* IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities.
|
||||
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
|
||||
|
@ -42,12 +42,12 @@ Hifi uses CMake to generate build files and project files for your platform.
|
|||
####Qt
|
||||
In order for CMake to find the Qt5 find modules, you will need to set an ENV variable pointing to your Qt installation.
|
||||
|
||||
For example, a Qt5 5.4.1 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).
|
||||
For example, a Qt5 5.5.1 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).
|
||||
|
||||
The path it needs to be set to will depend on where and how Qt5 was installed. e.g.
|
||||
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/clang_64/lib/cmake/
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.4.1/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.5.1/clang_64/lib/cmake/
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
|
||||
|
||||
####Generating build files
|
||||
|
@ -64,7 +64,7 @@ Any variables that need to be set for CMake to find dependencies can be set as E
|
|||
|
||||
For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation:
|
||||
|
||||
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/lib/cmake
|
||||
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.5.1/lib/cmake
|
||||
|
||||
####Finding Dependencies
|
||||
|
||||
|
@ -83,4 +83,3 @@ In the examples below the variable $NAME would be replaced by the name of the de
|
|||
####Devices
|
||||
|
||||
You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device.
|
||||
|
||||
|
|
13
BUILD_OSX.md
13
BUILD_OSX.md
|
@ -3,20 +3,15 @@ Please read the [general build guide](BUILD.md) for information on dependencies
|
|||
###Homebrew
|
||||
[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all High Fidelity dependencies very simple.
|
||||
|
||||
brew tap highfidelity/homebrew-formulas
|
||||
brew install cmake openssl
|
||||
brew install highfidelity/formulas/qt5
|
||||
brew link qt5 --force
|
||||
brew install cmake openssl qt5
|
||||
|
||||
We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. In the code block above qt5 is installed from a formula in this repository.
|
||||
|
||||
*Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.4.x stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.*
|
||||
We no longer require install of qt5 via our [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas). Versions of Qt that are 5.5.x and above provide a mechanism to disable the wireless scanning we previously had a custom patch for.
|
||||
|
||||
###Qt
|
||||
|
||||
Assuming you've installed Qt 5 using the homebrew instructions above, you'll need to set QT_CMAKE_PREFIX_PATH so CMake can find your installation of Qt. For Qt 5.4.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows.
|
||||
Assuming you've installed Qt 5 using the homebrew instructions above, you'll need to set QT_CMAKE_PREFIX_PATH so CMake can find your installation of Qt. For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows.
|
||||
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.4.1/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1/lib/cmake
|
||||
|
||||
###Xcode
|
||||
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.
|
||||
|
|
|
@ -29,12 +29,12 @@ NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit
|
|||
|
||||
* [Download the online installer](http://qt-project.org/downloads)
|
||||
* When it asks you to select components, ONLY select the following:
|
||||
* Qt > Qt 5.4.1 > **msvc2013 32-bit OpenGL**
|
||||
* Qt > Qt 5.5.1 > **msvc2013 32-bit**
|
||||
|
||||
* [Download the offline installer](http://download.qt.io/official_releases/qt/5.4/5.4.1/qt-opensource-windows-x86-msvc2013_opengl-5.4.1.exe)
|
||||
* [Download the offline installer](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013-5.5.1.exe)
|
||||
|
||||
Once Qt is installed, you need to manually configure the following:
|
||||
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.4.1\msvc2013_opengl\lib\cmake` directory.
|
||||
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.5.1\msvc2013\lib\cmake` directory.
|
||||
* You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New
|
||||
|
||||
###External Libraries
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <AvatarHashMap.h>
|
||||
#include <MessagesClient.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
@ -116,11 +117,21 @@ const int PING_INTERVAL = 1000;
|
|||
void Agent::run() {
|
||||
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
|
||||
|
||||
// Setup MessagesClient
|
||||
auto messagesClient = DependencyManager::set<MessagesClient>();
|
||||
QThread* messagesThread = new QThread;
|
||||
messagesThread->setObjectName("Messages Client Thread");
|
||||
messagesClient->moveToThread(messagesThread);
|
||||
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
|
||||
messagesThread->start();
|
||||
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet()
|
||||
<< NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer
|
||||
<< NodeType::EntityServer
|
||||
<< NodeType::MessagesMixer
|
||||
);
|
||||
|
||||
_pingTimer = new QTimer(this);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "avatars/AvatarMixer.h"
|
||||
#include "entities/EntityServer.h"
|
||||
#include "assets/AssetServer.h"
|
||||
#include "messages/MessagesMixer.h"
|
||||
|
||||
ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) {
|
||||
|
||||
|
@ -36,6 +37,8 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) {
|
|||
return new EntityServer(packet);
|
||||
case Assignment::AssetServerType:
|
||||
return new AssetServer(packet);
|
||||
case Assignment::MessagesMixerType:
|
||||
return new MessagesMixer(packet);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -112,7 +112,6 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN
|
|||
quint64 deletePacketSentAt = usecTimestampNow();
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
auto recentlyDeleted = tree->getRecentlyDeletedEntityIDs();
|
||||
bool hasMoreToSend = true;
|
||||
|
||||
packetsSent = 0;
|
||||
|
||||
|
|
157
assignment-client/src/messages/MessagesMixer.cpp
Normal file
157
assignment-client/src/messages/MessagesMixer.cpp
Normal file
|
@ -0,0 +1,157 @@
|
|||
//
|
||||
// MessagesMixer.cpp
|
||||
// assignment-client/src/messages
|
||||
//
|
||||
// Created by Brad hefta-Gaub on 11/16/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QBuffer>
|
||||
|
||||
#include <LogHandler.h>
|
||||
#include <NodeList.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
||||
#include "MessagesMixer.h"
|
||||
|
||||
const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer";
|
||||
|
||||
MessagesMixer::MessagesMixer(NLPacket& packet) :
|
||||
ThreadedAssignment(packet)
|
||||
{
|
||||
// make sure we hear about node kills so we can tell the other nodes
|
||||
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled);
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessages");
|
||||
packetReceiver.registerMessageListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe");
|
||||
packetReceiver.registerMessageListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe");
|
||||
}
|
||||
|
||||
MessagesMixer::~MessagesMixer() {
|
||||
}
|
||||
|
||||
void MessagesMixer::nodeKilled(SharedNodePointer killedNode) {
|
||||
for (auto& channel : _channelSubscribers) {
|
||||
channel.remove(killedNode->getUUID());
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesMixer::handleMessages(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
||||
Q_ASSERT(packetList->getType() == PacketType::MessagesData);
|
||||
|
||||
QByteArray packetData = packetList->getMessage();
|
||||
QBuffer packet{ &packetData };
|
||||
packet.open(QIODevice::ReadOnly);
|
||||
|
||||
quint16 channelLength;
|
||||
packet.read(reinterpret_cast<char*>(&channelLength), sizeof(channelLength));
|
||||
auto channelData = packet.read(channelLength);
|
||||
QString channel = QString::fromUtf8(channelData);
|
||||
|
||||
quint16 messageLength;
|
||||
packet.read(reinterpret_cast<char*>(&messageLength), sizeof(messageLength));
|
||||
auto messageData = packet.read(messageLength);
|
||||
QString message = QString::fromUtf8(messageData);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
nodeList->eachMatchingNode(
|
||||
[&](const SharedNodePointer& node)->bool {
|
||||
|
||||
return node->getType() == NodeType::Agent && node->getActiveSocket() &&
|
||||
_channelSubscribers[channel].contains(node->getUUID());
|
||||
},
|
||||
[&](const SharedNodePointer& node) {
|
||||
|
||||
auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true);
|
||||
|
||||
auto channelUtf8 = channel.toUtf8();
|
||||
quint16 channelLength = channelUtf8.length();
|
||||
packetList->writePrimitive(channelLength);
|
||||
packetList->write(channelUtf8);
|
||||
|
||||
auto messageUtf8 = message.toUtf8();
|
||||
quint16 messageLength = messageUtf8.length();
|
||||
packetList->writePrimitive(messageLength);
|
||||
packetList->write(messageUtf8);
|
||||
|
||||
nodeList->sendPacketList(std::move(packetList), *node);
|
||||
});
|
||||
}
|
||||
|
||||
void MessagesMixer::handleMessagesSubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
||||
Q_ASSERT(packetList->getType() == PacketType::MessagesSubscribe);
|
||||
QString channel = QString::fromUtf8(packetList->getMessage());
|
||||
qDebug() << "Node [" << senderNode->getUUID() << "] subscribed to channel:" << channel;
|
||||
_channelSubscribers[channel] << senderNode->getUUID();
|
||||
}
|
||||
|
||||
void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
||||
Q_ASSERT(packetList->getType() == PacketType::MessagesUnsubscribe);
|
||||
QString channel = QString::fromUtf8(packetList->getMessage());
|
||||
qDebug() << "Node [" << senderNode->getUUID() << "] unsubscribed from channel:" << channel;
|
||||
|
||||
if (_channelSubscribers.contains(channel)) {
|
||||
_channelSubscribers[channel].remove(senderNode->getUUID());
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME - make these stats relevant
|
||||
void MessagesMixer::sendStatsPacket() {
|
||||
QJsonObject statsObject;
|
||||
QJsonObject messagesObject;
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
// add stats for each listerner
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
QJsonObject messagesStats;
|
||||
|
||||
// add the key to ask the domain-server for a username replacement, if it has it
|
||||
messagesStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID());
|
||||
messagesStats["outbound_kbps"] = node->getOutboundBandwidth();
|
||||
messagesStats["inbound_kbps"] = node->getInboundBandwidth();
|
||||
|
||||
messagesObject[uuidStringWithoutCurlyBraces(node->getUUID())] = messagesStats;
|
||||
});
|
||||
|
||||
statsObject["messages"] = messagesObject;
|
||||
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
|
||||
}
|
||||
|
||||
void MessagesMixer::run() {
|
||||
ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
// wait until we have the domain-server settings, otherwise we bail
|
||||
DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
|
||||
qDebug() << "Waiting for domain settings from domain-server.";
|
||||
|
||||
// block until we get the settingsRequestComplete signal
|
||||
QEventLoop loop;
|
||||
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
|
||||
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
|
||||
domainHandler.requestDomainSettings();
|
||||
loop.exec();
|
||||
|
||||
if (domainHandler.getSettingsObject().isEmpty()) {
|
||||
qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment.";
|
||||
setFinished(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// parse the settings to pull out the values we need
|
||||
parseDomainServerSettings(domainHandler.getSettingsObject());
|
||||
}
|
||||
|
||||
void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
||||
// TODO - if we want options, parse them here...
|
||||
const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer";
|
||||
}
|
43
assignment-client/src/messages/MessagesMixer.h
Normal file
43
assignment-client/src/messages/MessagesMixer.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// MessagesMixer.h
|
||||
// assignment-client/src/messages
|
||||
//
|
||||
// Created by Brad hefta-Gaub on 11/16/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// The avatar mixer receives head, hand and positional data from all connected
|
||||
// nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_MessagesMixer_h
|
||||
#define hifi_MessagesMixer_h
|
||||
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
/// Handles assignments of type MessagesMixer - distribution of avatar data to various clients
|
||||
class MessagesMixer : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MessagesMixer(NLPacket& packet);
|
||||
~MessagesMixer();
|
||||
|
||||
public slots:
|
||||
void run();
|
||||
void nodeKilled(SharedNodePointer killedNode);
|
||||
void sendStatsPacket();
|
||||
|
||||
private slots:
|
||||
void handleMessages(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
||||
void handleMessagesSubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
||||
void handleMessagesUnsubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
||||
|
||||
private:
|
||||
void parseDomainServerSettings(const QJsonObject& domainSettings);
|
||||
|
||||
QHash<QString,QSet<QUuid>> _channelSubscribers;
|
||||
};
|
||||
|
||||
#endif // hifi_MessagesMixer_h
|
|
@ -48,7 +48,8 @@ QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID
|
|||
|
||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer << NodeType::EntityServer
|
||||
<< NodeType::AssetServer;
|
||||
<< NodeType::AssetServer
|
||||
<< NodeType::MessagesMixer;
|
||||
|
||||
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> packet) {
|
||||
if (packet->getPayloadSize() == 0) {
|
||||
|
@ -66,7 +67,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> pack
|
|||
}
|
||||
|
||||
static const NodeSet VALID_NODE_TYPES {
|
||||
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent
|
||||
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent, NodeType::MessagesMixer
|
||||
};
|
||||
|
||||
if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) {
|
||||
|
|
|
@ -554,7 +554,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
defaultedType != Assignment::AllTypes;
|
||||
defaultedType = static_cast<Assignment::Type>(static_cast<int>(defaultedType) + 1)) {
|
||||
if (!excludedTypes.contains(defaultedType)
|
||||
&& defaultedType != Assignment::UNUSED_0
|
||||
&& defaultedType != Assignment::UNUSED_1
|
||||
&& defaultedType != Assignment::AgentType) {
|
||||
|
||||
|
|
39
examples/example/messagesExample.js
Normal file
39
examples/example/messagesExample.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
var totalTime = 0;
|
||||
var unsubscribedForTime = 0;
|
||||
var subscribedForTime = 0;
|
||||
var subscribed = false;
|
||||
var SWITCH_SUBSCRIPTION_TIME = 10;
|
||||
Script.update.connect(function (deltaTime) {
|
||||
var channel = "example";
|
||||
totalTime += deltaTime;
|
||||
if (!subscribed) {
|
||||
unsubscribedForTime += deltaTime;
|
||||
} else {
|
||||
subscribedForTime += deltaTime;
|
||||
}
|
||||
|
||||
// if we've been unsubscribed for SWITCH_SUBSCRIPTION_TIME seconds, subscribe
|
||||
if (!subscribed && unsubscribedForTime > SWITCH_SUBSCRIPTION_TIME) {
|
||||
print("---- subscribing ----");
|
||||
Messages.subscribe(channel);
|
||||
subscribed = true;
|
||||
subscribedForTime = 0;
|
||||
}
|
||||
|
||||
// if we've been subscribed for SWITCH_SUBSCRIPTION_TIME seconds, unsubscribe
|
||||
if (subscribed && subscribedForTime > SWITCH_SUBSCRIPTION_TIME) {
|
||||
print("---- unsubscribing ----");
|
||||
Messages.unsubscribe(channel);
|
||||
subscribed = false;
|
||||
unsubscribedForTime = 0;
|
||||
}
|
||||
|
||||
// Even if not subscribed, still publish
|
||||
var message = "update() deltaTime:" + deltaTime;
|
||||
Messages.sendMessage(channel, message);
|
||||
});
|
||||
|
||||
|
||||
Messages.messageReceived.connect(function (channel, message, senderID) {
|
||||
print("message received on channel:" + channel + ", message:" + message + ", senderID:" + senderID);
|
||||
});
|
7
examples/example/messagesReceiverExample.js
Normal file
7
examples/example/messagesReceiverExample.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
print("---- subscribing ----");
|
||||
Messages.subscribe("example");
|
||||
|
||||
Messages.messageReceived.connect(function (channel, message, senderID) {
|
||||
print("message received on channel:" + channel + ", message:" + message + ", senderID:" + senderID);
|
||||
});
|
||||
|
122
examples/growth.js
Normal file
122
examples/growth.js
Normal file
|
@ -0,0 +1,122 @@
|
|||
//
|
||||
// growth.js
|
||||
//
|
||||
// Created by Zachary Pomerantz 11/2/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
var RADIUS = 5;
|
||||
var LIFETIME = 60; // only keep these entities around for a minute
|
||||
|
||||
var SIZE = 1; // starting size
|
||||
var SIZE_MAX = 3;
|
||||
var SIZE_MIN = 0.5;
|
||||
|
||||
var center = MyAvatar.position;
|
||||
var positions = [
|
||||
Vec3.sum(center, Vec3.multiply(RADIUS, Vec3.FRONT)),
|
||||
Vec3.sum(center, Vec3.multiply(-RADIUS, Vec3.FRONT)),
|
||||
Vec3.sum(center, Vec3.multiply(RADIUS, Vec3.RIGHT)),
|
||||
Vec3.sum(center, Vec3.multiply(-RADIUS, Vec3.RIGHT)),
|
||||
];
|
||||
|
||||
// Create spheres around the avatar
|
||||
var spheres = map(positions, getSphere);
|
||||
|
||||
// Toggle sphere growth
|
||||
each(spheres, function(sphere) {
|
||||
var state = {
|
||||
size: SIZE,
|
||||
grow: true, // grow/shrink
|
||||
state: false, // mutate/pause
|
||||
};
|
||||
|
||||
// Grow or shrink on click
|
||||
Script.addEventHandler(sphere, 'clickReleaseOnEntity', toggleGrowth);
|
||||
// TODO: Toggle on collisions as well
|
||||
|
||||
function toggleGrowth() {
|
||||
if (state.state) {
|
||||
stop();
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
var verb = state.grow ? 'grow' : 'shrink';
|
||||
print('starting to '+verb+' '+sphere);
|
||||
|
||||
Script.update.connect(grow);
|
||||
state.state = true;
|
||||
}
|
||||
|
||||
function stop() {
|
||||
print('stopping '+sphere);
|
||||
|
||||
Script.update.disconnect(grow);
|
||||
state.state = false;
|
||||
state.grow = !state.grow;
|
||||
}
|
||||
|
||||
function grow(delta) {
|
||||
if ((state.grow && state.size > SIZE_MAX) || // cannot grow more
|
||||
(!state.grow && state.size < SIZE_MIN)) { // cannot shrink more
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
state.size += (state.grow ? 1 : -1) * delta; // grow/shrink
|
||||
Entities.editEntity(sphere, { dimensions: getDimensions(state.size) });
|
||||
}
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function() { // cleanup
|
||||
each(spheres, function(sphere) {
|
||||
Entities.deleteEntity(sphere);
|
||||
});
|
||||
});
|
||||
|
||||
// NOTE: Everything below this line is a helper.
|
||||
// The basic logic of this example is entirely above this note.
|
||||
|
||||
// Entity helpers
|
||||
|
||||
function getSphere(position) {
|
||||
return Entities.addEntity({
|
||||
type: 'Sphere',
|
||||
position: position,
|
||||
dimensions: getDimensions(SIZE),
|
||||
color: getColor(),
|
||||
gravity: Vec3.ZERO,
|
||||
lifetime: LIFETIME,
|
||||
ignoreCollisions: true,
|
||||
});
|
||||
}
|
||||
|
||||
function getDimensions(size) {
|
||||
return { x: size, y: size, z: size };
|
||||
}
|
||||
|
||||
function getColor() {
|
||||
return { red: getValue(), green: getValue(), blue: getValue() };
|
||||
function getValue() { return Math.floor(Math.random() * 255); }
|
||||
}
|
||||
|
||||
// Functional helpers
|
||||
|
||||
function map(list, iterator) {
|
||||
var result = [];
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
result.push(iterator(list[i], i, list));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function each(list, iterator) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
iterator(list[i], i, list);
|
||||
}
|
||||
}
|
|
@ -70,6 +70,7 @@
|
|||
#include <LogHandler.h>
|
||||
#include <MainWindow.h>
|
||||
#include <MessageDialog.h>
|
||||
#include <MessagesClient.h>
|
||||
#include <ModelEntityItem.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
|
@ -339,6 +340,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<PathUtils>();
|
||||
DependencyManager::set<InterfaceActionFactory>();
|
||||
DependencyManager::set<AssetClient>();
|
||||
DependencyManager::set<MessagesClient>();
|
||||
DependencyManager::set<UserInputMapper>();
|
||||
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
|
||||
return true;
|
||||
|
@ -484,6 +486,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
|
||||
assetThread->start();
|
||||
|
||||
// Setup MessagesClient
|
||||
auto messagesClient = DependencyManager::get<MessagesClient>();
|
||||
QThread* messagesThread = new QThread;
|
||||
messagesThread->setObjectName("Messages Client Thread");
|
||||
messagesClient->moveToThread(messagesThread);
|
||||
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
|
||||
messagesThread->start();
|
||||
|
||||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
|
||||
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||
|
@ -550,7 +560,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
|
||||
// tell the NodeList instance who to tell the domain server we care about
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
||||
<< NodeType::EntityServer << NodeType::AssetServer);
|
||||
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer);
|
||||
|
||||
// connect to the packet sent signal of the _entityEditSender
|
||||
connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);
|
||||
|
|
|
@ -91,7 +91,6 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
|||
}
|
||||
|
||||
if (isMine) {
|
||||
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
||||
auto player = DependencyManager::get<recording::Deck>();
|
||||
// Only use face trackers when not playing back a recording.
|
||||
if (!player->isPlaying()) {
|
||||
|
|
|
@ -379,6 +379,13 @@ void MyAvatar::updateHMDFollowVelocity() {
|
|||
// update sensor to world matrix from current body position and hmd sensor.
|
||||
// This is so the correct camera can be used for rendering.
|
||||
void MyAvatar::updateSensorToWorldMatrix() {
|
||||
|
||||
#ifdef DEBUG_RENDERING
|
||||
// draw marker about avatar's position
|
||||
const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
DebugDraw::getInstance().addMyAvatarMarker("pos", glm::quat(), glm::vec3(), red);
|
||||
#endif
|
||||
|
||||
// update the sensor mat so that the body position will end up in the desired
|
||||
// position when driven from the head.
|
||||
glm::mat4 desiredMat = createMatFromQuatAndPos(getOrientation(), getPosition());
|
||||
|
@ -1859,6 +1866,7 @@ glm::quat MyAvatar::getWorldBodyOrientation() const {
|
|||
return glm::quat_cast(_sensorToWorldMatrix * _bodySensorMatrix);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// derive avatar body position and orientation from the current HMD Sensor location.
|
||||
// results are in sensor space
|
||||
glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
||||
|
@ -1876,6 +1884,66 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
|||
}
|
||||
return glm::mat4();
|
||||
}
|
||||
#else
|
||||
// old school meat hook style
|
||||
glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
||||
|
||||
// HMD is in sensor space.
|
||||
const glm::vec3 hmdPosition = getHMDSensorPosition();
|
||||
const glm::quat hmdOrientation = getHMDSensorOrientation();
|
||||
const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation);
|
||||
|
||||
/*
|
||||
const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f);
|
||||
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f);
|
||||
const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f);
|
||||
const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.0f, 0.0f);
|
||||
*/
|
||||
|
||||
// 2 meter tall dude
|
||||
const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.9f, 0.0f);
|
||||
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.9f, 0.0f);
|
||||
const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.70f, 0.0f);
|
||||
const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.05f, 0.0f);
|
||||
|
||||
vec3 localEyes, localNeck;
|
||||
if (!_debugDrawSkeleton) {
|
||||
const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
localEyes = rotY180 * (((DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) / 2.0f) - DEFAULT_HIPS_POS);
|
||||
localNeck = rotY180 * (DEFAULT_NECK_POS - DEFAULT_HIPS_POS);
|
||||
} else {
|
||||
// TODO: At the moment MyAvatar does not have access to the rig, which has the skeleton, which has the bind poses.
|
||||
// for now use the _debugDrawSkeleton, which is initialized with the same FBX model as the rig.
|
||||
|
||||
// TODO: cache these indices.
|
||||
int rightEyeIndex = _debugDrawSkeleton->nameToJointIndex("RightEye");
|
||||
int leftEyeIndex = _debugDrawSkeleton->nameToJointIndex("LeftEye");
|
||||
int neckIndex = _debugDrawSkeleton->nameToJointIndex("Neck");
|
||||
int hipsIndex = _debugDrawSkeleton->nameToJointIndex("Hips");
|
||||
|
||||
glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS;
|
||||
glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS;
|
||||
glm::vec3 absNeckPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS;
|
||||
glm::vec3 absHipsPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(hipsIndex).trans : DEFAULT_HIPS_POS;
|
||||
|
||||
const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
localEyes = rotY180 * (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos);
|
||||
localNeck = rotY180 * (absNeckPos - absHipsPos);
|
||||
}
|
||||
|
||||
// apply simplistic head/neck model
|
||||
// figure out where the avatar body should be by applying offsets from the avatar's neck & head joints.
|
||||
|
||||
// eyeToNeck offset is relative full HMD orientation.
|
||||
// while neckToRoot offset is only relative to HMDs yaw.
|
||||
glm::vec3 eyeToNeck = hmdOrientation * (localNeck - localEyes);
|
||||
glm::vec3 neckToRoot = hmdOrientationYawOnly * -localNeck;
|
||||
glm::vec3 bodyPos = hmdPosition + eyeToNeck + neckToRoot;
|
||||
|
||||
// avatar facing is determined solely by hmd orientation.
|
||||
return createMatFromQuatAndPos(hmdOrientationYawOnly, bodyPos);
|
||||
}
|
||||
#endif
|
||||
|
||||
glm::vec3 MyAvatar::getPositionForAudio() {
|
||||
switch (_audioListenerMode) {
|
||||
|
|
|
@ -247,7 +247,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
return; // only simulate for own avatar
|
||||
}
|
||||
|
||||
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
||||
auto player = DependencyManager::get<recording::Deck>();
|
||||
if (player->isPlaying()) {
|
||||
return;
|
||||
|
|
|
@ -79,7 +79,7 @@ const QString& AnimSkeleton::getJointName(int jointIndex) const {
|
|||
}
|
||||
|
||||
AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& poses) const {
|
||||
if (jointIndex < 0) {
|
||||
if (jointIndex < 0 || jointIndex >= (int)poses.size() || jointIndex >= (int)_joints.size()) {
|
||||
return AnimPose::identity;
|
||||
} else {
|
||||
return getAbsolutePose(_joints[jointIndex].parentIndex, poses) * poses[jointIndex];
|
||||
|
|
|
@ -23,6 +23,19 @@
|
|||
#include "AnimSkeleton.h"
|
||||
#include "IKTarget.h"
|
||||
|
||||
/*
|
||||
const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f);
|
||||
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f);
|
||||
const glm::vec3 DEFAULT_HEAD_POS(0.0f, 1.55f, 0.0f);
|
||||
const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f);
|
||||
*/
|
||||
|
||||
// 2 meter tall dude
|
||||
const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.9f, 0.0f);
|
||||
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.9f, 0.0f);
|
||||
const glm::vec3 DEFAULT_HEAD_POS(0.0f, 1.75f, 0.0f);
|
||||
const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.70f, 0.0f);
|
||||
|
||||
void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
|
||||
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
|
||||
if (handle->getPriority() > (*it)->getPriority()) {
|
||||
|
@ -410,17 +423,19 @@ void Rig::calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds,
|
|||
void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) {
|
||||
// TODO: use cached eye/hips indices for these calculations
|
||||
int numPoses = poses.size();
|
||||
int rightEyeIndex = _animSkeleton->nameToJointIndex(QString("RightEye"));
|
||||
int leftEyeIndex = _animSkeleton->nameToJointIndex(QString("LeftEye"));
|
||||
if (numPoses > rightEyeIndex && numPoses > leftEyeIndex
|
||||
&& rightEyeIndex > 0 && leftEyeIndex > 0) {
|
||||
int hipsIndex = _animSkeleton->nameToJointIndex(QString("Hips"));
|
||||
int headIndex = _animSkeleton->nameToJointIndex(QString("Head"));
|
||||
if (hipsIndex >= 0 && headIndex > 0) {
|
||||
if (hipsIndex > 0 && headIndex > 0) {
|
||||
int rightEyeIndex = _animSkeleton->nameToJointIndex(QString("RightEye"));
|
||||
int leftEyeIndex = _animSkeleton->nameToJointIndex(QString("LeftEye"));
|
||||
if (numPoses > rightEyeIndex && numPoses > leftEyeIndex && rightEyeIndex > 0 && leftEyeIndex > 0) {
|
||||
glm::vec3 rightEye = _animSkeleton->getAbsolutePose(rightEyeIndex, poses).trans;
|
||||
glm::vec3 leftEye = _animSkeleton->getAbsolutePose(leftEyeIndex, poses).trans;
|
||||
glm::vec3 hips = _animSkeleton->getAbsolutePose(hipsIndex, poses).trans;
|
||||
_eyesInRootFrame = 0.5f * (rightEye + leftEye) - hips;
|
||||
} else {
|
||||
glm::vec3 hips = _animSkeleton->getAbsolutePose(hipsIndex, poses).trans;
|
||||
_eyesInRootFrame = 0.5f * (DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) - hips;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1172,11 +1187,6 @@ static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const A
|
|||
int headIndex = skeleton->nameToJointIndex("Head");
|
||||
int neckIndex = skeleton->nameToJointIndex("Neck");
|
||||
|
||||
const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f);
|
||||
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f);
|
||||
const glm::vec3 DEFAULT_HEAD_POS(0.0f, 1.55f, 0.0f);
|
||||
const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f);
|
||||
|
||||
// Use absolute bindPose positions just in case the relBindPose have rotations we don't expect.
|
||||
glm::vec3 absRightEyePos = rightEyeIndex != -1 ? skeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS;
|
||||
glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? skeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS;
|
||||
|
|
|
@ -909,13 +909,8 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
|
|||
// we don't have an audioPacket yet - set that up now
|
||||
_audioPacket = NLPacket::create(PacketType::MicrophoneAudioWithEcho);
|
||||
}
|
||||
|
||||
// FIXME either discard stereo in the recording or record a stereo flag
|
||||
const int numNetworkBytes = _isStereoInput
|
||||
? AudioConstants::NETWORK_FRAME_BYTES_STEREO
|
||||
: AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
|
||||
const int numNetworkSamples = _isStereoInput
|
||||
? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO
|
||||
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
|
|
|
@ -662,7 +662,7 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
|
|||
// move head forward
|
||||
_particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles;
|
||||
} else {
|
||||
float age = 1.0f - _particleLifetimes[i] / _lifespan; // 0.0 .. 1.0
|
||||
float age = _particleLifetimes[i] / _lifespan; // 0.0 .. 1.0
|
||||
updateRadius(i, age);
|
||||
updateColor(i, age);
|
||||
updateAlpha(i, age);
|
||||
|
|
|
@ -30,6 +30,8 @@ Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) {
|
|||
return Assignment::EntityServerType;
|
||||
case NodeType::AssetServer:
|
||||
return Assignment::AssetServerType;
|
||||
case NodeType::MessagesMixer:
|
||||
return Assignment::MessagesMixerType;
|
||||
default:
|
||||
return Assignment::AllTypes;
|
||||
}
|
||||
|
@ -131,6 +133,8 @@ const char* Assignment::getTypeName() const {
|
|||
return "asset-server";
|
||||
case Assignment::EntityServerType:
|
||||
return "entity-server";
|
||||
case Assignment::MessagesMixerType:
|
||||
return "messages-mixer";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
AvatarMixerType = 1,
|
||||
AgentType = 2,
|
||||
AssetServerType = 3,
|
||||
UNUSED_0 = 4,
|
||||
MessagesMixerType = 4,
|
||||
UNUSED_1 = 5,
|
||||
EntityServerType = 6,
|
||||
AllTypes = 7
|
||||
|
|
|
@ -299,14 +299,17 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiS
|
|||
|
||||
qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode) {
|
||||
Q_ASSERT(!packet->isPartOfMessage());
|
||||
if (!destinationNode.getActiveSocket()) {
|
||||
return 0;
|
||||
}
|
||||
auto activeSocket = destinationNode.getActiveSocket();
|
||||
|
||||
if (activeSocket) {
|
||||
emit dataSent(destinationNode.getType(), packet->getDataSize());
|
||||
destinationNode.recordBytesSent(packet->getDataSize());
|
||||
|
||||
return sendPacket(std::move(packet), *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret());
|
||||
return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret());
|
||||
} else {
|
||||
qDebug() << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
|
||||
|
@ -327,9 +330,8 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const HifiS
|
|||
|
||||
qint64 LimitedNodeList::sendPacketList(NLPacketList& packetList, const Node& destinationNode) {
|
||||
auto activeSocket = destinationNode.getActiveSocket();
|
||||
if (!activeSocket) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (activeSocket) {
|
||||
qint64 bytesSent = 0;
|
||||
auto connectionSecret = destinationNode.getConnectionSecret();
|
||||
|
||||
|
@ -342,6 +344,11 @@ qint64 LimitedNodeList::sendPacketList(NLPacketList& packetList, const Node& des
|
|||
|
||||
emit dataSent(destinationNode.getType(), bytesSent);
|
||||
return bytesSent;
|
||||
} else {
|
||||
qDebug() << "LimitedNodeList::sendPacketList called without active socket for node" << destinationNode
|
||||
<< " - not sending.";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
qint64 LimitedNodeList::sendPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
|
||||
|
@ -372,6 +379,8 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr<NLPacketList> packetList,
|
|||
}
|
||||
|
||||
qint64 LimitedNodeList::sendPacketList(std::unique_ptr<NLPacketList> packetList, const Node& destinationNode) {
|
||||
auto activeSocket = destinationNode.getActiveSocket();
|
||||
if (activeSocket) {
|
||||
// close the last packet in the list
|
||||
packetList->closeCurrentPacket();
|
||||
|
||||
|
@ -381,14 +390,24 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr<NLPacketList> packetList,
|
|||
fillPacketHeader(*nlPacket, destinationNode.getConnectionSecret());
|
||||
}
|
||||
|
||||
return _nodeSocket.writePacketList(std::move(packetList), *destinationNode.getActiveSocket());
|
||||
return _nodeSocket.writePacketList(std::move(packetList), *activeSocket);
|
||||
} else {
|
||||
qCDebug(networking) << "LimitedNodeList::sendPacketList called without active socket for node. Not sending.";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr) {
|
||||
if (overridenSockAddr.isNull() && !destinationNode.getActiveSocket()) {
|
||||
qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node. Not sending.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// use the node's active socket as the destination socket if there is no overriden socket address
|
||||
auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket()
|
||||
: overridenSockAddr;
|
||||
|
||||
return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret());
|
||||
}
|
||||
|
||||
|
@ -511,11 +530,21 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
|||
qCDebug(networking) << "Added" << *newNode;
|
||||
|
||||
emit nodeAdded(newNodePointer);
|
||||
if (newNodePointer->getActiveSocket()) {
|
||||
emit nodeActivated(newNodePointer);
|
||||
} else {
|
||||
connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [=] {
|
||||
emit nodeActivated(newNodePointer);
|
||||
disconnect(newNodePointer.data(), &NetworkPeer::socketActivated, this, 0);
|
||||
});
|
||||
}
|
||||
|
||||
return newNodePointer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::unique_ptr<NLPacket> LimitedNodeList::constructPingPacket(PingType_t pingType) {
|
||||
int packetSize = sizeof(PingType_t) + sizeof(quint64);
|
||||
|
||||
|
|
|
@ -239,6 +239,7 @@ signals:
|
|||
void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID);
|
||||
void nodeAdded(SharedNodePointer);
|
||||
void nodeKilled(SharedNodePointer);
|
||||
void nodeActivated(SharedNodePointer);
|
||||
|
||||
void localSockAddrChanged(const HifiSockAddr& localSockAddr);
|
||||
void publicSockAddrChanged(const HifiSockAddr& publicSockAddr);
|
||||
|
|
108
libraries/networking/src/MessagesClient.cpp
Normal file
108
libraries/networking/src/MessagesClient.cpp
Normal file
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// MessagesClient.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Brad hefta-Gaub on 11/16/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "MessagesClient.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include "NetworkLogging.h"
|
||||
#include "NodeList.h"
|
||||
#include "PacketReceiver.h"
|
||||
|
||||
MessagesClient::MessagesClient() {
|
||||
setCustomDeleter([](Dependency* dependency){
|
||||
static_cast<MessagesClient*>(dependency)->deleteLater();
|
||||
});
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessagesPacket");
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &MessagesClient::handleNodeActivated);
|
||||
}
|
||||
|
||||
void MessagesClient::init() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesClient::handleMessagesPacket(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
|
||||
QByteArray packetData = packetList->getMessage();
|
||||
QBuffer packet{ &packetData };
|
||||
packet.open(QIODevice::ReadOnly);
|
||||
|
||||
quint16 channelLength;
|
||||
packet.read(reinterpret_cast<char*>(&channelLength), sizeof(channelLength));
|
||||
auto channelData = packet.read(channelLength);
|
||||
QString channel = QString::fromUtf8(channelData);
|
||||
|
||||
quint16 messageLength;
|
||||
packet.read(reinterpret_cast<char*>(&messageLength), sizeof(messageLength));
|
||||
auto messageData = packet.read(messageLength);
|
||||
QString message = QString::fromUtf8(messageData);
|
||||
|
||||
emit messageReceived(channel, message, senderNode->getUUID());
|
||||
}
|
||||
|
||||
void MessagesClient::sendMessage(const QString& channel, const QString& message) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer);
|
||||
|
||||
if (messagesMixer) {
|
||||
auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true);
|
||||
|
||||
auto channelUtf8 = channel.toUtf8();
|
||||
quint16 channelLength = channelUtf8.length();
|
||||
packetList->writePrimitive(channelLength);
|
||||
packetList->write(channelUtf8);
|
||||
|
||||
auto messageUtf8 = message.toUtf8();
|
||||
quint16 messageLength = messageUtf8.length();
|
||||
packetList->writePrimitive(messageLength);
|
||||
packetList->write(messageUtf8);
|
||||
|
||||
nodeList->sendPacketList(std::move(packetList), *messagesMixer);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesClient::subscribe(const QString& channel) {
|
||||
_subscribedChannels << channel;
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer);
|
||||
|
||||
if (messagesMixer) {
|
||||
auto packetList = NLPacketList::create(PacketType::MessagesSubscribe, QByteArray(), true, true);
|
||||
packetList->write(channel.toUtf8());
|
||||
nodeList->sendPacketList(std::move(packetList), *messagesMixer);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesClient::unsubscribe(const QString& channel) {
|
||||
_subscribedChannels.remove(channel);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer);
|
||||
|
||||
if (messagesMixer) {
|
||||
auto packetList = NLPacketList::create(PacketType::MessagesUnsubscribe, QByteArray(), true, true);
|
||||
packetList->write(channel.toUtf8());
|
||||
nodeList->sendPacketList(std::move(packetList), *messagesMixer);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesClient::handleNodeActivated(SharedNodePointer node) {
|
||||
if (node->getType() == NodeType::MessagesMixer) {
|
||||
for (const auto& channel : _subscribedChannels) {
|
||||
subscribe(channel);
|
||||
}
|
||||
}
|
||||
}
|
46
libraries/networking/src/MessagesClient.h
Normal file
46
libraries/networking/src/MessagesClient.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// MessagesClient.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by Brad hefta-Gaub on 11/16/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#ifndef hifi_MessagesClient_h
|
||||
#define hifi_MessagesClient_h
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "LimitedNodeList.h"
|
||||
#include "NLPacket.h"
|
||||
#include "Node.h"
|
||||
|
||||
class MessagesClient : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MessagesClient();
|
||||
|
||||
Q_INVOKABLE void init();
|
||||
|
||||
Q_INVOKABLE void sendMessage(const QString& channel, const QString& message);
|
||||
Q_INVOKABLE void subscribe(const QString& channel);
|
||||
Q_INVOKABLE void unsubscribe(const QString& channel);
|
||||
|
||||
signals:
|
||||
void messageReceived(const QString& channel, const QString& message, const QUuid& senderUUID);
|
||||
|
||||
private slots:
|
||||
void handleMessagesPacket(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
||||
void handleNodeActivated(SharedNodePointer node);
|
||||
|
||||
protected:
|
||||
QSet<QString> _subscribedChannels;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -13,3 +13,4 @@
|
|||
|
||||
Q_LOGGING_CATEGORY(networking, "hifi.networking")
|
||||
Q_LOGGING_CATEGORY(asset_client, "hifi.networking.asset_client")
|
||||
Q_LOGGING_CATEGORY(messages_client, "hifi.networking.messages_client")
|
||||
|
|
|
@ -16,5 +16,6 @@
|
|||
|
||||
Q_DECLARE_LOGGING_CATEGORY(networking)
|
||||
Q_DECLARE_LOGGING_CATEGORY(asset_client)
|
||||
Q_DECLARE_LOGGING_CATEGORY(messages_client)
|
||||
|
||||
#endif // hifi_NetworkLogging_h
|
||||
|
|
|
@ -113,6 +113,10 @@ void NetworkPeer::setActiveSocket(HifiSockAddr* discoveredSocket) {
|
|||
|
||||
// we're now considered connected to this peer - reset the number of connection attemps
|
||||
resetConnectionAttempts();
|
||||
|
||||
if (_activeSocket) {
|
||||
emit socketActivated(*_activeSocket);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkPeer::activateLocalSocket() {
|
||||
|
|
|
@ -83,6 +83,7 @@ public slots:
|
|||
void stopPingTimer();
|
||||
signals:
|
||||
void pingTimerTimeout();
|
||||
void socketActivated(const HifiSockAddr& sockAddr);
|
||||
protected:
|
||||
void setActiveSocket(HifiSockAddr* discoveredSocket);
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ void NodeType::init() {
|
|||
TypeNameHash.insert(NodeType::Agent, "Agent");
|
||||
TypeNameHash.insert(NodeType::AudioMixer, "Audio Mixer");
|
||||
TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer");
|
||||
TypeNameHash.insert(NodeType::MessagesMixer, "Messages Mixer");
|
||||
TypeNameHash.insert(NodeType::AssetServer, "Asset Server");
|
||||
TypeNameHash.insert(NodeType::Unassigned, "Unassigned");
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace NodeType {
|
|||
const NodeType_t AudioMixer = 'M';
|
||||
const NodeType_t AvatarMixer = 'W';
|
||||
const NodeType_t AssetServer = 'A';
|
||||
const NodeType_t MessagesMixer = 'm';
|
||||
const NodeType_t Unassigned = 1;
|
||||
|
||||
void init();
|
||||
|
|
|
@ -110,8 +110,12 @@ bool PacketReceiver::registerMessageListener(PacketType type, QObject* listener,
|
|||
|
||||
// add the mapping
|
||||
_packetListListenerMap[type] = ObjectMethodPair(QPointer<QObject>(listener), matchingMethod);
|
||||
|
||||
qCDebug(networking) << "Registering a packet listener for packet list type" << type;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
qCWarning(networking) << "FAILED to Register a packet listener for packet list type" << type;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -352,7 +356,7 @@ void PacketReceiver::handleVerifiedPacketList(std::unique_ptr<udt::PacketList> p
|
|||
}
|
||||
|
||||
} else if (it == _packetListListenerMap.end()) {
|
||||
qCWarning(networking) << "No listener found for packet type" << nlPacketList->getType();
|
||||
qCWarning(networking) << "No listener found for packet list type" << nlPacketList->getType();
|
||||
|
||||
// insert a dummy listener so we don't print this again
|
||||
_packetListListenerMap.insert(nlPacketList->getType(), { nullptr, QMetaMethod() });
|
||||
|
|
|
@ -87,7 +87,10 @@ public:
|
|||
AssetGetInfo,
|
||||
AssetGetInfoReply,
|
||||
DomainDisconnectRequest,
|
||||
DomainServerRemovedNode
|
||||
DomainServerRemovedNode,
|
||||
MessagesData,
|
||||
MessagesSubscribe,
|
||||
MessagesUnsubscribe
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -26,7 +26,8 @@ public:
|
|||
private:
|
||||
virtual FrameConstPointer readFrame(size_t index) const override;
|
||||
QString _name { QUuid().toString() };
|
||||
mutable size_t _frameIndex { 0 };
|
||||
|
||||
//mutable size_t _frameIndex { 0 }; // FIXME - not in use
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <AudioEffectOptions.h>
|
||||
#include <AvatarData.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
#include <MessagesClient.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
@ -367,6 +368,7 @@ void ScriptEngine::init() {
|
|||
registerGlobalObject("Vec3", &_vec3Library);
|
||||
registerGlobalObject("Uuid", &_uuidLibrary);
|
||||
registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
registerGlobalObject("Messages", DependencyManager::get<MessagesClient>().data());
|
||||
qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);
|
||||
qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue);
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ QJsonValue glmToJson(const T& t) {
|
|||
|
||||
template <typename T>
|
||||
T glmFromJson(const QJsonValue& json) {
|
||||
static const T DEFAULT_VALUE = T();
|
||||
T result;
|
||||
if (json.isArray()) {
|
||||
QJsonArray array = json.toArray();
|
||||
|
|
|
@ -167,10 +167,7 @@ void OculusLegacyDisplayPlugin::activate() {
|
|||
}
|
||||
});
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
ovrBool result =
|
||||
#endif
|
||||
ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs);
|
||||
ovrBool result = ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs);
|
||||
Q_ASSERT(result);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue