Merge branch 'master' of https://github.com/highfidelity/hifi into decouple-avatar-updates

This commit is contained in:
Howard Stearns 2015-09-08 16:54:14 -07:00
commit eb67254e54
213 changed files with 9249 additions and 2212 deletions

View file

@ -42,8 +42,8 @@ const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
quint16 assignmentMonitorPort) :
quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
quint16 assignmentServerPort, quint16 assignmentMonitorPort) :
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME)
{
LogUtils::init();
@ -53,7 +53,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
auto addressManager = DependencyManager::set<AddressManager>();
// create a NodeList as an unassigned client, must be after addressManager
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned);
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned, listenPort);
auto animationCache = DependencyManager::set<AnimationCache>();
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>();
@ -95,6 +95,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
}
_assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerPort, true);
_assignmentServerSocket.setObjectName("AssigmentServer");
nodeList->setAssignmentServerSocket(_assignmentServerSocket);
qDebug() << "Assignment server socket is" << _assignmentServerSocket;
@ -119,6 +120,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
// did we get an assignment-client monitor port?
if (assignmentMonitorPort > 0) {
_assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort);
_assignmentClientMonitorSocket.setObjectName("AssignmentClientMonitor");
qDebug() << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket;

View file

@ -23,6 +23,7 @@ class AssignmentClient : public QObject {
Q_OBJECT
public:
AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
quint16 listenPort,
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
quint16 assignmentMonitorPort);
~AssignmentClient();

View file

@ -59,6 +59,10 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
const QCommandLineOption poolOption(ASSIGNMENT_POOL_OPTION, "set assignment pool", "pool-name");
parser.addOption(poolOption);
const QCommandLineOption portOption(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION,
"UDP port for this assignment client (or monitor)", "port");
parser.addOption(portOption);
const QCommandLineOption walletDestinationOption(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION,
"set wallet destination", "wallet-uuid");
@ -158,12 +162,18 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
// check for an overriden assignment server port
quint16 assignmentServerPort = DEFAULT_DOMAIN_SERVER_PORT;
if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) {
assignmentServerPort = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toString().toUInt();
assignmentServerPort = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toUInt();
}
if (parser.isSet(assignmentServerPortOption)) {
assignmentServerPort = parser.value(assignmentServerPortOption).toInt();
}
// check for an overidden listen port
quint16 listenPort = 0;
if (argumentVariantMap.contains(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION)) {
listenPort = argumentVariantMap.value(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION).toUInt();
}
if (parser.isSet(numChildsOption)) {
if (minForks && minForks > numForks) {
@ -185,12 +195,12 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
if (numForks || minForks || maxForks) {
AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks,
requestAssignmentType, assignmentPool,
walletUUID, assignmentServerHostname,
listenPort, walletUUID, assignmentServerHostname,
assignmentServerPort);
monitor->setParent(this);
connect(this, &QCoreApplication::aboutToQuit, monitor, &AssignmentClientMonitor::aboutToQuit);
} else {
AssignmentClient* client = new AssignmentClient(requestAssignmentType, assignmentPool,
AssignmentClient* client = new AssignmentClient(requestAssignmentType, assignmentPool, listenPort,
walletUUID, assignmentServerHostname,
assignmentServerPort, monitorPort);
client->setParent(this);

View file

@ -17,15 +17,15 @@
const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "t";
const QString ASSIGNMENT_POOL_OPTION = "pool";
const QString ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION = "p";
const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet";
const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "a";
const QString CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION = "p";
const QString CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION = "server-port";
const QString ASSIGNMENT_NUM_FORKS_OPTION = "n";
const QString ASSIGNMENT_MIN_FORKS_OPTION = "min";
const QString ASSIGNMENT_MAX_FORKS_OPTION = "max";
const QString ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION = "monitor-port";
class AssignmentClientApp : public QCoreApplication {
Q_OBJECT
public:

View file

@ -28,7 +28,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
const unsigned int minAssignmentClientForks,
const unsigned int maxAssignmentClientForks,
Assignment::Type requestAssignmentType, QString assignmentPool,
QUuid walletUUID, QString assignmentServerHostname,
quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
quint16 assignmentServerPort) :
_numAssignmentClientForks(numAssignmentClientForks),
_minAssignmentClientForks(minAssignmentClientForks),
@ -50,7 +50,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
// create a NodeList so we can receive stats from children
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
auto addressManager = DependencyManager::set<AddressManager>();
auto nodeList = DependencyManager::set<LimitedNodeList>();
auto nodeList = DependencyManager::set<LimitedNodeList>(listenPort);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket");

View file

@ -28,7 +28,7 @@ class AssignmentClientMonitor : public QObject {
public:
AssignmentClientMonitor(const unsigned int numAssignmentClientForks, const unsigned int minAssignmentClientForks,
const unsigned int maxAssignmentClientForks, Assignment::Type requestAssignmentType,
QString assignmentPool, QUuid walletUUID, QString assignmentServerHostname,
QString assignmentPool, quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
quint16 assignmentServerPort);
~AssignmentClientMonitor();

View file

@ -16,6 +16,7 @@
#include "audio/AudioMixer.h"
#include "avatars/AvatarMixer.h"
#include "entities/EntityServer.h"
#include "assets/AssetServer.h"
ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) {
@ -33,6 +34,8 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) {
return new Agent(packet);
case Assignment::EntityServerType:
return new EntityServer(packet);
case Assignment::AssetServerType:
return new AssetServer(packet);
default:
return NULL;
}

View file

@ -0,0 +1,167 @@
//
// AssetServer.cpp
// assignment-client/src/assets
//
// Created by Ryan Huffman on 2015/07/21
// 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 "AssetServer.h"
#include <QBuffer>
#include <QCryptographicHash>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QCoreApplication>
#include <QEventLoop>
#include <QRunnable>
#include <QString>
#include "NetworkLogging.h"
#include "NodeType.h"
#include "SendAssetTask.h"
#include "UploadAssetTask.h"
const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server";
AssetServer::AssetServer(NLPacket& packet) :
ThreadedAssignment(packet),
_taskPool(this)
{
// Most of the work will be I/O bound, reading from disk and constructing packet objects,
// so the ideal is greater than the number of cores on the system.
static const int TASK_POOL_THREAD_COUNT = 50;
_taskPool.setMaxThreadCount(TASK_POOL_THREAD_COUNT);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AssetGet, this, "handleAssetGet");
packetReceiver.registerListener(PacketType::AssetGetInfo, this, "handleAssetGetInfo");
packetReceiver.registerMessageListener(PacketType::AssetUpload, this, "handleAssetUpload");
}
void AssetServer::run() {
ThreadedAssignment::commonInit(ASSET_SERVER_LOGGING_TARGET_NAME, NodeType::AssetServer);
auto nodeList = DependencyManager::get<NodeList>();
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
_resourcesDirectory = QDir(QCoreApplication::applicationDirPath()).filePath("resources/assets");
if (!_resourcesDirectory.exists()) {
qDebug() << "Creating resources directory";
_resourcesDirectory.mkpath(".");
}
qDebug() << "Serving files from: " << _resourcesDirectory.path();
// Scan for new files
qDebug() << "Looking for new files in asset directory";
auto files = _resourcesDirectory.entryInfoList(QDir::Files);
QRegExp filenameRegex { "^[a-f0-9]{" + QString::number(SHA256_HASH_HEX_LENGTH) + "}(\\..+)?$" };
for (const auto& fileInfo : files) {
auto filename = fileInfo.fileName();
if (!filenameRegex.exactMatch(filename)) {
qDebug() << "Found file: " << filename;
if (!fileInfo.isReadable()) {
qDebug() << "\tCan't open file for reading: " << filename;
continue;
}
// Read file
QFile file { fileInfo.absoluteFilePath() };
file.open(QFile::ReadOnly);
QByteArray data = file.readAll();
auto hash = hashData(data);
auto hexHash = hash.toHex();
qDebug() << "\tMoving " << filename << " to " << hexHash;
file.rename(_resourcesDirectory.absoluteFilePath(hexHash) + "." + fileInfo.suffix());
}
}
}
void AssetServer::handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
QByteArray assetHash;
MessageID messageID;
uint8_t extensionLength;
if (packet->getPayloadSize() < qint64(SHA256_HASH_LENGTH + sizeof(messageID) + sizeof(extensionLength))) {
qDebug() << "ERROR bad file request";
return;
}
packet->readPrimitive(&messageID);
assetHash = packet->readWithoutCopy(SHA256_HASH_LENGTH);
packet->readPrimitive(&extensionLength);
QByteArray extension = packet->read(extensionLength);
auto replyPacket = NLPacket::create(PacketType::AssetGetInfoReply);
QByteArray hexHash = assetHash.toHex();
replyPacket->writePrimitive(messageID);
replyPacket->write(assetHash);
QString fileName = QString(hexHash) + "." + extension;
QFileInfo fileInfo { _resourcesDirectory.filePath(fileName) };
if (fileInfo.exists() && fileInfo.isReadable()) {
qDebug() << "Opening file: " << fileInfo.filePath();
replyPacket->writePrimitive(AssetServerError::NoError);
replyPacket->writePrimitive(fileInfo.size());
} else {
qDebug() << "Asset not found: " << QString(hexHash);
replyPacket->writePrimitive(AssetServerError::AssetNotFound);
}
auto nodeList = DependencyManager::get<NodeList>();
nodeList->sendPacket(std::move(replyPacket), *senderNode);
}
void AssetServer::handleAssetGet(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
auto minSize = qint64(sizeof(MessageID) + SHA256_HASH_LENGTH + sizeof(uint8_t) + sizeof(DataOffset) + sizeof(DataOffset));
if (packet->getPayloadSize() < minSize) {
qDebug() << "ERROR bad file request";
return;
}
// Queue task
auto task = new SendAssetTask(packet, senderNode, _resourcesDirectory);
_taskPool.start(task);
}
void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
if (senderNode->getCanRez()) {
qDebug() << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID());
auto task = new UploadAssetTask(packetList, senderNode, _resourcesDirectory);
_taskPool.start(task);
} else {
// this is a node the domain told us is not allowed to rez entities
// for now this also means it isn't allowed to add assets
// so return a packet with error that indicates that
auto permissionErrorPacket = NLPacket::create(PacketType::AssetUploadReply, sizeof(MessageID) + sizeof(AssetServerError));
MessageID messageID;
packetList->readPrimitive(&messageID);
// write the message ID and a permission denied error
permissionErrorPacket->writePrimitive(messageID);
permissionErrorPacket->writePrimitive(AssetServerError::PermissionDenied);
// send off the packet
auto nodeList = DependencyManager::get<NodeList>();
nodeList->sendPacket(std::move(permissionErrorPacket), *senderNode);
}
}

View file

@ -0,0 +1,45 @@
//
// AssetServer.h
// assignment-client/src/assets
//
// Created by Ryan Huffman on 2015/07/21
// 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_AssetServer_h
#define hifi_AssetServer_h
#include <QDir>
#include <ThreadedAssignment.h>
#include <QThreadPool>
#include "AssetUtils.h"
class AssetServer : public ThreadedAssignment {
Q_OBJECT
public:
AssetServer(NLPacket& packet);
public slots:
void run();
private slots:
void handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleAssetGet(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
void handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
private:
static void writeError(NLPacketList* packetList, AssetServerError error);
QDir _resourcesDirectory;
QThreadPool _taskPool;
};
inline void writeError(NLPacketList* packetList, AssetServerError error) {
packetList->writePrimitive(error);
}
#endif

View file

@ -0,0 +1,89 @@
//
// SendAssetTask.cpp
// assignment-client/src/assets
//
// Created by Ryan Huffman on 2015/08/26
// 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 "SendAssetTask.h"
#include <QFile>
#include <DependencyManager.h>
#include <NetworkLogging.h>
#include <NLPacket.h>
#include <NLPacketList.h>
#include <NodeList.h>
#include <udt/Packet.h>
#include "AssetUtils.h"
SendAssetTask::SendAssetTask(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendToNode, const QDir& resourcesDir) :
QRunnable(),
_packet(packet),
_senderNode(sendToNode),
_resourcesDir(resourcesDir)
{
}
void SendAssetTask::run() {
MessageID messageID;
uint8_t extensionLength;
DataOffset start, end;
_packet->readPrimitive(&messageID);
QByteArray assetHash = _packet->read(SHA256_HASH_LENGTH);
_packet->readPrimitive(&extensionLength);
QByteArray extension = _packet->read(extensionLength);
// `start` and `end` indicate the range of data to retrieve for the asset identified by `assetHash`.
// `start` is inclusive, `end` is exclusive. Requesting `start` = 1, `end` = 10 will retrieve 9 bytes of data,
// starting at index 1.
_packet->readPrimitive(&start);
_packet->readPrimitive(&end);
QString hexHash = assetHash.toHex();
qDebug() << "Received a request for the file (" << messageID << "): " << hexHash << " from " << start << " to " << end;
qDebug() << "Starting task to send asset: " << hexHash << " for messageID " << messageID;
auto replyPacketList = std::unique_ptr<NLPacketList>(new NLPacketList(PacketType::AssetGetReply, QByteArray(), true, true));
replyPacketList->write(assetHash);
replyPacketList->writePrimitive(messageID);
if (end <= start) {
writeError(replyPacketList.get(), AssetServerError::InvalidByteRange);
} else {
QString filePath = _resourcesDir.filePath(QString(hexHash) + "." + QString(extension));
QFile file { filePath };
if (file.open(QIODevice::ReadOnly)) {
if (file.size() < end) {
writeError(replyPacketList.get(), AssetServerError::InvalidByteRange);
qCDebug(networking) << "Bad byte range: " << hexHash << " " << start << ":" << end;
} else {
auto size = end - start;
file.seek(start);
replyPacketList->writePrimitive(AssetServerError::NoError);
replyPacketList->writePrimitive(size);
replyPacketList->write(file.read(size));
qCDebug(networking) << "Sending asset: " << hexHash;
}
file.close();
} else {
qCDebug(networking) << "Asset not found: " << filePath << "(" << hexHash << ")";
writeError(replyPacketList.get(), AssetServerError::AssetNotFound);
}
}
auto nodeList = DependencyManager::get<NodeList>();
nodeList->sendPacketList(std::move(replyPacketList), *_senderNode);
}

View file

@ -0,0 +1,38 @@
//
// SendAssetTask.h
// assignment-client/src/assets
//
// Created by Ryan Huffman on 2015/08/26
// 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_SendAssetTask_h
#define hifi_SendAssetTask_h
#include <QtCore/QByteArray>
#include <QtCore/QSharedPointer>
#include <QtCore/QString>
#include <QtCore/QRunnable>
#include "AssetUtils.h"
#include "AssetServer.h"
#include "Node.h"
class NLPacket;
class SendAssetTask : public QRunnable {
public:
SendAssetTask(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendToNode, const QDir& resourcesDir);
void run();
private:
QSharedPointer<NLPacket> _packet;
SharedNodePointer _senderNode;
QDir _resourcesDir;
};
#endif

View file

@ -0,0 +1,80 @@
//
// UploadAssetTask.cpp
// assignment-client/src/assets
//
// Created by Stephen Birarda on 2015-08-28.
// 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 "UploadAssetTask.h"
#include <QtCore/QBuffer>
#include <QtCore/QFile>
#include <AssetUtils.h>
#include <NodeList.h>
#include <NLPacketList.h>
UploadAssetTask::UploadAssetTask(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode,
const QDir& resourcesDir) :
_packetList(packetList),
_senderNode(senderNode),
_resourcesDir(resourcesDir)
{
}
void UploadAssetTask::run() {
auto data = _packetList->getMessage();
QBuffer buffer { &data };
buffer.open(QIODevice::ReadOnly);
MessageID messageID;
buffer.read(reinterpret_cast<char*>(&messageID), sizeof(messageID));
uint8_t extensionLength;
buffer.read(reinterpret_cast<char*>(&extensionLength), sizeof(extensionLength));
QByteArray extension = buffer.read(extensionLength);
uint64_t fileSize;
buffer.read(reinterpret_cast<char*>(&fileSize), sizeof(fileSize));
qDebug() << "UploadAssetTask reading a file of " << fileSize << "bytes and extension" << extension << "from"
<< uuidStringWithoutCurlyBraces(_senderNode->getUUID());
auto replyPacket = NLPacket::create(PacketType::AssetUploadReply);
replyPacket->writePrimitive(messageID);
if (fileSize > MAX_UPLOAD_SIZE) {
replyPacket->writePrimitive(AssetServerError::AssetTooLarge);
} else {
QByteArray fileData = buffer.read(fileSize);
auto hash = hashData(fileData);
auto hexHash = hash.toHex();
qDebug() << "Hash for uploaded file from" << uuidStringWithoutCurlyBraces(_senderNode->getUUID())
<< "is: (" << hexHash << ") ";
QFile file { _resourcesDir.filePath(QString(hexHash)) + "." + QString(extension) };
if (file.exists()) {
qDebug() << "[WARNING] This file already exists: " << hexHash;
} else {
file.open(QIODevice::WriteOnly);
file.write(fileData);
file.close();
}
replyPacket->writePrimitive(AssetServerError::NoError);
replyPacket->write(hash);
}
auto nodeList = DependencyManager::get<NodeList>();
nodeList->sendPacket(std::move(replyPacket), *_senderNode);
}

View file

@ -0,0 +1,37 @@
//
// UploadAssetTask.h
// assignment-client/src/assets
//
// Created by Stephen Birarda on 2015-08-28.
// 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
//
#pragma once
#ifndef hifi_UploadAssetTask_h
#define hifi_UploadAssetTask_h
#include <QtCore/QDir>
#include <QtCore/QObject>
#include <QtCore/QRunnable>
#include <QtCore/QSharedPointer>
class NLPacketList;
class Node;
class UploadAssetTask : public QRunnable {
public:
UploadAssetTask(QSharedPointer<NLPacketList> packetList, QSharedPointer<Node> senderNode, const QDir& resourcesDir);
void run();
private:
QSharedPointer<NLPacketList> _packetList;
QSharedPointer<Node> _senderNode;
QDir _resourcesDir;
};
#endif // hifi_UploadAssetTask_h

View file

@ -96,14 +96,11 @@ AudioMixer::AudioMixer(NLPacket& packet) :
// SOON
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
QSet<PacketType::Value> nodeAudioPackets {
PacketType::MicrophoneAudioNoEcho, PacketType::MicrophoneAudioWithEcho,
PacketType::InjectAudio, PacketType::SilentAudioFrame,
PacketType::AudioStreamStats
};
packetReceiver.registerListenerForTypes(nodeAudioPackets, this, "handleNodeAudioPacket");
packetReceiver.registerListenerForTypes({ PacketType::MicrophoneAudioNoEcho, PacketType::MicrophoneAudioWithEcho,
PacketType::InjectAudio, PacketType::SilentAudioFrame,
PacketType::AudioStreamStats },
this, "handleNodeAudioPacket");
packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket");
}
@ -652,9 +649,6 @@ void AudioMixer::run() {
auto nodeList = DependencyManager::get<NodeList>();
// we do not want this event loop to be the handler for UDP datagrams, so disconnect
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
nodeList->linkedDataCreateCallback = [](Node* node) {
@ -672,7 +666,7 @@ void AudioMixer::run() {
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);

View file

@ -50,7 +50,7 @@ AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() const {
}
int AudioMixerClientData::parseData(NLPacket& packet) {
PacketType::Value packetType = packet.getType();
PacketType packetType = packet.getType();
if (packetType == PacketType::AudioStreamStats) {

View file

@ -18,7 +18,7 @@ AvatarAudioStream::AvatarAudioStream(bool isStereo, const InboundAudioStream::Se
{
}
int AvatarAudioStream::parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
int readBytes = 0;
if (type == PacketType::SilentAudioFrame) {

View file

@ -25,7 +25,7 @@ private:
AvatarAudioStream(const AvatarAudioStream&);
AvatarAudioStream& operator= (const AvatarAudioStream&);
int parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
};
#endif // hifi_AvatarAudioStream_h

View file

@ -259,8 +259,8 @@ void AvatarMixer::broadcastAvatarData() {
return;
}
PacketSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData);
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber();
if (lastSeqToReceiver > lastSeqFromSender) {
// Did we somehow get out of order packets from the sender?
@ -289,7 +289,7 @@ void AvatarMixer::broadcastAvatarData() {
// set the last sent sequence number for this sender on the receiver
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData));
otherNodeData->getLastReceivedSequenceNumber());
// start a new segment in the PacketList for this avatar
avatarPacketList.startSegment();
@ -539,6 +539,7 @@ void AvatarMixer::run() {
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);

View file

@ -14,6 +14,9 @@
#include "AvatarMixerClientData.h"
int AvatarMixerClientData::parseData(NLPacket& packet) {
// pull the sequence number from the data first
packet.readPrimitive(&_lastReceivedSequenceNumber);
// compute the offset to the data payload
return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead()));
}
@ -24,13 +27,13 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() {
return oldValue;
}
PacketSequenceNumber AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
// return the matching PacketSequenceNumber, or the default if we don't have it
auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID);
if (nodeMatch != _lastBroadcastSequenceNumbers.end()) {
return nodeMatch->second;
} else {
return DEFAULT_SEQUENCE_NUMBER;
return 0;
}
}

View file

@ -36,10 +36,12 @@ public:
bool checkAndSetHasReceivedFirstPackets();
PacketSequenceNumber getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, PacketSequenceNumber sequenceNumber)
uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber)
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
@ -77,8 +79,9 @@ public:
void loadJSONStats(QJsonObject& jsonObject) const;
private:
AvatarData _avatar;
std::unordered_map<QUuid, PacketSequenceNumber, UUIDHasher> _lastBroadcastSequenceNumbers;
uint16_t _lastReceivedSequenceNumber { 0 };
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
bool _hasReceivedFirstPackets = false;
quint64 _billboardChangeTimestamp = 0;

View file

@ -22,7 +22,7 @@ public:
OctreeQueryNode(),
_lastDeletedEntitiesSentAt(0) { }
virtual PacketType::Value getMyPacketType() const { return PacketType::EntityData; }
virtual PacketType getMyPacketType() const { return PacketType::EntityData; }
quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; }
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }

View file

@ -28,11 +28,11 @@ public:
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode();
virtual char getMyNodeType() const { return NodeType::EntityServer; }
virtual PacketType::Value getMyQueryMessageType() const { return PacketType::EntityQuery; }
virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; }
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
virtual const char* getMyLoggingServerTargetName() const { return MODEL_SERVER_LOGGING_TARGET_NAME; }
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_MODELS_PERSIST_FILE; }
virtual PacketType::Value getMyEditNackType() const { return PacketType::EntityEditNack; }
virtual PacketType getMyEditNackType() const { return PacketType::EntityEditNack; }
virtual QString getMyDomainSettingsKey() const { return QString("entity_server_settings"); }
// subclass may implement these method

View file

@ -89,7 +89,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
}
// Ask our tree subclass if it can handle the incoming packet...
PacketType::Value packetType = packet->getType();
PacketType packetType = packet->getType();
if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket);
@ -128,7 +128,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
}
if (debugProcessPacket) {
qDebug() << " numBytesPacketHeader=" << packet->totalHeadersSize();
qDebug() << " numBytesPacketHeader=" << NLPacket::totalHeaderSize(packetType);
qDebug() << " sizeof(sequence)=" << sizeof(sequence);
qDebug() << " sizeof(sentAt)=" << sizeof(sentAt);
qDebug() << " atByte (in payload)=" << packet->pos();
@ -150,7 +150,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
if (debugProcessPacket) {
qDebug() << " --- inside while loop ---";
qDebug() << " maxSize=" << maxSize;
qDebug("OctreeInboundPacketProcessor::processPacket() %c "
qDebug("OctreeInboundPacketProcessor::processPacket() %hhu "
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld maxSize=%d",
packetType, packet->getPayload(), packet->getPayloadSize(), editData,
packet->pos(), maxSize);
@ -188,7 +188,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
}
if (debugProcessPacket) {
qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %c "
qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %hhu "
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld",
packetType, packet->getPayload(), packet->getPayloadSize(), editData, packet->pos());
}
@ -207,7 +207,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<NLPacket> packet
}
trackInboundPacket(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime);
} else {
qDebug("unknown packet ignored... packetType=%d", packetType);
qDebug("unknown packet ignored... packetType=%hhu", packetType);
}
}

View file

@ -24,7 +24,7 @@ OctreeQueryNode::OctreeQueryNode() :
_viewSent(false),
_octreePacket(),
_octreePacketWaiting(false),
_lastOctreePayload(new char[MAX_PACKET_SIZE]),
_lastOctreePayload(new char[udt::MAX_PACKET_SIZE]),
_lastOctreePacketLength(0),
_duplicatePacketCount(0),
_firstSuppressedPacket(usecTimestampNow()),

View file

@ -34,7 +34,7 @@ public:
virtual ~OctreeQueryNode();
void init(); // called after creation to set up some virtual items
virtual PacketType::Value getMyPacketType() const = 0;
virtual PacketType getMyPacketType() const = 0;
void resetOctreePacket(); // resets octree packet to after "V" header
@ -150,7 +150,7 @@ private:
quint64 _lastRootTimestamp;
PacketType::Value _myPacketType;
PacketType _myPacketType;
bool _isShuttingDown;
SentPacketHistory _sentPacketHistory;

View file

@ -219,7 +219,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
packetSent = true;
int packetSizeWithHeader = nodeData->getPacket().getDataSize();
thisWastedBytes = MAX_PACKET_SIZE - packetSizeWithHeader;
thisWastedBytes = udt::MAX_PACKET_SIZE - packetSizeWithHeader;
_totalWastedBytes += thisWastedBytes;
_totalBytes += nodeData->getPacket().getDataSize();
_totalPackets++;
@ -251,7 +251,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
packetSent = true;
int packetSizeWithHeader = nodeData->getPacket().getDataSize();
int thisWastedBytes = MAX_PACKET_SIZE - packetSizeWithHeader;
int thisWastedBytes = udt::MAX_PACKET_SIZE - packetSizeWithHeader;
_totalWastedBytes += thisWastedBytes;
_totalBytes += packetSizeWithHeader;
_totalPackets++;
@ -598,7 +598,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
_totalBytes += packet->getDataSize();
_totalPackets++;
_totalWastedBytes += MAX_PACKET_SIZE - packet->getDataSize();
_totalWastedBytes += udt::MAX_PACKET_SIZE - packet->getDataSize();
}
}

View file

@ -63,11 +63,11 @@ public:
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode() = 0;
virtual char getMyNodeType() const = 0;
virtual PacketType::Value getMyQueryMessageType() const = 0;
virtual PacketType getMyQueryMessageType() const = 0;
virtual const char* getMyServerName() const = 0;
virtual const char* getMyLoggingServerTargetName() const = 0;
virtual const char* getMyDefaultPersistFilename() const = 0;
virtual PacketType::Value getMyEditNackType() const = 0;
virtual PacketType getMyEditNackType() const = 0;
virtual QString getMyDomainSettingsKey() const { return QString("octree_server_settings"); }
// subclass may implement these method

View file

@ -73,7 +73,6 @@ endfunction()
macro(AUTOSCRIBE_SHADER_LIB)
file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
foreach(HIFI_LIBRARY ${ARGN})
#if (NOT TARGET ${HIFI_LIBRARY})

View file

@ -9,8 +9,8 @@
macro(SETUP_HIFI_LIBRARY)
project(${TARGET_NAME})
project(${TARGET_NAME})
# grab the implemenation and header files
file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c")
list(APPEND ${TARGET_NAME}_SRCS ${LIB_SRCS})
@ -33,5 +33,8 @@ macro(SETUP_HIFI_LIBRARY)
foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES})
target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})
endforeach()
# Don't make scribed shaders cumulative
set(AUTOSCRIBE_SHADER_LIB_SRC "")
endmacro(SETUP_HIFI_LIBRARY)

View file

@ -166,6 +166,21 @@
}
]
},
{
"name": "asset_server",
"label": "Asset Server",
"assignment-types": [3],
"settings": [
{
"name": "enabled",
"type": "checkbox",
"label": "Enabled",
"help": "Assigns an asset-server in your domain to serve files to clients via the ATP protocol (over UDP)",
"default": false,
"advanced": true
}
]
},
{
"name": "audio_env",
"label": "Audio Environment",

View file

@ -48,7 +48,8 @@ QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID
}
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
<< NodeType::AvatarMixer << NodeType::EntityServer;
<< NodeType::AvatarMixer << NodeType::EntityServer
<< NodeType::AssetServer;
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> packet) {
if (packet->getPayloadSize() == 0) {
@ -65,6 +66,16 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<NLPacket> pack
return;
}
static const NodeSet VALID_NODE_TYPES {
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent
};
if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) {
qDebug() << "Received an invalid node type with connect request. Will not allow connection from"
<< nodeConnection.senderSockAddr;
return;
}
// check if this connect request matches an assignment in the queue
auto pendingAssignment = _pendingAssignedNodes.find(nodeConnection.connectUUID);

View file

@ -108,6 +108,11 @@ DomainServer::DomainServer(int argc, char* argv[]) :
}
}
DomainServer::~DomainServer() {
// destroy the LimitedNodeList before the DomainServer QCoreApplication is down
DependencyManager::destroy<LimitedNodeList>();
}
void DomainServer::aboutToQuit() {
// clear the log handler so that Qt doesn't call the destructor on LogHandler
@ -257,7 +262,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
auto nodeList = DependencyManager::set<LimitedNodeList>(domainServerPort, domainServerDTLSPort);
// no matter the local port, save it to shared mem so that local assignment clients can ask what it is
nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this, nodeList->getNodeSocket().localPort());
nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this, nodeList->getSocketLocalPort());
// store our local http ports in shared memory
quint16 localHttpPort = DOMAIN_SERVER_HTTP_PORT;
@ -280,10 +285,15 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
// register as the packet receiver for the types we want
PacketReceiver& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket");
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
// NodeList won't be available to the settings manager when it is created, so call registerListener here
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
// register the gatekeeper for the packets it needs to receive
packetReceiver.registerListener(PacketType::DomainConnectRequest, &_gatekeeper, "processConnectRequestPacket");
packetReceiver.registerListener(PacketType::ICEPing, &_gatekeeper, "processICEPingPacket");
packetReceiver.registerListener(PacketType::ICEPingReply, &_gatekeeper, "processICEPingReplyPacket");
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_gatekeeper, "processICEPeerInformationPacket");
@ -561,8 +571,19 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
if (!excludedTypes.contains(defaultedType)
&& defaultedType != Assignment::UNUSED_0
&& defaultedType != Assignment::UNUSED_1
&& defaultedType != Assignment::UNUSED_2
&& defaultedType != Assignment::AgentType) {
if (defaultedType == Assignment::AssetServerType) {
// Make sure the asset-server is enabled before adding it here.
// Initially we do not assign it by default so we can test it in HF domains first
static const QString ASSET_SERVER_ENABLED_KEYPATH = "asset_server.enabled";
if (!_settingsManager.valueOrDefaultValueForKeyPath(ASSET_SERVER_ENABLED_KEYPATH).toBool()) {
// skip to the next iteration if asset-server isn't enabled
continue;
}
}
// 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);

View file

@ -39,7 +39,8 @@ class DomainServer : public QCoreApplication, public HTTPSRequestHandler {
Q_OBJECT
public:
DomainServer(int argc, char* argv[]);
~DomainServer();
static int const EXIT_CODE_REBOOT;
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);

View file

@ -22,6 +22,7 @@
#include <Assignment.h>
#include <HifiConfigVariantMap.h>
#include <HTTPConnection.h>
#include <NLPacketList.h>
#include "DomainServerSettingsManager.h"
@ -66,6 +67,21 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
}
void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<NLPacket> packet) {
Assignment::Type type;
packet->readPrimitive(&type);
QJsonObject responseObject = responseObjectForType(QString::number(type));
auto json = QJsonDocument(responseObject).toJson();
auto packetList = std::unique_ptr<NLPacketList>(new NLPacketList(PacketType::DomainSettings, QByteArray(), true, true));
packetList->write(json);
auto nodeList = DependencyManager::get<LimitedNodeList>();
nodeList->sendPacketList(std::move(packetList), packet->getSenderSockAddr());
}
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
_configMap.loadMasterAndUserConfig(argumentList);

View file

@ -18,6 +18,8 @@
#include <HifiConfigVariantMap.h>
#include <HTTPManager.h>
#include <NLPacket.h>
const QString SETTINGS_PATHS_KEY = "paths";
const QString SETTINGS_PATH = "/settings";
@ -38,6 +40,10 @@ public:
QVariantMap& getUserSettingsMap() { return _configMap.getUserConfig(); }
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
private slots:
void processSettingsRequestPacket(QSharedPointer<NLPacket> packet);
private:
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false);
void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant);

View file

@ -11,7 +11,8 @@
//
Script.include("../../libraries/utils.js");
Script.include("../libraries/utils.js");
var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK");
var rightTriggerAction = RIGHT_HAND_CLICK;
@ -41,7 +42,7 @@ var INTERSECT_COLOR = {
blue: 10
};
var GRAB_RADIUS = 0.5;
var GRAB_RADIUS = 1.0;
var GRAB_COLOR = {
red: 250,
@ -135,7 +136,8 @@ controller.prototype.checkForIntersections = function(origin, direction) {
var intersection = Entities.findRayIntersection(pickRay, true);
if (intersection.intersects && intersection.properties.collisionsWillMove === 1) {
this.distanceToEntity = Vec3.distance(origin, intersection.properties.position);
var handPosition = Controller.getSpatialControlPosition(this.palm);
this.distanceToEntity = Vec3.distance(handPosition, intersection.properties.position);
Entities.editEntity(this.pointer, {
linePoints: [
ZERO_VEC,
@ -236,19 +238,24 @@ controller.prototype.update = function() {
controller.prototype.grabEntity = function() {
var handRotation = this.getHandRotation();
var handPosition = this.getHandPosition();
var objectRotation = Entities.getEntityProperties(this.grabbedEntity).rotation;
var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
var objectPosition = Entities.getEntityProperties(this.grabbedEntity).position;
var offset = Vec3.subtract(objectPosition, handPosition);
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
this.closeGrabbing = true;
//check if our entity has instructions on how to be grabbed, otherwise, just use default relative position and rotation
var userData = getEntityUserData(this.grabbedEntity);
var relativePosition = ZERO_VEC;
var relativeRotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
if(userData.spatialKey) {
if(userData.spatialKey.relativePosition) {
relativePosition = userData.spatialKey.relativePosition;
}
if(userData.spatialKey.relativeRotation) {
relativeRotation = userData.spatialKey.relativeRotation;
}
}
this.actionID = Entities.addAction("hold", this.grabbedEntity, {
relativePosition: offsetPosition,
relativeRotation: offsetRotation,
hand: this.hand,
timeScale: 0.05
timeScale: 0.05,
relativePosition: relativePosition,
relativeRotation: relativeRotation
});
}
@ -305,10 +312,10 @@ controller.prototype.onActionEvent = function(action, state) {
self.checkForEntityArrival = true;
}, 500);
var handPosition = Controller.getSpatialControlPosition(this.palm);
var direction = Controller.getSpatialControlNormal(this.tip);
var direction = Vec3.normalize(Controller.getSpatialControlNormal(this.tip));
//move final destination along line a bit, so it doesnt hit avatar hand
Entities.updateAction(this.grabbedEntity, this.actionID, {
targetPosition: Vec3.sum(handPosition, Vec3.multiply(2, direction))
targetPosition: Vec3.sum(handPosition, Vec3.multiply(3, direction))
});
}
}
@ -339,8 +346,6 @@ function cleanup() {
leftController.cleanup();
}
Script.scriptEnding.connect(cleanup);
Script.update.connect(update)
Controller.actionEvent.connect(onActionEvent);

View file

@ -0,0 +1,126 @@
//
// boombox.js
// examples/entityScripts
//
// Created by Brad Hefta-Gaub on 9/3/15.
// Copyright 2015 High Fidelity, Inc.
//
// This is an example of a boom box entity script which when assigned to an entity, will detect when the entity is being touched by the avatars hands
// and start to play a boom box song
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
Script.include("../libraries/utils.js");
var _this;
var BOOMBOX_USER_DATA_KEY = "boombox";
var THE_SONG = "http://hifi-public.s3.amazonaws.com/ryan/freaks7.wav";
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
DetectTouched = function() {
_this = this;
_this.beingTouched = false;
_this.isPlaying = false;
_this.injector = null;
};
DetectTouched.prototype = {
// update() will be called regulary, because we've hooked the update signal in our preload() function.
// we will check the avatars hand positions and if either hand is in our bounding box, we will notice that
update: function() {
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
var entityID = _this.entityID;
var leftHandPosition = MyAvatar.getLeftPalmPosition();
var rightHandPosition = MyAvatar.getRightPalmPosition();
var props = Entities.getEntityProperties(entityID);
var entityMinPoint = props.boundingBox.brn;
var entityMaxPoint = props.boundingBox.tfl;
// this is our assumed boombox data if it's not known
var defaultBoomboxData = { isPlaying: false, avatarID: null };
// this handy function getEntityCustomData() is available in utils.js and it will return just the specific section
// of user data we asked for. If it's not available it returns our default data.
var boomboxData = getEntityCustomData(BOOMBOX_USER_DATA_KEY, entityID, defaultBoomboxData);
// Only allow interaction if no one else is interacting with the boombox or if we are...
if (!boomboxData.isPlaying || boomboxData.avatarID == MyAvatar.sessionUUID) {
// If we were not previously being touched, and we just got touched, then we do our touching action.
if (pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint) || pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint)) {
if (!_this.beingTouched) {
print("I was just touched...");
// remember we're being grabbed so we can detect being released
_this.beingTouched = true;
// determine what to do based on if we are currently playing
if (!_this.isPlaying) {
// if we are not currently playing, then start playing.
print("Start playing...");
_this.isPlaying = true;
if (_this.injector == null) {
_this.injector = Audio.playSound(_this.song, {
position: props.position, // position of boombox entity
volume: 0.5,
loop: true
});
} else {
_this.injector.restart();
}
setEntityCustomData(BOOMBOX_USER_DATA_KEY, entityID, { isPlaying: true, avatarID: MyAvatar.sessionUUID });
} else {
// if we are currently playing, then stop playing
print("Stop playing...");
_this.isPlaying = false;
_this.injector.stop();
setEntityCustomData(BOOMBOX_USER_DATA_KEY, entityID, { isPlaying: false, avatarID: null });
} // endif (!isPlaying)
} // endif !beingTouched
} else if (_this.beingTouched) {
// if we are not being grabbed, and we previously were, then we were just released, remember that
// and print out a message
_this.beingTouched = false;
print("I'm am no longer being touched...");
}
} else {
// end if for -- only interact if no one else is...
print("someone else is playing the boombox...");
}
},
// preload() will be called when the entity has become visible (or known) to the interface
// it gives us a chance to set our local JavaScript object up. In this case it means:
// * remembering our entityID, so we can access it in cases where we're called without an entityID
// * connecting to the update signal so we can check our grabbed state
preload: function(entityID) {
print("preload!");
this.entityID = entityID;
Script.update.connect(this.update);
this.song = SoundCache.getSound(THE_SONG);
},
// unload() will be called when our entity is no longer available. It may be because we were deleted,
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
// to the update signal
unload: function(entityID) {
Script.update.disconnect(this.update);
},
};
// entity scripts always need to return a newly constructed object of our type
return new DetectTouched();
})

View file

@ -1,16 +1,6 @@
//
// sprayPaintCan.js
// examples/entityScripts
//
// Created by Eric Levin on 9/3/15
// Copyright 2015 High Fidelity, Inc.
//
// This is an example of an entity script for painting with a spraypaint can
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
(function() {
Script.include("../libraries/utils.js");
SPATIAL_USER_DATA_KEY = "spatialKey";
this.userData = {};
var TIP_OFFSET_Z = 0.14;
@ -36,6 +26,8 @@
});
this.getUserData = function() {
if (this.properties.userData) {
this.userData = JSON.parse(this.properties.userData);
}
@ -50,8 +42,7 @@
this.update = function(deltaTime) {
self.properties = Entities.getEntityProperties(self.entityId);
self.getUserData();
//Only run the logic if this is the client whose avatar is grabbing
if (self.userData.grabKey && self.userData.grabKey.activated === true && self.userData.grabKey.avatarId === MyAvatar.sessionUUID) {
if (self.userData.grabKey && self.userData.grabKey.activated === true) {
if (self.activated !== true) {
Entities.editEntity(self.paintStream, {
animationSettings: startSetting
@ -70,6 +61,8 @@
this.sprayStream = function() {
var forwardVec = Quat.getFront(self.properties.rotation);
forwardVec = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 90, 0), forwardVec);
var upVec = Quat.getUp(self.properties.rotation);
var position = Vec3.sum(self.properties.position, Vec3.multiply(forwardVec, TIP_OFFSET_Z));
position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y))
@ -92,11 +85,12 @@
var normal = Vec3.multiply(-1, Quat.getFront(intersection.properties.rotation));
this.paint(intersection.intersection, normal);
}
}
this.paint = function(position, normal) {
if (!this.painting) {
print("position " + JSON.stringify(position))
this.newStroke(position);
this.painting = true;
@ -125,6 +119,7 @@
strokeWidths: this.strokeWidths
});
}
this.newStroke = function(position) {
@ -157,10 +152,16 @@
this.entityId = entityId;
this.properties = Entities.getEntityProperties(self.entityId);
this.getUserData();
print("USER DATA " + JSON.stringify(this.userData))
if (this.userData.activated) {
if (this.userData.grabKey && this.userData.grabKey.activated) {
this.activated = true;
}
if(!this.userData.spatialKey) {
var data = {
relativePosition: {x: 0, y: 0, z: 0},
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0,0)
}
setEntityCustomData(SPATIAL_USER_DATA_KEY, this.entityId, data);
}
this.initialize();
}
@ -173,6 +174,7 @@
running: false
});
this.paintStream = Entities.addEntity({
type: "ParticleEffect",
animationSettings: animationSettings,

View file

@ -0,0 +1,6 @@
{
"ProceduralEntity":
{
"shaderUrl": "https://s3.amazonaws.com/Oculus/shadertoys/relentlessSkybox.fs"
}
}

View file

@ -0,0 +1,144 @@
// srtuss, 2013
// collecting some design ideas for a new game project.
// no raymarching is used.
// if i could add a custom soundtrack, it'd use this one (essential for desired sensation)
// http://www.youtube.com/watch?v=1uFAu65tZpo
//#define GREEN_VERSION
// ** improved camera shaking
// ** cleaned up code
// ** added stuff to the gates
// *******************************************************************************************
// Please do NOT use this shader in your own productions/videos/games without my permission!
// If you'd still like to do so, please drop me a mail (stral@aon.at)
// *******************************************************************************************
float time = iGlobalTime;
vec2 rotate(vec2 p, float a) {
return vec2(p.x * cos(a) - p.y * sin(a), p.x * sin(a) + p.y * cos(a));
}
float box(vec2 p, vec2 b, float r) {
return length(max(abs(p) - b, 0.0)) - r;
}
// iq's ray-plane-intersection code
vec3 intersect(in vec3 o, in vec3 d, vec3 c, vec3 u, vec3 v)
{
vec3 q = o - c;
return vec3(
dot(cross(u, v), q),
dot(cross(q, u), d),
dot(cross(v, q), d)) / dot(cross(v, u), d);
}
// some noise functions for fast developing
float rand11(float p) {
return fract(sin(p * 591.32) * 43758.5357);
}
float rand12(vec2 p) {
return fract(sin(dot(p.xy, vec2(12.9898, 78.233))) * 43758.5357);
}
vec2 rand21(float p) {
return fract(vec2(sin(p * 591.32), cos(p * 391.32)));
}
vec2 rand22(in vec2 p)
{
return fract(vec2(sin(p.x * 591.32 + p.y * 154.077), cos(p.x * 391.32 + p.y * 49.077)));
}
float noise11(float p) {
float fl = floor(p);
return mix(rand11(fl), rand11(fl + 1.0), fract(p)); //smoothstep(0.0, 1.0, fract(p)));
}
float fbm11(float p) {
return noise11(p) * 0.5 + noise11(p * 2.0) * 0.25 + noise11(p * 5.0) * 0.125;
}
vec3 noise31(float p) {
return vec3(noise11(p), noise11(p + 18.952), noise11(p - 11.372)) * 2.0 - 1.0;
}
// something that looks a bit like godrays coming from the surface
float sky(vec3 p) {
float a = atan(p.x, p.z);
float t = time * 0.1;
float v = rand11(floor(a * 4.0 + t)) * 0.5 + rand11(floor(a * 8.0 - t)) * 0.25
+ rand11(floor(a * 16.0 + t)) * 0.125;
return v;
}
vec3 voronoi(in vec2 x)
{
vec2 n = floor(x); // grid cell id
vec2 f = fract(x);// grid internal position
vec2 mg;// shortest distance...
vec2 mr;// ..and second shortest distance
float md = 8.0, md2 = 8.0;
for(int j = -1; j <= 1; j ++)
{
for(int i = -1; i <= 1; i ++)
{
vec2 g = vec2(float(i), float(j)); // cell id
vec2 o = rand22(n + g);// offset to edge point
vec2 r = g + o - f;
float d = max(abs(r.x), abs(r.y));// distance to the edge
if(d < md)
{ md2 = md; md = d; mr = r; mg = g;}
else if(d < md2)
{ md2 = d;}
}
}
return vec3(n + mg, md2 - md);
}
vec3 getSkyboxColor() {
vec3 rd = normalize(_normal);
vec3 ro = vec3(0, 0, 1);
float inten = 0.0;
// background
float sd = dot(rd, vec3(0.0, 1.0, 0.0));
inten = pow(1.0 - abs(sd), 20.0) + pow(sky(rd), 5.0) * step(0.0, rd.y) * 0.2;
vec3 its;
float v, g;
// voronoi floor layers
for(int i = 0; i < 4; i ++)
{
float layer = float(i);
its = intersect(ro, rd, vec3(0.0, -5.0 - layer * 5.0, 0.0), vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0));
if(its.x > 0.0)
{
vec3 vo = voronoi((its.yz) * 0.05 + 8.0 * rand21(float(i)));
v = exp(-100.0 * (vo.z - 0.02));
float fx = 0.0;
// add some special fx to lowest layer
if(i == 3)
{
float crd = 0.0; //fract(time * 0.2) * 50.0 - 25.0;
float fxi = cos(vo.x * 0.2 + time * 1.5);//abs(crd - vo.x);
fx = clamp(smoothstep(0.9, 1.0, fxi), 0.0, 0.9) * 1.0 * rand12(vo.xy);
fx *= exp(-3.0 * vo.z) * 2.0;
}
inten += v * 0.1 + fx;
}
}
inten *= 0.4 + (sin(time) * 0.5 + 0.5) * 0.6;
vec3 ct = vec3(0.6, 0.8, 9.0);
// find a color for the computed intensity
vec3 col = pow(vec3(inten), ct);
return col;
}

View file

@ -34,8 +34,12 @@ IceServer::IceServer(int argc, char* argv[]) :
qDebug() << "monitoring http endpoint is listening on " << ICE_SERVER_MONITORING_PORT;
_serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT);
// call our process datagrams slot when the UDP socket has packets ready
connect(&_serverSocket, &QUdpSocket::readyRead, this, &IceServer::processDatagrams);
// set processPacket as the verified packet callback for the udt::Socket
_serverSocket.setPacketHandler([this](std::unique_ptr<udt::Packet> packet) { processPacket(std::move(packet)); });
// set packetVersionMatch as the verify packet operator for the udt::Socket
using std::placeholders::_1;
_serverSocket.setPacketFilterOperator(std::bind(&IceServer::packetVersionMatch, this, _1));
// setup our timer to clear inactive peers
QTimer* inactivePeerTimer = new QTimer(this);
@ -44,67 +48,69 @@ IceServer::IceServer(int argc, char* argv[]) :
}
void IceServer::processDatagrams() {
HifiSockAddr sendingSockAddr;
while (_serverSocket.hasPendingDatagrams()) {
// setup a buffer to read the packet into
int packetSizeWithHeader = _serverSocket.pendingDatagramSize();
auto buffer = std::unique_ptr<char[]>(new char[packetSizeWithHeader]);
_serverSocket.readDatagram(buffer.get(), packetSizeWithHeader,
sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer());
bool IceServer::packetVersionMatch(const udt::Packet& packet) {
PacketType headerType = NLPacket::typeInHeader(packet);
PacketVersion headerVersion = NLPacket::versionInHeader(packet);
if (headerVersion == versionForPacketType(headerType)) {
return true;
} else {
qDebug() << "Packet version mismatch for packet" << headerType
<< "(" << nameForPacketType(headerType) << ") from" << packet.getSenderSockAddr();
// make sure that this packet at least looks like something we can read
if (packetSizeWithHeader >= Packet::localHeaderSize(PacketType::ICEServerHeartbeat)) {
return false;
}
}
void IceServer::processPacket(std::unique_ptr<udt::Packet> packet) {
auto nlPacket = NLPacket::fromBase(std::move(packet));
// make sure that this packet at least looks like something we can read
if (nlPacket->getPayloadSize() >= NLPacket::localHeaderSize(PacketType::ICEServerHeartbeat)) {
if (nlPacket->getType() == PacketType::ICEServerHeartbeat) {
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*nlPacket);
auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, sendingSockAddr);
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
peer->activateMatchingOrNewSymmetricSocket(nlPacket->getSenderSockAddr());
} else if (nlPacket->getType() == PacketType::ICEServerQuery) {
QDataStream heartbeatStream(nlPacket.get());
PacketType::Value packetType = packet->getType();
// this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer?
QUuid senderUUID;
heartbeatStream >> senderUUID;
if (packetType == PacketType::ICEServerHeartbeat) {
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*packet);
// pull the public and private sock addrs for this peer
HifiSockAddr publicSocket, localSocket;
heartbeatStream >> publicSocket >> localSocket;
// check if this node also included a UUID that they would like to connect to
QUuid connectRequestID;
heartbeatStream >> connectRequestID;
SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID);
if (matchingPeer) {
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
peer->activateMatchingOrNewSymmetricSocket(sendingSockAddr);
} else if (packetType == PacketType::ICEServerQuery) {
QDataStream heartbeatStream(packet.get());
qDebug() << "Sending information for peer" << connectRequestID << "to peer" << senderUUID;
// this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer?
QUuid senderUUID;
heartbeatStream >> senderUUID;
// we have the peer they want to connect to - send them pack the information for that peer
sendPeerInformationPacket(*matchingPeer, &nlPacket->getSenderSockAddr());
// pull the public and private sock addrs for this peer
HifiSockAddr publicSocket, localSocket;
heartbeatStream >> publicSocket >> localSocket;
// we also need to send them to the active peer they are hoping to connect to
// create a dummy peer object we can pass to sendPeerInformationPacket
// check if this node also included a UUID that they would like to connect to
QUuid connectRequestID;
heartbeatStream >> connectRequestID;
SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID);
if (matchingPeer) {
qDebug() << "Sending information for peer" << connectRequestID << "to peer" << senderUUID;
// we have the peer they want to connect to - send them pack the information for that peer
sendPeerInformationPacket(*(matchingPeer.data()), &sendingSockAddr);
// we also need to send them to the active peer they are hoping to connect to
// create a dummy peer object we can pass to sendPeerInformationPacket
NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket);
sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket());
} else {
qDebug() << "Peer" << senderUUID << "asked for" << connectRequestID << "but no matching peer found";
}
NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket);
sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket());
} else {
qDebug() << "Peer" << senderUUID << "asked for" << connectRequestID << "but no matching peer found";
}
}
}
}
SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(Packet& packet) {
SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(NLPacket& packet) {
// pull the UUID, public and private sock addrs for this peer
QUuid senderUUID;
@ -137,14 +143,13 @@ SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(Packet& packet) {
}
void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) {
auto peerPacket = Packet::create(PacketType::ICEServerPeerInformation);
auto peerPacket = NLPacket::create(PacketType::ICEServerPeerInformation);
// get the byte array for this peer
peerPacket->write(peer.toByteArray());
// write the current packet
_serverSocket.writeDatagram(peerPacket->getData(), peerPacket->getDataSize(),
destinationSockAddr->getAddress(), destinationSockAddr->getPort());
_serverSocket.writePacket(*peerPacket, *destinationSockAddr);
}
void IceServer::clearInactivePeers() {

View file

@ -19,7 +19,8 @@
#include <NetworkPeer.h>
#include <HTTPConnection.h>
#include <HTTPManager.h>
#include <udt/Packet.h>
#include <NLPacket.h>
#include <udt/Socket.h>
typedef QHash<QUuid, SharedNetworkPeer> NetworkPeerHash;
@ -29,15 +30,16 @@ public:
IceServer(int argc, char* argv[]);
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
private slots:
void processDatagrams();
void clearInactivePeers();
private:
SharedNetworkPeer addOrUpdateHeartbeatingPeer(Packet& incomingPacket);
bool packetVersionMatch(const udt::Packet& packet);
void processPacket(std::unique_ptr<udt::Packet> packet);
SharedNetworkPeer addOrUpdateHeartbeatingPeer(NLPacket& incomingPacket);
void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr);
QUuid _id;
QUdpSocket _serverSocket;
udt::Socket _serverSocket;
NetworkPeerHash _activePeers;
HTTPManager _httpManager;
};

View file

@ -113,7 +113,7 @@ endif()
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
# link required hifi libraries
link_hifi_libraries(shared octree environment gpu model render fbx networking entities avatars
link_hifi_libraries(shared octree environment gpu gpu-networking procedural model render fbx networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer ui auto-updater
plugins display-plugins input-plugins)

View file

@ -28,24 +28,24 @@ Item {
anchors.fill: parent
onClicked: { root.expanded = !root.expanded; }
}
Column {
id: generalCol
spacing: 4; x: 4; y: 4;
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Servers: " + root.serverCount
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Avatars: " + root.avatarCount
text: "Avatars: " + root.avatarCount
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Framerate: " + root.framerate
text: "Framerate: " + root.framerate
}
Text {
color: root.fontColor;
@ -60,12 +60,12 @@ Item {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount
text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2)
text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2)
}
}
}
@ -82,30 +82,35 @@ Item {
Column {
id: pingCol
spacing: 4; x: 4; y: 4;
Text {
Text {
color: root.fontColor
font.pixelSize: root.fontSize
text: "Audio ping: " + root.audioPing
text: "Audio ping: " + root.audioPing
}
Text {
Text {
color: root.fontColor
font.pixelSize: root.fontSize
text: "Avatar ping: " + root.avatarPing
text: "Avatar ping: " + root.avatarPing
}
Text {
Text {
color: root.fontColor
font.pixelSize: root.fontSize
text: "Entities avg ping: " + root.entitiesPing
text: "Entities avg ping: " + root.entitiesPing
}
Text {
Text {
color: root.fontColor
font.pixelSize: root.fontSize
visible: root.expanded;
text: "Asset ping: " + root.assetPing
}
Text {
color: root.fontColor
font.pixelSize: root.fontSize
visible: root.expanded;
text: "Voxel max ping: " + 0
}
}
}
Rectangle {
width: geoCol.width + 8
height: geoCol.height + 8
@ -117,34 +122,34 @@ Item {
Column {
id: geoCol
spacing: 4; x: 4; y: 4;
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Position: " + root.position.x.toFixed(1) + ", " +
root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1)
text: "Position: " + root.position.x.toFixed(1) + ", " +
root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1)
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Velocity: " + root.velocity.toFixed(1)
text: "Velocity: " + root.velocity.toFixed(1)
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Yaw: " + root.yaw.toFixed(1)
text: "Yaw: " + root.yaw.toFixed(1)
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded;
text: "Avatar Mixer: " + root.avatarMixerKbps + " kbps, " +
root.avatarMixerPps + "pps";
visible: root.expanded;
text: "Avatar Mixer: " + root.avatarMixerKbps + " kbps, " +
root.avatarMixerPps + "pps";
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded;
text: "Downloads: ";
visible: root.expanded;
text: "Downloads: ";
}
}
}
@ -159,72 +164,72 @@ Item {
Column {
id: octreeCol
spacing: 4; x: 4; y: 4;
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
text: "Triangles: " + root.triangles +
" / Quads: " + root.quads + " / Material Switches: " + root.materialSwitches
text: "Triangles: " + root.triangles +
" / Quads: " + root.quads + " / Material Switches: " + root.materialSwitches
}
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded;
text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque +
" / Translucent: " + root.meshTranslucent;
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded;
text: "\tOpaque considered: " + root.opaqueConsidered +
" / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall;
text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque +
" / Translucent: " + root.meshTranslucent;
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: !root.expanded
text: "Octree Elements Server: " + root.serverElements +
" Local: " + root.localElements;
visible: root.expanded;
text: "\tOpaque considered: " + root.opaqueConsidered +
" / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall;
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded
text: "Octree Sending Mode: " + root.sendingMode;
visible: !root.expanded
text: "Octree Elements Server: " + root.serverElements +
" Local: " + root.localElements;
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded
text: "Octree Packets to Process: " + root.packetStats;
visible: root.expanded
text: "Octree Sending Mode: " + root.sendingMode;
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded
text: "Octree Elements - ";
visible: root.expanded
text: "Octree Packets to Process: " + root.packetStats;
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded
text: "\tServer: " + root.serverElements +
" Internal: " + root.serverInternal +
" Leaves: " + root.serverLeaves;
visible: root.expanded
text: "Octree Elements - ";
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded
text: "\tLocal: " + root.localElements +
" Internal: " + root.localInternal +
" Leaves: " + root.localLeaves;
visible: root.expanded
text: "\tServer: " + root.serverElements +
" Internal: " + root.serverInternal +
" Leaves: " + root.serverLeaves;
}
Text {
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded
text: "LOD: " + root.lodStatus;
visible: root.expanded
text: "\tLocal: " + root.localElements +
" Internal: " + root.localInternal +
" Leaves: " + root.localLeaves;
}
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded
text: "LOD: " + root.lodStatus;
}
}
}

View file

@ -52,6 +52,7 @@
#include <AccountManager.h>
#include <AddressManager.h>
#include <AssetClient.h>
#include <ApplicationVersion.h>
#include <CursorManager.h>
#include <AudioInjector.h>
@ -310,6 +311,7 @@ bool setupEssentials(int& argc, char** argv) {
auto autoUpdater = DependencyManager::set<AutoUpdater>();
auto pathUtils = DependencyManager::set<PathUtils>();
auto actionFactory = DependencyManager::set<InterfaceActionFactory>();
auto assetClient = DependencyManager::set<AssetClient>();
auto userInputMapper = DependencyManager::set<UserInputMapper>();
return true;
@ -456,6 +458,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
audioThread->start();
QThread* assetThread = new QThread;
assetThread->setObjectName("Asset Thread");
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->moveToThread(assetThread);
assetThread->start();
const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
@ -485,8 +496,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
connect(nodeList.data(), &NodeList::uuidChanged, _myAvatar, &MyAvatar::setSessionUUID);
connect(nodeList.data(), &NodeList::uuidChanged, this, &Application::setSessionUUID);
connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);
connect(&nodeList->getPacketReceiver(), &PacketReceiver::packetVersionMismatch,
this, &Application::notifyPacketVersionMismatch);
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch);
// connect to appropriate slots on AccountManager
AccountManager& accountManager = AccountManager::getInstance();
@ -526,7 +536,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// tell the NodeList instance who to tell the domain server we care about
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
<< NodeType::EntityServer);
<< NodeType::EntityServer << NodeType::AssetServer);
// connect to the packet sent signal of the _entityEditSender
connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);
@ -877,6 +887,12 @@ Application::~Application() {
DependencyManager::destroy<GeometryCache>();
DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<SoundCache>();
// cleanup the AssetClient thread
QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
DependencyManager::destroy<AssetClient>();
assetThread->quit();
assetThread->wait();
QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
@ -886,7 +902,7 @@ Application::~Application() {
// ask the node thread to quit and wait until it is done
nodeThread->quit();
nodeThread->wait();
Leapmotion::destroy();
RealSense::destroy();
ConnexionClient::getInstance().destroy();
@ -2040,6 +2056,7 @@ void Application::sendPingPackets() {
case NodeType::AvatarMixer:
case NodeType::AudioMixer:
case NodeType::EntityServer:
case NodeType::AssetServer:
return true;
default:
return false;
@ -3028,7 +3045,7 @@ int Application::sendNackPackets() {
return packetsSent;
}
void Application::queryOctree(NodeType_t serverType, PacketType::Value packetType, NodeToJurisdictionMap& jurisdictions) {
void Application::queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions) {
//qCDebug(interfaceapp) << ">>> inside... queryOctree()... _viewFrustum.getFieldOfView()=" << _viewFrustum.getFieldOfView();
bool wantExtraDebugging = getLogger()->extraDebugging();
@ -3797,6 +3814,9 @@ void Application::nodeAdded(SharedNodePointer node) {
if (node->getType() == NodeType::AvatarMixer) {
// new avatar mixer, send off our identity packet right away
_myAvatar->sendIdentityPacket();
} else if (node->getType() == NodeType::AssetServer) {
// the addition of an asset-server always re-enables the upload to asset server menu option
Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(true);
}
}
@ -3844,6 +3864,10 @@ void Application::nodeKilled(SharedNodePointer node) {
} else if (node->getType() == NodeType::AvatarMixer) {
// our avatar mixer has gone away - clear the hash of avatars
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
} else if (node->getType() == NodeType::AssetServer
&& !DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AssetServer)) {
// this was our last asset server - disable the menu option to upload an asset
Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(false);
}
}

View file

@ -499,7 +499,7 @@ private:
void renderLookatIndicator(glm::vec3 pointOfInterest);
void queryOctree(NodeType_t serverType, PacketType::Value packetType, NodeToJurisdictionMap& jurisdictions);
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
glm::vec3 getSunDirection();

View file

@ -22,7 +22,6 @@
#include <UserActivityLogger.h>
#include <VrMenu.h>
#include "Application.h"
#include "AccountManager.h"
#include "audio/AudioScope.h"
@ -33,13 +32,15 @@
#include "devices/3DConnexionClient.h"
#include "MainWindow.h"
#include "scripting/MenuScriptingInterface.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h"
#endif
#include "ui/AssetUploadDialogFactory.h"
#include "ui/DialogsManager.h"
#include "ui/StandAloneJSConsole.h"
#include "InterfaceLogging.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h"
#endif
#include "Menu.h"
Menu* Menu::_instance = NULL;
@ -353,7 +354,21 @@ Menu::Menu() {
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
0, // QML Qt::SHIFT | Qt::Key_L,
dialogsManager.data(), SLOT(lodTools()));
MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets");
auto& assetDialogFactory = AssetUploadDialogFactory::getInstance();
assetDialogFactory.setParent(this);
QAction* assetUpload = addActionToQMenuAndActionHash(assetDeveloperMenu,
MenuOption::UploadAsset,
0,
&assetDialogFactory,
SLOT(showDialog()));
// disable the asset upload action by default - it gets enabled only if asset server becomes present
assetUpload->setEnabled(false);
MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar");
MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking");

View file

@ -288,6 +288,7 @@ namespace MenuOption {
const QString ToolWindow = "Tool Window";
const QString TransmitterDrive = "Transmitter Drive";
const QString TurnWithHead = "Turn using Head";
const QString UploadAsset = "Upload File to Asset Server";
const QString UseAudioForMouth = "Use Audio for Mouth";
const QString UseCamera = "Use Camera";
const QString VelocityFilter = "Velocity Filter";

View file

@ -19,12 +19,8 @@
OctreePacketProcessor::OctreePacketProcessor() {
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
QSet<PacketType::Value> types {
PacketType::OctreeStats, PacketType::EntityData,
PacketType::EntityErase, PacketType::OctreeStats
};
packetReceiver.registerDirectListenerForTypes(types, this, "handleOctreePacket");
packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
this, "handleOctreePacket");
}
void OctreePacketProcessor::handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
@ -44,7 +40,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer<NLPacket> packet, Share
Application* app = Application::getInstance();
bool wasStatsPacket = false;
PacketType::Value octreePacketType = packet->getType();
PacketType octreePacketType = packet->getType();
// note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA
// immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first
@ -68,17 +64,17 @@ void OctreePacketProcessor::processPacket(QSharedPointer<NLPacket> packet, Share
}
} // fall through to piggyback message
PacketType::Value packetType = packet->getType();
PacketType packetType = packet->getType();
// check version of piggyback packet against expected version
if (packet->getVersion() != versionForPacketType(packet->getType())) {
static QMultiMap<QUuid, PacketType::Value> versionDebugSuppressMap;
static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;
const QUuid& senderUUID = packet->getSourceID();
if (!versionDebugSuppressMap.contains(senderUUID, packetType)) {
qDebug() << "Packet version mismatch on" << packetType << "- Sender"
<< senderUUID << "sent" << (int) packetType << "but"
qDebug() << "OctreePacketProcessor - piggyback packet version mismatch on" << packetType << "- Sender"
<< senderUUID << "sent" << (int) packet->getVersion() << "but"
<< (int) versionForPacketType(packetType) << "expected.";
emit packetVersionMismatch();

View file

@ -14,8 +14,7 @@
#include <OffscreenQmlDialog.h>
class AddressBarDialog : public OffscreenQmlDialog
{
class AddressBarDialog : public OffscreenQmlDialog {
Q_OBJECT
HIFI_QML_DECL
Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged)

View file

@ -0,0 +1,159 @@
//
// AssetUploadDialogFactory.cpp
// interface/src/ui
//
// Created by Stephen Birarda on 2015-08-26.
// 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 "AssetUploadDialogFactory.h"
#include <AssetClient.h>
#include <AssetUpload.h>
#include <AssetUtils.h>
#include <NodeList.h>
#include <QtCore/QDebug>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QLabel>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QVBoxLayout>
AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() {
static AssetUploadDialogFactory staticInstance;
return staticInstance;
}
AssetUploadDialogFactory::AssetUploadDialogFactory() {
}
static const QString PERMISSION_DENIED_ERROR = "You do not have permission to upload content to this asset-server.";
void AssetUploadDialogFactory::showDialog() {
auto nodeList = DependencyManager::get<NodeList>();
if (nodeList->getThisNodeCanRez()) {
auto filename = QFileDialog::getOpenFileUrl(_dialogParent, "Select a file to upload");
if (!filename.isEmpty()) {
qDebug() << "Selected filename for upload to asset-server: " << filename;
auto assetClient = DependencyManager::get<AssetClient>();
auto upload = assetClient->createUpload(filename.path());
if (upload) {
// connect to the finished signal so we know when the AssetUpload is done
QObject::connect(upload, &AssetUpload::finished, this, &AssetUploadDialogFactory::handleUploadFinished);
// start the upload now
upload->start();
} else {
// show a QMessageBox to say that there is no local asset server
QString messageBoxText = QString("Could not upload \n\n%1\n\nbecause you are currently not connected" \
" to a local asset-server.").arg(QFileInfo(filename.toString()).fileName());
QMessageBox::information(_dialogParent, "Failed to Upload", messageBoxText);
}
}
} else {
// we don't have permission to upload to asset server in this domain - show the permission denied error
showErrorDialog(QString(), PERMISSION_DENIED_ERROR);
}
}
void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const QString& hash) {
if (upload->getResult() == AssetUpload::Success) {
// show message box for successful upload, with copiable text for ATP hash
QDialog* hashCopyDialog = new QDialog(_dialogParent);
// delete the dialog on close
hashCopyDialog->setAttribute(Qt::WA_DeleteOnClose);
// set the window title
hashCopyDialog->setWindowTitle(tr("Successful Asset Upload"));
// setup a layout for the contents of the dialog
QVBoxLayout* boxLayout = new QVBoxLayout;
// set the label text (this shows above the text box)
QLabel* lineEditLabel = new QLabel;
lineEditLabel->setText(QString("ATP URL for %1").arg(QFileInfo(upload->getFilename()).fileName()));
// setup the line edit to hold the copiable text
QLineEdit* lineEdit = new QLineEdit;
QString atpURL = QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension());
// set the ATP URL as the text value so it's copiable
lineEdit->insert(atpURL);
// figure out what size this line edit should be using font metrics
QFontMetrics textMetrics { lineEdit->font() };
// set the fixed width on the line edit
// pad it by 10 to cover the border and some extra space on the right side (for clicking)
static const int LINE_EDIT_RIGHT_PADDING { 10 };
lineEdit->setFixedWidth(textMetrics.width(atpURL) + LINE_EDIT_RIGHT_PADDING );
// left align the ATP URL line edit
lineEdit->home(true);
// add the label and line edit to the dialog
boxLayout->addWidget(lineEditLabel);
boxLayout->addWidget(lineEdit);
// setup an OK button to close the dialog
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
connect(buttonBox, &QDialogButtonBox::accepted, hashCopyDialog, &QDialog::close);
boxLayout->addWidget(buttonBox);
// set the new layout on the dialog
hashCopyDialog->setLayout(boxLayout);
// show the new dialog
hashCopyDialog->show();
} else {
// figure out the right error message for the message box
QString additionalError;
switch (upload->getResult()) {
case AssetUpload::PermissionDenied:
additionalError = PERMISSION_DENIED_ERROR;
break;
case AssetUpload::TooLarge:
additionalError = "The uploaded content was too large and could not be stored in the asset-server.";
break;
case AssetUpload::ErrorLoadingFile:
additionalError = "The file could not be opened. Please check your permissions and try again.";
break;
default:
// not handled, do not show a message box
return;
}
// display a message box with the error
showErrorDialog(QFileInfo(upload->getFilename()).fileName(), additionalError);
}
upload->deleteLater();
}
void AssetUploadDialogFactory::showErrorDialog(const QString& filename, const QString& additionalError) {
QString errorMessage;
if (!filename.isEmpty()) {
errorMessage += QString("Failed to upload %1.\n\n").arg(filename);
}
errorMessage += additionalError;
QMessageBox::warning(_dialogParent, "Failed Upload", errorMessage);
}

View file

@ -0,0 +1,42 @@
//
// AssetUploadDialogFactory.h
// interface/src/ui
//
// Created by Stephen Birarda on 2015-08-26.
// 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
//
#pragma once
#ifndef hifi_AssetUploadDialogFactory_h
#define hifi_AssetUploadDialogFactory_h
#include <QtCore/QObject>
class AssetUpload;
class AssetUploadDialogFactory : public QObject {
Q_OBJECT
public:
AssetUploadDialogFactory(const AssetUploadDialogFactory& other) = delete;
AssetUploadDialogFactory& operator=(const AssetUploadDialogFactory& rhs) = delete;
static AssetUploadDialogFactory& getInstance();
void setDialogParent(QWidget* dialogParent) { _dialogParent = dialogParent; }
public slots:
void showDialog();
private slots:
void handleUploadFinished(AssetUpload* upload, const QString& hash);
private:
AssetUploadDialogFactory();
void showErrorDialog(const QString& filename, const QString& additionalError);
QWidget* _dialogParent { nullptr };
};
#endif // hifi_AssetUploadDialogFactory_h

View file

@ -89,10 +89,11 @@ BandwidthDialog::BandwidthDialog(QWidget* parent) :
_allChannelDisplays[4] = _otherChannelDisplay =
new BandwidthChannelDisplay({NodeType::Unassigned}, form, "Other", "Kbps", 1.0, COLOR2);
_allChannelDisplays[5] = _totalChannelDisplay =
new BandwidthChannelDisplay({NodeType::DomainServer, NodeType::EntityServer,
NodeType::EnvironmentServer, NodeType::AudioMixer, NodeType::Agent,
NodeType::AvatarMixer, NodeType::Unassigned},
form, "Total", "Kbps", 1.0, COLOR2);
new BandwidthChannelDisplay({
NodeType::DomainServer, NodeType::EntityServer,
NodeType::AudioMixer, NodeType::Agent,
NodeType::AvatarMixer, NodeType::Unassigned
}, form, "Total", "Kbps", 1.0, COLOR2);
connect(averageUpdateTimer, SIGNAL(timeout()), this, SLOT(updateTimerTimeout()));
averageUpdateTimer->start(1000);

View file

@ -1,6 +1,6 @@
//
// DialogsManager.cpp
//
// interface/src/ui
//
// Created by Clement on 1/18/15.
// Copyright 2015 High Fidelity, Inc.

View file

@ -1,6 +1,6 @@
//
// DialogsManager.h
//
// interface/src/ui
//
// Created by Clement on 1/18/15.
// Copyright 2015 High Fidelity, Inc.

View file

@ -16,8 +16,7 @@
#include <OffscreenQmlDialog.h>
class LoginDialog : public OffscreenQmlDialog
{
class LoginDialog : public OffscreenQmlDialog {
Q_OBJECT
HIFI_QML_DECL

View file

@ -127,8 +127,10 @@ void Stats::updateStats() {
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer);
STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1);
STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1);
STAT_UPDATE(assetPing, assetServerNode ? assetServerNode->getPingMs() : -1);
//// Now handle entity servers, since there could be more than one, we average their ping times
int totalPingOctree = 0;

View file

@ -40,6 +40,7 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, audioPing, 0)
STATS_PROPERTY(int, avatarPing, 0)
STATS_PROPERTY(int, entitiesPing, 0)
STATS_PROPERTY(int, assetPing, 0)
STATS_PROPERTY(QVector3D, position, QVector3D(0, 0, 0) )
STATS_PROPERTY(float, velocity, 0)
STATS_PROPERTY(float, yaw, 0)
@ -107,6 +108,7 @@ signals:
void audioPingChanged();
void avatarPingChanged();
void entitiesPingChanged();
void assetPingChanged();
void positionChanged();
void velocityChanged();
void yawChanged();

View file

@ -381,8 +381,8 @@ AnimNodeLoader::AnimNodeLoader(const QUrl& url) :
_resource(nullptr) {
_resource = new Resource(url);
connect(_resource, SIGNAL(loaded(QNetworkReply&)), SLOT(onRequestDone(QNetworkReply&)));
connect(_resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(onRequestError(QNetworkReply::NetworkError)));
connect(_resource, &Resource::loaded, this, &AnimNodeLoader::onRequestDone);
connect(_resource, &Resource::failed, this, &AnimNodeLoader::onRequestError);
}
AnimNode::Pointer AnimNodeLoader::load(const QByteArray& contents, const QUrl& jsonUrl) {
@ -420,8 +420,8 @@ AnimNode::Pointer AnimNodeLoader::load(const QByteArray& contents, const QUrl& j
return loadNode(rootVal.toObject(), jsonUrl);
}
void AnimNodeLoader::onRequestDone(QNetworkReply& request) {
auto node = load(request.readAll(), _url);
void AnimNodeLoader::onRequestDone(const QByteArray& data) {
auto node = load(data, _url);
if (node) {
emit success(node);
} else {

View file

@ -36,7 +36,7 @@ protected:
static AnimNode::Pointer load(const QByteArray& contents, const QUrl& jsonUrl);
protected slots:
void onRequestDone(QNetworkReply& request);
void onRequestDone(const QByteArray& data);
void onRequestError(QNetworkReply::NetworkError error);
protected:

View file

@ -39,14 +39,16 @@ QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url, const Q
return QSharedPointer<Resource>(new Animation(url), &Resource::allReferencesCleared);
}
AnimationReader::AnimationReader(const QUrl& url, QNetworkReply* reply) :
Animation::Animation(const QUrl& url) : Resource(url) {}
AnimationReader::AnimationReader(const QUrl& url, const QByteArray& data) :
_url(url),
_reply(reply) {
_data(data) {
}
void AnimationReader::run() {
try {
if (!_reply) {
if (_data.isEmpty()) {
throw QString("Reply is NULL ?!");
}
QString urlname = _url.path().toLower();
@ -58,7 +60,7 @@ void AnimationReader::run() {
// Parse the FBX directly from the QNetworkReply
FBXGeometry* fbxgeo = nullptr;
if (_url.path().toLower().endsWith(".fbx")) {
fbxgeo = readFBX(_reply, QVariantHash(), _url.path());
fbxgeo = readFBX(_data, QVariantHash(), _url.path());
} else {
QString errorStr("usupported format");
emit onError(299, errorStr);
@ -71,11 +73,8 @@ void AnimationReader::run() {
} catch (const QString& error) {
emit onError(299, error);
}
_reply->deleteLater();
}
Animation::Animation(const QUrl& url) : Resource(url) {}
bool Animation::isLoaded() const {
return _loaded && _geometry;
}
@ -108,9 +107,9 @@ const QVector<FBXAnimationFrame>& Animation::getFramesReference() const {
return _geometry->animationFrames;
}
void Animation::downloadFinished(QNetworkReply* reply) {
void Animation::downloadFinished(const QByteArray& data) {
// parse the animation/fbx file on a background thread.
AnimationReader* animationReader = new AnimationReader(reply->url(), reply);
AnimationReader* animationReader = new AnimationReader(_url, data);
connect(animationReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(animationParseSuccess(FBXGeometry*)));
connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString)));
QThreadPool::globalInstance()->start(animationReader);

View file

@ -65,7 +65,7 @@ public:
const QVector<FBXAnimationFrame>& getFramesReference() const;
protected:
virtual void downloadFinished(QNetworkReply* reply);
virtual void downloadFinished(const QByteArray& data) override;
protected slots:
void animationParseSuccess(FBXGeometry* geometry);
@ -81,7 +81,7 @@ class AnimationReader : public QObject, public QRunnable {
Q_OBJECT
public:
AnimationReader(const QUrl& url, QNetworkReply* reply);
AnimationReader(const QUrl& url, const QByteArray& data);
virtual void run();
signals:
@ -90,7 +90,7 @@ signals:
private:
QUrl _url;
QNetworkReply* _reply;
QByteArray _data;
};
class AnimationDetails {

View file

@ -164,7 +164,7 @@ int InboundAudioStream::parseData(NLPacket& packet) {
return packet.pos();
}
int InboundAudioStream::parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
int InboundAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
if (type == PacketType::SilentAudioFrame) {
quint16 numSilentSamples = 0;
memcpy(&numSilentSamples, packetAfterSeqNum.constData(), sizeof(quint16));
@ -177,7 +177,7 @@ int InboundAudioStream::parseStreamProperties(PacketType::Value type, const QByt
}
}
int InboundAudioStream::parseAudioData(PacketType::Value type, const QByteArray& packetAfterStreamProperties, int numAudioSamples) {
int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int numAudioSamples) {
return _ringBuffer.writeData(packetAfterStreamProperties.data(), numAudioSamples * sizeof(int16_t));
}

View file

@ -194,11 +194,11 @@ protected:
/// parses the info between the seq num and the audio data in the network packet and calculates
/// how many audio samples this packet contains (used when filling in samples for dropped packets).
/// default implementation assumes no stream properties and raw audio samples after stream propertiess
virtual int parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& networkSamples);
virtual int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& networkSamples);
/// parses the audio data in the network packet.
/// default implementation assumes packet contains raw audio samples after stream properties
virtual int parseAudioData(PacketType::Value type, const QByteArray& packetAfterStreamProperties, int networkSamples);
virtual int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int networkSamples);
/// writes silent samples to the buffer that may be dropped to reduce latency caused by the buffer
virtual int writeDroppableSilentSamples(int silentSamples);

View file

@ -30,7 +30,7 @@ InjectedAudioStream::InjectedAudioStream(const QUuid& streamIdentifier, const bo
const uchar MAX_INJECTOR_VOLUME = 255;
int InjectedAudioStream::parseStreamProperties(PacketType::Value type,
int InjectedAudioStream::parseStreamProperties(PacketType type,
const QByteArray& packetAfterSeqNum,
int& numAudioSamples) {
// setup a data stream to read from this packet

View file

@ -31,7 +31,7 @@ private:
InjectedAudioStream& operator= (const InjectedAudioStream&);
AudioStreamStats getAudioStreamStats() const;
int parseStreamProperties(PacketType::Value type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
const QUuid _streamIdentifier;
float _radius;

View file

@ -42,7 +42,7 @@ int MixedProcessedAudioStream::writeLastFrameRepeatedWithFade(int samples) {
return deviceSamplesWritten;
}
int MixedProcessedAudioStream::parseAudioData(PacketType::Value type, const QByteArray& packetAfterStreamProperties, int networkSamples) {
int MixedProcessedAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int networkSamples) {
emit addedStereoSamples(packetAfterStreamProperties);

View file

@ -35,7 +35,7 @@ public:
protected:
int writeDroppableSilentSamples(int silentSamples);
int writeLastFrameRepeatedWithFade(int samples);
int parseAudioData(PacketType::Value type, const QByteArray& packetAfterStreamProperties, int networkSamples);
int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int networkSamples);
private:
int networkToDeviceSamples(int networkSamples);

View file

@ -56,16 +56,17 @@ Sound::Sound(const QUrl& url, bool isStereo) :
}
void Sound::downloadFinished(QNetworkReply* reply) {
void Sound::downloadFinished(const QByteArray& data) {
// replace our byte array with the downloaded data
QByteArray rawAudioByteArray = reply->readAll();
QString fileName = reply->url().fileName();
QByteArray rawAudioByteArray = QByteArray(data);
QString fileName = getURL().fileName();
const QString WAV_EXTENSION = ".wav";
if (reply->hasRawHeader("Content-Type") || fileName.endsWith(WAV_EXTENSION)) {
if (fileName.endsWith(WAV_EXTENSION)) {
QByteArray headerContentType = reply->rawHeader("Content-Type");
QString headerContentType = "audio/x-wav";
//QByteArray headerContentType = reply->rawHeader("Content-Type");
// WAV audio file encountered
if (headerContentType == "audio/x-wav"
@ -80,9 +81,9 @@ void Sound::downloadFinished(QNetworkReply* reply) {
} else {
// check if this was a stereo raw file
// since it's raw the only way for us to know that is if the file was called .stereo.raw
if (reply->url().fileName().toLower().endsWith("stereo.raw")) {
if (fileName.toLower().endsWith("stereo.raw")) {
_isStereo = true;
qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << reply->url() << "as stereo audio file.";
qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file.";
}
// Process as RAW file
@ -94,7 +95,6 @@ void Sound::downloadFinished(QNetworkReply* reply) {
}
_isReady = true;
reply->deleteLater();
}
void Sound::downSample(const QByteArray& rawAudioByteArray) {

View file

@ -39,7 +39,7 @@ private:
void downSample(const QByteArray& rawAudioByteArray);
void interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray);
virtual void downloadFinished(QNetworkReply* reply);
virtual void downloadFinished(const QByteArray& data) override;
};
typedef QSharedPointer<Sound> SharedSoundPointer;

View file

@ -201,8 +201,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
_headData->_isFaceTrackerConnected = true;
}
QByteArray avatarDataByteArray;
avatarDataByteArray.resize(MAX_PACKET_SIZE);
QByteArray avatarDataByteArray(udt::MAX_PACKET_SIZE, 0);
unsigned char* destinationBuffer = reinterpret_cast<unsigned char*>(avatarDataByteArray.data());
unsigned char* startPosition = destinationBuffer;
@ -1139,14 +1138,17 @@ void AvatarData::setJointMappingsFromNetworkReply() {
void AvatarData::sendAvatarDataPacket() {
auto nodeList = DependencyManager::get<NodeList>();
// about 2% of the time, we send a full update (meaning, we transmit all the joint data), even if nothing has changed.
// this is to guard against a joint moving once, the packet getting lost, and the joint never moving again.
bool sendFullUpdate = randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO;
QByteArray avatarByteArray = toByteArray(true, sendFullUpdate);
doneEncoding(true);
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size());
static AvatarDataSequenceNumber sequenceNumber = 0;
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size() + sizeof(sequenceNumber));
avatarPacket->writePrimitive(sequenceNumber++);
avatarPacket->write(avatarByteArray);
nodeList->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);

View file

@ -59,9 +59,11 @@ typedef unsigned long long quint64;
#include "Recorder.h"
#include "Referential.h"
typedef std::shared_ptr<AvatarData> AvatarSharedPointer;
typedef std::weak_ptr<AvatarData> AvatarWeakPointer;
typedef QHash<QUuid, AvatarSharedPointer> AvatarHash;
using AvatarSharedPointer = std::shared_ptr<AvatarData>;
using AvatarWeakPointer = std::weak_ptr<AvatarData>;
using AvatarHash = QHash<QUuid, AvatarSharedPointer>;
using AvatarDataSequenceNumber = uint16_t;
// avatar motion behaviors
const quint32 AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED = 1U << 0;

View file

@ -26,4 +26,4 @@ find_package(PolyVox REQUIRED)
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES})
link_hifi_libraries(shared gpu script-engine render render-utils)
link_hifi_libraries(shared gpu gpu-networking procedural script-engine render render-utils)

View file

@ -22,6 +22,7 @@
#include <PerfStat.h>
#include <SceneScriptingInterface.h>
#include <ScriptEngine.h>
#include <procedural/Procedural.h>
#include <TextureCache.h>
#include "EntityTreeRenderer.h"
@ -454,13 +455,24 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
_viewState->endOverrideEnvironmentData();
auto stage = scene->getSkyStage();
if (zone->getBackgroundMode() == BACKGROUND_MODE_SKYBOX) {
stage->getSkybox()->setColor(zone->getSkyboxProperties().getColorVec3());
auto skybox = stage->getSkybox();
skybox->setColor(zone->getSkyboxProperties().getColorVec3());
static QString userData;
if (userData != zone->getUserData()) {
userData = zone->getUserData();
QSharedPointer<Procedural> procedural(new Procedural(userData));
if (procedural->_enabled) {
skybox->setProcedural(procedural);
} else {
skybox->setProcedural(QSharedPointer<Procedural>());
}
}
if (zone->getSkyboxProperties().getURL().isEmpty()) {
stage->getSkybox()->setCubemap(gpu::TexturePointer());
skybox->setCubemap(gpu::TexturePointer());
} else {
// Update the Texture of the Skybox with the one pointed by this zone
auto cubeMap = DependencyManager::get<TextureCache>()->getTexture(zone->getSkyboxProperties().getURL(), CUBE_TEXTURE);
stage->getSkybox()->setCubemap(cubeMap->getGPUTexture());
skybox->setCubemap(cubeMap->getGPUTexture());
}
stage->setBackgroundMode(model::SunSkyStage::SKY_BOX);
} else {

View file

@ -43,8 +43,8 @@ public:
virtual ~EntityTreeRenderer();
virtual char getMyNodeType() const { return NodeType::EntityServer; }
virtual PacketType::Value getMyQueryMessageType() const { return PacketType::EntityQuery; }
virtual PacketType::Value getExpectedPacketType() const { return PacketType::EntityData; }
virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; }
virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; }
virtual void renderElement(OctreeElement* element, RenderArgs* args);
virtual float getSizeScale() const;
virtual int getBoundaryLevelAdjust() const;

View file

@ -21,6 +21,8 @@
#include <PerfStat.h>
#include "RenderableDebugableEntityItem.h"
#include "../render-utils/simple_vert.h"
#include "../render-utils/simple_frag.h"
EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return std::make_shared<RenderableBoxEntityItem>(entityID, properties);
@ -42,11 +44,18 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
if (!_procedural) {
_procedural.reset(new ProceduralInfo(this));
_procedural.reset(new Procedural(this->getUserData()));
_procedural->_vertexSource = simple_vert;
_procedural->_fragmentSource = simple_frag;
_procedural->_state->setCullMode(gpu::State::CULL_NONE);
_procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL);
_procedural->_state->setBlendFunction(false,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
}
if (_procedural->ready()) {
_procedural->prepare(batch);
_procedural->prepare(batch, this->getDimensions());
DependencyManager::get<GeometryCache>()->renderSolidCube(batch, 1.0f, _procedural->getColor(cubeColor));
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);

View file

@ -13,10 +13,11 @@
#define hifi_RenderableBoxEntityItem_h
#include <BoxEntityItem.h>
#include "RenderableEntityItem.h"
#include "RenderableProceduralItem.h"
#include <procedural/Procedural.h>
class RenderableBoxEntityItem : public BoxEntityItem, RenderableProceduralItem {
#include "RenderableEntityItem.h"
class RenderableBoxEntityItem : public BoxEntityItem {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
@ -28,6 +29,8 @@ public:
virtual void setUserData(const QString& value);
SIMPLE_RENDERABLE()
private:
QSharedPointer<Procedural> _procedural;
};

View file

@ -227,7 +227,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
if (hasModel()) {
if (_model) {
if (getModelURL() != _model->getURLAsString()) {
if (getModelURL() != _model->getURL().toString()) {
qDebug() << "Updating model URL: " << getModelURL();
_model->setURL(getModelURL());
}

View file

@ -228,6 +228,14 @@ void RenderableParticleEffectEntityItem::updateRenderItem() {
// update vertex buffer
auto vertexBuffer = payload.getVertexBuffer();
size_t numBytes = sizeof(Vertex) * _vertices.size();
if (numBytes == 0) {
vertexBuffer->resize(0);
auto indexBuffer = payload.getIndexBuffer();
indexBuffer->resize(0);
return;
}
vertexBuffer->resize(numBytes);
gpu::Byte* data = vertexBuffer->editData();
memcpy(data, &(_vertices[0]), numBytes);
@ -293,7 +301,7 @@ void RenderableParticleEffectEntityItem::updateRenderItem() {
payload.setPipeline(_untexturedPipeline);
}
});
_scene->enqueuePendingChanges(pendingChanges);
}

View file

@ -1,60 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/09/05
// Copyright 2013-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
//
#pragma once
#ifndef hifi_RenderableProcedrualItem_h
#define hifi_RenderableProcedrualItem_h
#include <QtCore/qglobal.h>
#include <QtCore/QString>
#include <QtCore/QUrl>
#include <QtCore/QJsonObject>
#include <ShaderCache.h>
#include <gpu/Shader.h>
#include <gpu/Pipeline.h>
#include <gpu/Batch.h>
class EntityItem;
class QJsonObject;
class RenderableProceduralItem {
protected:
// FIXME better encapsulation
// FIXME better mechanism for extending to things rendered using shaders other than simple.slv
struct ProceduralInfo {
ProceduralInfo(EntityItem* entity);
void parse();
void parse(const QJsonObject&);
bool ready();
void prepare(gpu::Batch& batch);
glm::vec4 getColor(const glm::vec4& entityColor);
bool _enabled{ false };
uint8_t _version{ 1 };
gpu::PipelinePointer _pipeline;
gpu::ShaderPointer _vertexShader;
gpu::ShaderPointer _fragmentShader;
gpu::ShaderPointer _shader;
QString _shaderSource;
QString _shaderPath;
QUrl _shaderUrl;
quint64 _shaderModified{ 0 };
bool _pipelineDirty{ true };
int32_t _timeSlot{ gpu::Shader::INVALID_LOCATION };
int32_t _scaleSlot{ gpu::Shader::INVALID_LOCATION };
uint64_t _start{ 0 };
NetworkShaderPointer _networkShader;
EntityItem* _entity;
QJsonObject _uniforms;
};
QSharedPointer<ProceduralInfo> _procedural;
};
#endif

View file

@ -21,6 +21,8 @@
#include <PerfStat.h>
#include "RenderableDebugableEntityItem.h"
#include "../render-utils/simple_vert.h"
#include "../render-utils/simple_frag.h"
EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return std::make_shared<RenderableSphereEntityItem>(entityID, properties);
@ -47,12 +49,19 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
static const int SLICES = 15, STACKS = 15;
if (!_procedural) {
_procedural.reset(new ProceduralInfo(this));
_procedural.reset(new Procedural(getUserData()));
_procedural->_vertexSource = simple_vert;
_procedural->_fragmentSource = simple_frag;
_procedural->_state->setCullMode(gpu::State::CULL_NONE);
_procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL);
_procedural->_state->setBlendFunction(false,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
}
glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha());
if (_procedural->ready()) {
_procedural->prepare(batch);
_procedural->prepare(batch, getDimensions());
DependencyManager::get<GeometryCache>()->renderSphere(batch, 0.5f, SLICES, STACKS, _procedural->getColor(sphereColor));
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);

View file

@ -13,10 +13,11 @@
#define hifi_RenderableSphereEntityItem_h
#include <SphereEntityItem.h>
#include "RenderableEntityItem.h"
#include "RenderableProceduralItem.h"
#include <procedural/Procedural.h>
class RenderableSphereEntityItem : public SphereEntityItem, RenderableProceduralItem {
#include "RenderableEntityItem.h"
class RenderableSphereEntityItem : public SphereEntityItem {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
@ -28,6 +29,9 @@ public:
virtual void setUserData(const QString& value);
SIMPLE_RENDERABLE();
private:
QSharedPointer<Procedural> _procedural;
};

View file

@ -28,13 +28,13 @@ void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer<NLPacket
}
}
void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType::Value type, QByteArray& buffer, int clockSkew) {
void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, int clockSkew) {
if (type == PacketType::EntityAdd || type == PacketType::EntityEdit) {
EntityItem::adjustEditPacketForClockSkew(buffer, clockSkew);
}
}
void EntityEditPacketSender::queueEditEntityMessage(PacketType::Value type, EntityItemID modelID,
void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemID modelID,
const EntityItemProperties& properties) {
if (!_shouldSend) {
return; // bail early

View file

@ -26,13 +26,13 @@ public:
/// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in
/// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known.
/// NOTE: EntityItemProperties assumes that all distances are in meter units
void queueEditEntityMessage(PacketType::Value type, EntityItemID modelID, const EntityItemProperties& properties);
void queueEditEntityMessage(PacketType type, EntityItemID modelID, const EntityItemProperties& properties);
void queueEraseEntityMessage(const EntityItemID& entityItemID);
// My server type is the model server
virtual char getMyNodeType() const { return NodeType::EntityServer; }
virtual void adjustEditPacketForClockSkew(PacketType::Value type, QByteArray& buffer, int clockSkew);
virtual void adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, int clockSkew);
public slots:
void processEntityEditNackPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);

View file

@ -11,8 +11,10 @@
#include <QtCore/QObject>
#include <QDebug>
#include <BufferParser.h>
#include <udt/PacketHeaders.h>
#include <UUID.h>
#include "RegisteredMetaTypes.h"
#include "EntityItemID.h"

View file

@ -724,7 +724,7 @@ void EntityItemPropertiesFromScriptValueHonorReadOnly(const QScriptValue &object
//
// TODO: Implement support for script and visible properties.
//
bool EntityItemProperties::encodeEntityEditPacket(PacketType::Value command, EntityItemID id, const EntityItemProperties& properties,
bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
QByteArray& buffer) {
OctreePacketData ourDataPacket(false, buffer.size()); // create a packetData object to add out packet details too.
OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro

View file

@ -197,7 +197,7 @@ public:
void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; }
void setLocalRenderAlpha(float value) { _localRenderAlpha = value; _localRenderAlphaChanged = true; }
static bool encodeEntityEditPacket(PacketType::Value command, EntityItemID id, const EntityItemProperties& properties,
static bool encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
QByteArray& buffer);
static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer);

View file

@ -32,7 +32,7 @@ EntityScriptingInterface::EntityScriptingInterface() :
connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptingInterface::canRezChanged);
}
void EntityScriptingInterface::queueEntityMessage(PacketType::Value packetType,
void EntityScriptingInterface::queueEntityMessage(PacketType packetType,
EntityItemID entityID, const EntityItemProperties& properties) {
getEntityPacketSender()->queueEditEntityMessage(packetType, entityID, properties);
}

View file

@ -171,7 +171,7 @@ private:
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulation*, EntityItemPointer)> actor);
bool setVoxels(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
void queueEntityMessage(PacketType::Value packetType, EntityItemID entityID, const EntityItemProperties& properties);
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode

View file

@ -63,7 +63,7 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
resetClientEditStats();
}
bool EntityTree::handlesEditPacketType(PacketType::Value packetType) const {
bool EntityTree::handlesEditPacketType(PacketType packetType) const {
// we handle these types of "edit" packets
switch (packetType) {
case PacketType::EntityAdd:

View file

@ -62,10 +62,10 @@ public:
// These methods will allow the OctreeServer to send your tree inbound edit packets of your
// own definition. Implement these to allow your octree based server to support editing
virtual bool getWantSVOfileVersions() const { return true; }
virtual PacketType::Value expectedDataPacketType() const { return PacketType::EntityData; }
virtual PacketType expectedDataPacketType() const { return PacketType::EntityData; }
virtual bool canProcessVersion(PacketVersion thisVersion) const
{ return thisVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS; }
virtual bool handlesEditPacketType(PacketType::Value packetType) const;
virtual bool handlesEditPacketType(PacketType packetType) const;
virtual int processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength,
const SharedNodePointer& senderNode);

View file

@ -31,8 +31,8 @@ public:
virtual ~EntityTreeHeadlessViewer();
virtual char getMyNodeType() const { return NodeType::EntityServer; }
virtual PacketType::Value getMyQueryMessageType() const { return PacketType::EntityQuery; }
virtual PacketType::Value getExpectedPacketType() const { return PacketType::EntityData; }
virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; }
virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; }
void update();

View file

@ -194,10 +194,10 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f
}
bool OBJReader::isValidTexture(const QByteArray &filename) {
if (!_url) {
if (_url.isEmpty()) {
return false;
}
QUrl candidateUrl = _url->resolved(QUrl(filename));
QUrl candidateUrl = _url.resolved(QUrl(filename));
QNetworkReply *netReply = request(candidateUrl, true);
bool isValid = netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200);
netReply->deleteLater();
@ -330,7 +330,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
}
QByteArray groupName = tokenizer.getDatum();
currentGroup = groupName;
} else if (token == "mtllib" && _url) {
} else if (token == "mtllib" && !_url.isEmpty()) {
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
}
@ -340,7 +340,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
}
librariesSeen[libraryName] = true;
// Throw away any path part of libraryName, and merge against original url.
QUrl libraryUrl = _url->resolved(QUrl(libraryName).fileName());
QUrl libraryUrl = _url.resolved(QUrl(libraryName).fileName());
#ifdef WANT_DEBUG
qCDebug(modelformat) << "OBJ Reader new library:" << libraryName << " at:" << libraryUrl;
#endif
@ -415,17 +415,14 @@ done:
}
FBXGeometry* OBJReader::readOBJ(const QByteArray& model, const QVariantHash& mapping) {
QBuffer buffer(const_cast<QByteArray*>(&model));
FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url) {
QBuffer buffer { &model };
buffer.open(QIODevice::ReadOnly);
return readOBJ(&buffer, mapping, nullptr);
}
FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url) {
FBXGeometry* geometryPtr = new FBXGeometry();
FBXGeometry& geometry = *geometryPtr;
OBJTokenizer tokenizer(device);
OBJTokenizer tokenizer { &buffer };
float scaleGuess = 1.0f;
_url = url;
@ -463,8 +460,8 @@ FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping,
// Some .obj files use the convention that a group with uv coordinates that doesn't define a material, should use
// a texture with the same basename as the .obj file.
if (url) {
QString filename = url->fileName();
if (!url.isEmpty()) {
QString filename = url.fileName();
int extIndex = filename.lastIndexOf('.'); // by construction, this does not fail
QString basename = filename.remove(extIndex + 1, sizeof("obj"));
OBJMaterial& preDefinedMaterial = materials[SMART_DEFAULT_MATERIAL_NAME];

View file

@ -71,10 +71,10 @@ public:
QHash<QString, OBJMaterial> materials;
QNetworkReply* request(QUrl& url, bool isTest);
FBXGeometry* readOBJ(const QByteArray& model, const QVariantHash& mapping);
FBXGeometry* readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url);
FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url = QUrl());
private:
QUrl* _url = nullptr;
QUrl _url;
QHash<QByteArray, bool> librariesSeen;
bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess);

View file

@ -0,0 +1,11 @@
set(TARGET_NAME gpu-networking)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library()
add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(shared networking gpu)

View file

@ -0,0 +1,11 @@
//
// Created by Bradley Austin Davis on 2015/08/07
// Copyright 2013-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 "GpuNetworkingLogging.h"
Q_LOGGING_CATEGORY(gpunetwork, "hifi.gpu-network")

View file

@ -0,0 +1,11 @@
//
// Created by Bradley Austin Davis on 2015/08/07
// Copyright 2013-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 <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(gpunetwork)

View file

@ -8,13 +8,13 @@
#include "ShaderCache.h"
NetworkShader::NetworkShader(const QUrl& url, bool delayLoad)
: Resource(url, delayLoad) {};
: Resource(url, delayLoad)
{
}
void NetworkShader::downloadFinished(QNetworkReply* reply) {
if (reply) {
_source = reply->readAll();
reply->deleteLater();
}
void NetworkShader::downloadFinished(const QByteArray& data) {
_source = QString::fromUtf8(data);
}
ShaderCache& ShaderCache::instance() {

View file

@ -14,9 +14,9 @@
class NetworkShader : public Resource {
public:
NetworkShader(const QUrl& url, bool delayLoad);
virtual void downloadFinished(QNetworkReply* reply) override;
virtual void downloadFinished(const QByteArray& data) override;
QByteArray _source;
QString _source;
};
using NetworkShaderPointer = QSharedPointer<NetworkShader>;

View file

@ -1,6 +1,6 @@
//
// TextureCache.cpp
// interface/src/renderer
// libraries/gpu-networking/src
//
// Created by Andrzej Kapolka on 8/6/13.
// Copyright 2013 High Fidelity, Inc.
@ -21,13 +21,11 @@
#include <QRunnable>
#include <QThreadPool>
#include <qimagereader.h>
#include "PathUtils.h"
#include <PathUtils.h>
#include <gpu/Batch.h>
#include "RenderUtilsLogging.h"
#include "GpuNetworkingLogging.h"
TextureCache::TextureCache() {
const qint64 TEXTURE_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE;
@ -215,8 +213,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArr
class ImageReader : public QRunnable {
public:
ImageReader(const QWeakPointer<Resource>& texture, TextureType type, QNetworkReply* reply, const QUrl& url = QUrl(),
const QByteArray& content = QByteArray());
ImageReader(const QWeakPointer<Resource>& texture, TextureType type, const QByteArray& data, const QUrl& url = QUrl());
virtual void run();
@ -224,27 +221,27 @@ private:
QWeakPointer<Resource> _texture;
TextureType _type;
QNetworkReply* _reply;
QUrl _url;
QByteArray _content;
};
void NetworkTexture::downloadFinished(QNetworkReply* reply) {
void NetworkTexture::downloadFinished(const QByteArray& data) {
// send the reader off to the thread pool
QThreadPool::globalInstance()->start(new ImageReader(_self, _type, reply));
QThreadPool::globalInstance()->start(new ImageReader(_self, _type, data, _url));
}
void NetworkTexture::loadContent(const QByteArray& content) {
QThreadPool::globalInstance()->start(new ImageReader(_self, _type, NULL, _url, content));
QThreadPool::globalInstance()->start(new ImageReader(_self, _type, content, _url));
}
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, TextureType type, QNetworkReply* reply,
const QUrl& url, const QByteArray& content) :
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, TextureType type, const QByteArray& data,
const QUrl& url) :
_texture(texture),
_type(type),
_reply(reply),
_url(url),
_content(content) {
_content(data)
{
}
std::once_flag onceListSupportedFormatsflag;
@ -255,7 +252,7 @@ void listSupportedImageFormats() {
foreach(const QByteArray& f, supportedFormats) {
formats += QString(f) + ",";
}
qCDebug(renderutils) << "List of supported Image formats:" << formats;
qCDebug(gpunetwork) << "List of supported Image formats:" << formats;
});
}
@ -297,16 +294,8 @@ public:
void ImageReader::run() {
QSharedPointer<Resource> texture = _texture.toStrongRef();
if (texture.isNull()) {
if (_reply) {
_reply->deleteLater();
}
return;
}
if (_reply) {
_url = _reply->url();
_content = _reply->readAll();
_reply->deleteLater();
}
listSupportedImageFormats();
@ -323,9 +312,9 @@ void ImageReader::run() {
if (originalWidth == 0 || originalHeight == 0 || imageFormat == QImage::Format_Invalid) {
if (filenameExtension.empty()) {
qCDebug(renderutils) << "QImage failed to create from content, no file extension:" << _url;
qCDebug(gpunetwork) << "QImage failed to create from content, no file extension:" << _url;
} else {
qCDebug(renderutils) << "QImage failed to create from content" << _url;
qCDebug(gpunetwork) << "QImage failed to create from content" << _url;
}
return;
}
@ -333,7 +322,7 @@ void ImageReader::run() {
int imageArea = image.width() * image.height();
auto ntex = dynamic_cast<NetworkTexture*>(&*texture);
if (ntex && (ntex->getType() == CUBE_TEXTURE)) {
qCDebug(renderutils) << "Cube map size:" << _url << image.width() << image.height();
qCDebug(gpunetwork) << "Cube map size:" << _url << image.width() << image.height();
}
int opaquePixels = 0;
@ -384,7 +373,7 @@ void ImageReader::run() {
}
}
if (opaquePixels == imageArea) {
qCDebug(renderutils) << "Image with alpha channel is completely opaque:" << _url;
qCDebug(gpunetwork) << "Image with alpha channel is completely opaque:" << _url;
image = image.convertToFormat(QImage::Format_RGB888);
}
@ -532,7 +521,7 @@ void ImageReader::run() {
faces.push_back(image.copy(QRect(layout._faceZPos._x * faceWidth, layout._faceZPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZPos._horizontalMirror, layout._faceZPos._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceZNeg._x * faceWidth, layout._faceZNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZNeg._horizontalMirror, layout._faceZNeg._verticalMirror));
} else {
qCDebug(renderutils) << "Failed to find a known cube map layout from this image:" << _url;
qCDebug(gpunetwork) << "Failed to find a known cube map layout from this image:" << _url;
return;
}

Some files were not shown because too many files have changed in this diff Show more