mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge branch 'master' into ajt/particle-system-improvements
Conflicts: libraries/networking/src/PacketHeaders.cpp libraries/networking/src/PacketHeaders.h
This commit is contained in:
commit
e1cbfcd901
96 changed files with 2068 additions and 896 deletions
|
@ -224,5 +224,7 @@ void Agent::run() {
|
|||
|
||||
void Agent::aboutToFinish() {
|
||||
_scriptEngine.stop();
|
||||
NetworkAccessManager::getInstance().clearAccessCache();
|
||||
|
||||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(NULL);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <QProcess>
|
||||
#include <QSettings>
|
||||
#include <QSharedMemory>
|
||||
|
@ -30,22 +32,19 @@
|
|||
#include <SoundCache.h>
|
||||
|
||||
#include "AssignmentFactory.h"
|
||||
#include "AssignmentThread.h"
|
||||
|
||||
#include "AssignmentClient.h"
|
||||
|
||||
const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client";
|
||||
const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
|
||||
|
||||
SharedAssignmentPointer AssignmentClient::_currentAssignment;
|
||||
|
||||
int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
|
||||
|
||||
AssignmentClient::AssignmentClient(int ppid, Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort) :
|
||||
AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
|
||||
quint16 assignmentMonitorPort) :
|
||||
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME),
|
||||
_localASPortSharedMem(NULL),
|
||||
_localACMPortSharedMem(NULL)
|
||||
_localASPortSharedMem(NULL)
|
||||
{
|
||||
LogUtils::init();
|
||||
|
||||
|
@ -108,19 +107,39 @@ AssignmentClient::AssignmentClient(int ppid, Assignment::Type requestAssignmentT
|
|||
|
||||
// Create Singleton objects on main thread
|
||||
NetworkAccessManager::getInstance();
|
||||
|
||||
// Hook up a timer to send this child's status to the Monitor once per second
|
||||
setUpStatsToMonitor(ppid);
|
||||
|
||||
// did we get an assignment-client monitor port?
|
||||
if (assignmentMonitorPort > 0) {
|
||||
_assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort);
|
||||
|
||||
qDebug() << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket;
|
||||
|
||||
// Hook up a timer to send this child's status to the Monitor once per second
|
||||
setUpStatsToMonitor();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AssignmentClient::stopAssignmentClient() {
|
||||
qDebug() << "Exiting.";
|
||||
qDebug() << "Forced stop of assignment-client.";
|
||||
|
||||
_requestTimer.stop();
|
||||
_statsTimerACM.stop();
|
||||
|
||||
if (_currentAssignment) {
|
||||
_currentAssignment->aboutToQuit();
|
||||
// grab the thread for the current assignment
|
||||
QThread* currentAssignmentThread = _currentAssignment->thread();
|
||||
|
||||
// ask the current assignment to stop
|
||||
QMetaObject::invokeMethod(_currentAssignment, "stop", Qt::BlockingQueuedConnection);
|
||||
|
||||
// ask the current assignment to delete itself on its thread
|
||||
_currentAssignment->deleteLater();
|
||||
|
||||
// when this thread is destroyed we don't need to run our assignment complete method
|
||||
disconnect(currentAssignmentThread, &QThread::destroyed, this, &AssignmentClient::assignmentCompleted);
|
||||
|
||||
// wait on the thread from that assignment - it will be gone once the current assignment deletes
|
||||
currentAssignmentThread->quit();
|
||||
currentAssignmentThread->wait();
|
||||
}
|
||||
|
@ -129,24 +148,13 @@ void AssignmentClient::stopAssignmentClient() {
|
|||
|
||||
void AssignmentClient::aboutToQuit() {
|
||||
stopAssignmentClient();
|
||||
|
||||
// clear the log handler so that Qt doesn't call the destructor on LogHandler
|
||||
qInstallMessageHandler(0);
|
||||
// clear out pointer to the assignment so the destructor gets called. if we don't do this here,
|
||||
// it will get destroyed along with all the other "static" stuff. various static member variables
|
||||
// will be destroyed first and things go wrong.
|
||||
_currentAssignment.clear();
|
||||
qInstallMessageHandler(0);
|
||||
}
|
||||
|
||||
|
||||
void AssignmentClient::setUpStatsToMonitor(int ppid) {
|
||||
// Figure out the address to send out stats to
|
||||
quint16 localMonitorServerPort = DEFAULT_ASSIGNMENT_CLIENT_MONITOR_PORT;
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
nodeList->getLocalServerPortFromSharedMemory(QString(ASSIGNMENT_CLIENT_MONITOR_LOCAL_PORT_SMEM_KEY) + "-" +
|
||||
QString::number(ppid), _localACMPortSharedMem, localMonitorServerPort);
|
||||
_assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, localMonitorServerPort, true);
|
||||
|
||||
void AssignmentClient::setUpStatsToMonitor() {
|
||||
// send a stats packet every 1 seconds
|
||||
connect(&_statsTimerACM, &QTimer::timeout, this, &AssignmentClient::sendStatsPacketToACM);
|
||||
_statsTimerACM.start(1000);
|
||||
|
@ -202,8 +210,11 @@ void AssignmentClient::readPendingDatagrams() {
|
|||
|
||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) {
|
||||
|
||||
qDebug() << "Received a PacketTypeCreateAssignment - attempting to unpack.";
|
||||
|
||||
// construct the deployed assignment from the packet data
|
||||
_currentAssignment = SharedAssignmentPointer(AssignmentFactory::unpackAssignment(receivedPacket));
|
||||
_currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket);
|
||||
|
||||
if (_currentAssignment) {
|
||||
qDebug() << "Received an assignment -" << *_currentAssignment;
|
||||
|
@ -216,15 +227,24 @@ void AssignmentClient::readPendingDatagrams() {
|
|||
qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString();
|
||||
|
||||
// start the deployed assignment
|
||||
AssignmentThread* workerThread = new AssignmentThread(_currentAssignment, this);
|
||||
workerThread->setObjectName("worker");
|
||||
QThread* workerThread = new QThread;
|
||||
workerThread->setObjectName("ThreadedAssignment Worker");
|
||||
|
||||
connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run);
|
||||
connect(_currentAssignment.data(), &ThreadedAssignment::finished, workerThread, &QThread::quit);
|
||||
connect(_currentAssignment.data(), &ThreadedAssignment::finished,
|
||||
this, &AssignmentClient::assignmentCompleted);
|
||||
|
||||
// once the ThreadedAssignment says it is finished - we ask it to deleteLater
|
||||
connect(_currentAssignment.data(), &ThreadedAssignment::finished, _currentAssignment.data(),
|
||||
&ThreadedAssignment::deleteLater);
|
||||
|
||||
// once it is deleted, we quit the worker thread
|
||||
connect(_currentAssignment.data(), &ThreadedAssignment::destroyed, workerThread, &QThread::quit);
|
||||
|
||||
// have the worker thread remove itself once it is done
|
||||
connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
|
||||
|
||||
// once the worker thread says it is done, we consider the assignment completed
|
||||
connect(workerThread, &QThread::destroyed, this, &AssignmentClient::assignmentCompleted);
|
||||
|
||||
_currentAssignment->moveToThread(workerThread);
|
||||
|
||||
// move the NodeList to the thread used for the _current assignment
|
||||
|
@ -243,8 +263,9 @@ void AssignmentClient::readPendingDatagrams() {
|
|||
} else if (packetTypeForPacket(receivedPacket) == PacketTypeStopNode) {
|
||||
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
|
||||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
|
||||
qDebug() << "Network told me to exit.";
|
||||
emit stopAssignmentClient();
|
||||
qDebug() << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketTypeStopNode.";
|
||||
|
||||
QCoreApplication::quit();
|
||||
} else {
|
||||
qDebug() << "Got a stop packet from other than localhost.";
|
||||
}
|
||||
|
@ -281,20 +302,22 @@ void AssignmentClient::handleAuthenticationRequest() {
|
|||
}
|
||||
|
||||
void AssignmentClient::assignmentCompleted() {
|
||||
|
||||
// we expect that to be here the previous assignment has completely cleaned up
|
||||
assert(_currentAssignment.isNull());
|
||||
|
||||
// reset our current assignment pointer to NULL now that it has been deleted
|
||||
_currentAssignment = NULL;
|
||||
|
||||
// reset the logging target to the the CHILD_TARGET_NAME
|
||||
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||
|
||||
qDebug("Assignment finished or never started - waiting for new assignment.");
|
||||
qDebug() << "Assignment finished or never started - waiting for new assignment.";
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// have us handle incoming NodeList datagrams again
|
||||
disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment.data(), 0);
|
||||
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
|
||||
|
||||
// clear our current assignment shared pointer now that we're done with it
|
||||
// if the assignment thread is still around it has its own shared pointer to the assignment
|
||||
_currentAssignment.clear();
|
||||
// have us handle incoming NodeList datagrams again, and make sure our ThreadedAssignment isn't handling them
|
||||
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
|
||||
|
||||
// reset our NodeList by switching back to unassigned and clearing the list
|
||||
nodeList->setOwnerType(NodeType::Unassigned);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_AssignmentClient_h
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QPointer>
|
||||
|
||||
#include "ThreadedAssignment.h"
|
||||
|
||||
|
@ -22,10 +23,9 @@ class AssignmentClient : public QObject {
|
|||
Q_OBJECT
|
||||
public:
|
||||
|
||||
AssignmentClient(int ppid, Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort);
|
||||
static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; }
|
||||
|
||||
AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
|
||||
quint16 assignmentMonitorPort);
|
||||
private slots:
|
||||
void sendAssignmentRequest();
|
||||
void readPendingDatagrams();
|
||||
|
@ -38,13 +38,13 @@ public slots:
|
|||
void aboutToQuit();
|
||||
|
||||
private:
|
||||
void setUpStatsToMonitor(int ppid);
|
||||
void setUpStatsToMonitor();
|
||||
|
||||
Assignment _requestAssignment;
|
||||
static SharedAssignmentPointer _currentAssignment;
|
||||
QPointer<ThreadedAssignment> _currentAssignment;
|
||||
QString _assignmentServerHostname;
|
||||
HifiSockAddr _assignmentServerSocket;
|
||||
QSharedMemory* _localASPortSharedMem; // memory shared with domain server
|
||||
QSharedMemory* _localACMPortSharedMem; // memory shared with assignment client monitor
|
||||
QTimer _requestTimer; // timer for requesting and assignment
|
||||
QTimer _statsTimerACM; // timer for sending stats to assignment client monitor
|
||||
|
||||
|
|
|
@ -79,9 +79,8 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
const QCommandLineOption maxChildsOption(ASSIGNMENT_MAX_FORKS_OPTION, "maximum number of children", "child-count");
|
||||
parser.addOption(maxChildsOption);
|
||||
|
||||
const QCommandLineOption ppidOption(PARENT_PID_OPTION, "parent's process id", "pid");
|
||||
parser.addOption(ppidOption);
|
||||
|
||||
const QCommandLineOption monitorPortOption(ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION, "assignment-client monitor port", "port");
|
||||
parser.addOption(monitorPortOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qCritical() << parser.errorText() << endl;
|
||||
|
@ -113,9 +112,9 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
maxForks = parser.value(maxChildsOption).toInt();
|
||||
}
|
||||
|
||||
int ppid = 0;
|
||||
if (parser.isSet(ppidOption)) {
|
||||
ppid = parser.value(ppidOption).toInt();
|
||||
unsigned short monitorPort = 0;
|
||||
if (parser.isSet(monitorPortOption)) {
|
||||
monitorPort = parser.value(monitorPortOption).toUShort();
|
||||
}
|
||||
|
||||
if (!numForks && minForks) {
|
||||
|
@ -162,12 +161,11 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) {
|
||||
assignmentServerPort = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toString().toUInt();
|
||||
}
|
||||
|
||||
if (parser.isSet(assignmentServerPortOption)) {
|
||||
assignmentServerPort = parser.value(assignmentServerPortOption).toInt();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (parser.isSet(numChildsOption)) {
|
||||
if (minForks && minForks > numForks) {
|
||||
qCritical() << "--min can't be more than -n";
|
||||
|
@ -186,14 +184,17 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
|
||||
if (numForks || minForks || maxForks) {
|
||||
AssignmentClientMonitor monitor(numForks, minForks, maxForks, requestAssignmentType, assignmentPool,
|
||||
walletUUID, assignmentServerHostname, assignmentServerPort);
|
||||
connect(this, &QCoreApplication::aboutToQuit, &monitor, &AssignmentClientMonitor::aboutToQuit);
|
||||
exec();
|
||||
AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks,
|
||||
requestAssignmentType, assignmentPool,
|
||||
walletUUID, assignmentServerHostname,
|
||||
assignmentServerPort);
|
||||
monitor->setParent(this);
|
||||
connect(this, &QCoreApplication::aboutToQuit, monitor, &AssignmentClientMonitor::aboutToQuit);
|
||||
} else {
|
||||
AssignmentClient client(ppid, requestAssignmentType, assignmentPool,
|
||||
walletUUID, assignmentServerHostname, assignmentServerPort);
|
||||
connect(this, &QCoreApplication::aboutToQuit, &client, &AssignmentClient::aboutToQuit);
|
||||
exec();
|
||||
AssignmentClient* client = new AssignmentClient(requestAssignmentType, assignmentPool,
|
||||
walletUUID, assignmentServerHostname,
|
||||
assignmentServerPort, monitorPort);
|
||||
client->setParent(this);
|
||||
connect(this, &QCoreApplication::aboutToQuit, client, &AssignmentClient::aboutToQuit);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ const QString CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION = "p";
|
|||
const QString ASSIGNMENT_NUM_FORKS_OPTION = "n";
|
||||
const QString ASSIGNMENT_MIN_FORKS_OPTION = "min";
|
||||
const QString ASSIGNMENT_MAX_FORKS_OPTION = "max";
|
||||
const QString PARENT_PID_OPTION = "ppid";
|
||||
const QString ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION = "monitor-port";
|
||||
|
||||
|
||||
class AssignmentClientApp : public QCoreApplication {
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
|
||||
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
|
||||
const int WAIT_FOR_CHILD_MSECS = 500;
|
||||
const int WAIT_FOR_CHILD_MSECS = 1000;
|
||||
|
||||
AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmentClientForks,
|
||||
const unsigned int minAssignmentClientForks,
|
||||
|
@ -41,22 +41,20 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
_assignmentServerPort(assignmentServerPort)
|
||||
{
|
||||
qDebug() << "_requestAssignmentType =" << _requestAssignmentType;
|
||||
|
||||
|
||||
// start the Logging class with the parent's target name
|
||||
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME);
|
||||
|
||||
// make sure we output process IDs for a monitor otherwise it's insane to parse
|
||||
LogHandler::getInstance().setShouldOutputPID(true);
|
||||
|
||||
// create a NodeList so we can receive stats from children
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
auto addressManager = DependencyManager::set<AddressManager>();
|
||||
auto nodeList = DependencyManager::set<LimitedNodeList>(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_PORT);
|
||||
auto nodeList = DependencyManager::set<LimitedNodeList>();
|
||||
|
||||
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClientMonitor::readPendingDatagrams);
|
||||
|
||||
qint64 pid = QCoreApplication::applicationPid ();
|
||||
|
||||
nodeList->putLocalPortIntoSharedMemory(QString(ASSIGNMENT_CLIENT_MONITOR_LOCAL_PORT_SMEM_KEY) + "-" + QString::number(pid),
|
||||
this, nodeList->getNodeSocket().localPort());
|
||||
|
||||
// use QProcess to fork off a process for each of the child assignment clients
|
||||
for (unsigned int i = 0; i < _numAssignmentClientForks; i++) {
|
||||
spawnChildClient();
|
||||
|
@ -71,61 +69,60 @@ AssignmentClientMonitor::~AssignmentClientMonitor() {
|
|||
stopChildProcesses();
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::waitOnChildren(int msecs) {
|
||||
QMutableListIterator<QProcess*> i(_childProcesses);
|
||||
while (i.hasNext()) {
|
||||
QProcess* childProcess = i.next();
|
||||
bool finished = childProcess->waitForFinished(msecs);
|
||||
if (finished) {
|
||||
i.remove();
|
||||
}
|
||||
void AssignmentClientMonitor::simultaneousWaitOnChildren(int waitMsecs) {
|
||||
QElapsedTimer waitTimer;
|
||||
waitTimer.start();
|
||||
|
||||
// loop as long as we still have processes around and we're inside the wait window
|
||||
while(_childProcesses.size() > 0 && !waitTimer.hasExpired(waitMsecs)) {
|
||||
// continue processing events so we can handle a process finishing up
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::childProcessFinished() {
|
||||
QProcess* childProcess = qobject_cast<QProcess*>(sender());
|
||||
qint64 processID = _childProcesses.key(childProcess);
|
||||
|
||||
if (processID > 0) {
|
||||
qDebug() << "Child process" << processID << "has finished. Removing from internal map.";
|
||||
_childProcesses.remove(processID);
|
||||
}
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::stopChildProcesses() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
qDebug() << "asking child" << node->getUUID() << "to exit.";
|
||||
node->activateLocalSocket();
|
||||
QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode);
|
||||
nodeList->writeUnverifiedDatagram(diePacket, *node->getActiveSocket());
|
||||
});
|
||||
|
||||
// try to give all the children time to shutdown
|
||||
waitOnChildren(WAIT_FOR_CHILD_MSECS);
|
||||
|
||||
// ask more firmly
|
||||
QMutableListIterator<QProcess*> i(_childProcesses);
|
||||
while (i.hasNext()) {
|
||||
QProcess* childProcess = i.next();
|
||||
// ask child processes to terminate
|
||||
foreach(QProcess* childProcess, _childProcesses) {
|
||||
qDebug() << "Attempting to terminate child process" << childProcess->processId();
|
||||
childProcess->terminate();
|
||||
}
|
||||
|
||||
// try to give all the children time to shutdown
|
||||
waitOnChildren(WAIT_FOR_CHILD_MSECS);
|
||||
|
||||
// ask even more firmly
|
||||
QMutableListIterator<QProcess*> j(_childProcesses);
|
||||
while (j.hasNext()) {
|
||||
QProcess* childProcess = j.next();
|
||||
childProcess->kill();
|
||||
|
||||
simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS);
|
||||
|
||||
if (_childProcesses.size() > 0) {
|
||||
// ask even more firmly
|
||||
foreach(QProcess* childProcess, _childProcesses) {
|
||||
qDebug() << "Attempting to kill child process" << childProcess->processId();
|
||||
childProcess->kill();
|
||||
}
|
||||
|
||||
simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS);
|
||||
}
|
||||
|
||||
waitOnChildren(WAIT_FOR_CHILD_MSECS);
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::aboutToQuit() {
|
||||
stopChildProcesses();
|
||||
|
||||
// clear the log handler so that Qt doesn't call the destructor on LogHandler
|
||||
qInstallMessageHandler(0);
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::spawnChildClient() {
|
||||
QProcess *assignmentClient = new QProcess(this);
|
||||
|
||||
_childProcesses.append(assignmentClient);
|
||||
QProcess* assignmentClient = new QProcess(this);
|
||||
|
||||
|
||||
// unparse the parts of the command-line that the child cares about
|
||||
QStringList _childArguments;
|
||||
if (_assignmentPool != "") {
|
||||
|
@ -149,17 +146,21 @@ void AssignmentClientMonitor::spawnChildClient() {
|
|||
_childArguments.append(QString::number(_requestAssignmentType));
|
||||
}
|
||||
|
||||
// tell children which shared memory key to use
|
||||
qint64 pid = QCoreApplication::applicationPid ();
|
||||
_childArguments.append("--" + PARENT_PID_OPTION);
|
||||
_childArguments.append(QString::number(pid));
|
||||
// tell children which assignment monitor port to use
|
||||
// for now they simply talk to us on localhost
|
||||
_childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION);
|
||||
_childArguments.append(QString::number(DependencyManager::get<NodeList>()->getLocalSockAddr().getPort()));
|
||||
|
||||
// make sure that the output from the child process appears in our output
|
||||
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments);
|
||||
|
||||
// make sure we hear that this process has finished when it does
|
||||
connect(assignmentClient, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(childProcessFinished()));
|
||||
|
||||
qDebug() << "Spawned a child client with PID" << assignmentClient->pid();
|
||||
_childProcesses.insert(assignmentClient->processId(), assignmentClient);
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::checkSpares() {
|
||||
|
@ -193,12 +194,11 @@ void AssignmentClientMonitor::checkSpares() {
|
|||
qDebug() << "asking child" << aSpareId << "to exit.";
|
||||
SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId);
|
||||
childNode->activateLocalSocket();
|
||||
QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode);
|
||||
|
||||
QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode);
|
||||
nodeList->writeUnverifiedDatagram(diePacket, childNode);
|
||||
}
|
||||
}
|
||||
|
||||
waitOnChildren(0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -229,7 +229,7 @@ void AssignmentClientMonitor::readPendingDatagrams() {
|
|||
} else {
|
||||
// tell unknown assignment-client child to exit.
|
||||
qDebug() << "asking unknown child to exit.";
|
||||
QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode);
|
||||
QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode);
|
||||
nodeList->writeUnverifiedDatagram(diePacket, senderSockAddr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,17 +33,19 @@ public:
|
|||
quint16 assignmentServerPort);
|
||||
~AssignmentClientMonitor();
|
||||
|
||||
void waitOnChildren(int msecs);
|
||||
void stopChildProcesses();
|
||||
private slots:
|
||||
void readPendingDatagrams();
|
||||
void checkSpares();
|
||||
|
||||
void childProcessFinished();
|
||||
|
||||
public slots:
|
||||
void aboutToQuit();
|
||||
|
||||
private:
|
||||
void spawnChildClient();
|
||||
void simultaneousWaitOnChildren(int waitMsecs);
|
||||
|
||||
QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children
|
||||
|
||||
const unsigned int _numAssignmentClientForks;
|
||||
|
@ -56,7 +58,7 @@ private:
|
|||
QString _assignmentServerHostname;
|
||||
quint16 _assignmentServerPort;
|
||||
|
||||
QList<QProcess*> _childProcesses;
|
||||
QMap<qint64, QProcess*> _childProcesses;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentClientMonitor_h
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
//
|
||||
// AssignmentThread.cpp
|
||||
// assignment-client/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AssignmentThread.h"
|
||||
|
||||
AssignmentThread::AssignmentThread(const SharedAssignmentPointer& assignment, QObject* parent) :
|
||||
QThread(parent),
|
||||
_assignment(assignment)
|
||||
{
|
||||
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
//
|
||||
// AssignmentThread.h
|
||||
// assignment-client/src
|
||||
//
|
||||
// Created by Stephen Birarda on 2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AssignmentThread_h
|
||||
#define hifi_AssignmentThread_h
|
||||
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
class AssignmentThread : public QThread {
|
||||
public:
|
||||
AssignmentThread(const SharedAssignmentPointer& assignment, QObject* parent);
|
||||
private:
|
||||
SharedAssignmentPointer _assignment;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentThread_h
|
|
@ -65,12 +65,6 @@ const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
|
|||
const QString AUDIO_ENV_GROUP_KEY = "audio_env";
|
||||
const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer";
|
||||
|
||||
void attachNewNodeDataToNode(Node *newNode) {
|
||||
if (!newNode->getLinkedData()) {
|
||||
newNode->setLinkedData(new AudioMixerClientData());
|
||||
}
|
||||
}
|
||||
|
||||
InboundAudioStream::Settings AudioMixer::_streamSettings;
|
||||
|
||||
bool AudioMixer::_printStreamStats = false;
|
||||
|
@ -514,7 +508,8 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
|
|||
bool sendData = dataChanged || (randFloat() < CHANCE_OF_SEND);
|
||||
|
||||
if (sendData) {
|
||||
int numBytesEnvPacketHeader = populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
int numBytesEnvPacketHeader = nodeList->populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment);
|
||||
char* envDataAt = clientEnvBuffer + numBytesEnvPacketHeader;
|
||||
|
||||
unsigned char bitset = 0;
|
||||
|
@ -531,7 +526,7 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
|
|||
memcpy(envDataAt, &wetLevel, sizeof(float));
|
||||
envDataAt += sizeof(float);
|
||||
}
|
||||
DependencyManager::get<NodeList>()->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node);
|
||||
nodeList->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,7 +547,7 @@ void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const Hif
|
|||
SharedNodePointer sendingNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
if (sendingNode->getCanAdjustLocks()) {
|
||||
QByteArray packet = receivedPacket;
|
||||
populatePacketHeader(packet, PacketTypeMuteEnvironment);
|
||||
nodeList->populatePacketHeader(packet, PacketTypeMuteEnvironment);
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node){
|
||||
if (node->getType() == NodeType::Agent && node->getActiveSocket() &&
|
||||
|
@ -686,7 +681,9 @@ void AudioMixer::run() {
|
|||
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
nodeList->linkedDataCreateCallback = attachNewNodeDataToNode;
|
||||
nodeList->linkedDataCreateCallback = [](Node* node) {
|
||||
node->setLinkedData(new AudioMixerClientData());
|
||||
};
|
||||
|
||||
// wait until we have the domain-server settings, otherwise we bail
|
||||
DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
|
@ -794,7 +791,7 @@ void AudioMixer::run() {
|
|||
// if the stream should be muted, send mute packet
|
||||
if (nodeData->getAvatarAudioStream()
|
||||
&& shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) {
|
||||
QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeNoisyMute);
|
||||
QByteArray packet = nodeList->byteArrayWithPopulatedHeader(PacketTypeNoisyMute);
|
||||
nodeList->writeDatagram(packet, node);
|
||||
}
|
||||
|
||||
|
@ -806,7 +803,7 @@ void AudioMixer::run() {
|
|||
char* mixDataAt;
|
||||
if (streamsMixed > 0) {
|
||||
// pack header
|
||||
int numBytesMixPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio);
|
||||
int numBytesMixPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio);
|
||||
mixDataAt = clientMixBuffer + numBytesMixPacketHeader;
|
||||
|
||||
// pack sequence number
|
||||
|
@ -819,7 +816,7 @@ void AudioMixer::run() {
|
|||
mixDataAt += AudioConstants::NETWORK_FRAME_BYTES_STEREO;
|
||||
} else {
|
||||
// pack header
|
||||
int numBytesPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame);
|
||||
int numBytesPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame);
|
||||
mixDataAt = clientMixBuffer + numBytesPacketHeader;
|
||||
|
||||
// pack sequence number
|
||||
|
@ -854,7 +851,7 @@ void AudioMixer::run() {
|
|||
++_numStatFrames;
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
|
||||
if (_isFinished) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer&
|
|||
quint8 appendFlag = 0;
|
||||
|
||||
// pack header
|
||||
int numBytesPacketHeader = populatePacketHeader(packet, PacketTypeAudioStreamStats);
|
||||
int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeAudioStreamStats);
|
||||
char* headerEndAt = packet + numBytesPacketHeader;
|
||||
|
||||
// calculate how many stream stat structs we can fit in each packet
|
||||
|
|
|
@ -52,18 +52,11 @@ AvatarMixer::~AvatarMixer() {
|
|||
if (_broadcastTimer) {
|
||||
_broadcastTimer->deleteLater();
|
||||
}
|
||||
|
||||
_broadcastThread.quit();
|
||||
_broadcastThread.wait();
|
||||
}
|
||||
|
||||
void attachAvatarDataToNode(Node* newNode) {
|
||||
if (!newNode->getLinkedData()) {
|
||||
// setup the client linked data - default the number of frames since adjustment
|
||||
// to our number of frames per second
|
||||
newNode->setLinkedData(new AvatarMixerClientData());
|
||||
}
|
||||
}
|
||||
|
||||
const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f;
|
||||
|
||||
// NOTE: some additional optimizations to consider.
|
||||
|
@ -129,10 +122,9 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
|
||||
static QByteArray mixedAvatarByteArray;
|
||||
|
||||
int numPacketHeaderBytes = populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
int numPacketHeaderBytes = nodeList->populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData);
|
||||
|
||||
// setup for distributed random floating point values
|
||||
std::random_device randomDevice;
|
||||
std::mt19937 generator(randomDevice());
|
||||
|
@ -161,7 +153,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
|
||||
// reset packet pointers for this node
|
||||
mixedAvatarByteArray.resize(numPacketHeaderBytes);
|
||||
|
||||
|
||||
AvatarData& avatar = nodeData->getAvatar();
|
||||
glm::vec3 myPosition = avatar.getPosition();
|
||||
|
||||
|
@ -179,7 +171,13 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
|
||||
// keep track of outbound data rate specifically for avatar data
|
||||
int numAvatarDataBytes = 0;
|
||||
|
||||
|
||||
// keep track of the number of other avatars held back in this frame
|
||||
int numAvatarsHeldBack = 0;
|
||||
|
||||
// keep track of the number of other avatar frames skipped
|
||||
int numAvatarsWithSkippedFrames = 0;
|
||||
|
||||
// use the data rate specifically for avatar data for FRD adjustment checks
|
||||
float avatarDataRateLastSecond = nodeData->getOutboundAvatarDataKbps();
|
||||
|
||||
|
@ -257,8 +255,38 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
&& distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PacketSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
|
||||
PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketTypeAvatarData);
|
||||
|
||||
if (lastSeqToReceiver > lastSeqFromSender) {
|
||||
// Did we somehow get out of order packets from the sender?
|
||||
// We don't expect this to happen - in RELEASE we add this to a trackable stat
|
||||
// and in DEBUG we crash on the assert
|
||||
|
||||
otherNodeData->incrementNumOutOfOrderSends();
|
||||
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// make sure we haven't already sent this data from this sender to this receiver
|
||||
// or that somehow we haven't sent
|
||||
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
|
||||
++numAvatarsHeldBack;
|
||||
return;
|
||||
} else if (lastSeqFromSender - lastSeqToReceiver > 1) {
|
||||
// this is a skip - we still send the packet but capture the presence of the skip so we see it happening
|
||||
++numAvatarsWithSkippedFrames;
|
||||
}
|
||||
|
||||
// we're going to send this avatar
|
||||
|
||||
// increment the number of avatars sent to this reciever
|
||||
nodeData->incrementNumAvatarsSentLastFrame();
|
||||
|
||||
// set the last sent sequence number for this sender on the receiver
|
||||
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
|
||||
otherNode->getLastSequenceNumberForPacketType(PacketTypeAvatarData));
|
||||
|
||||
QByteArray avatarByteArray;
|
||||
avatarByteArray.append(otherNode->getUUID().toRfc4122());
|
||||
|
@ -287,7 +315,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
&& (forceSend
|
||||
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|
||||
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||
QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
|
||||
QByteArray billboardPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
|
||||
billboardPacket.append(otherNode->getUUID().toRfc4122());
|
||||
billboardPacket.append(otherNodeData->getAvatar().getBillboard());
|
||||
|
||||
|
@ -301,7 +329,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|
||||
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||
|
||||
QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);
|
||||
QByteArray identityPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);
|
||||
|
||||
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
|
||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
|
||||
|
@ -318,6 +346,10 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
|
||||
// record the bytes sent for other avatar data in the AvatarMixerClientData
|
||||
nodeData->recordSentAvatarData(numAvatarDataBytes + mixedAvatarByteArray.size());
|
||||
|
||||
// record the number of avatars held back this frame
|
||||
nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack);
|
||||
nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames);
|
||||
|
||||
if (numOtherAvatars == 0) {
|
||||
// update the full rate distance to FLOAT_MAX since we didn't have any other avatars to send
|
||||
|
@ -334,13 +366,36 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
|
||||
if (killedNode->getType() == NodeType::Agent
|
||||
&& killedNode->getLinkedData()) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// this was an avatar we were sending to other people
|
||||
// send a kill packet for it to our other nodes
|
||||
QByteArray killPacket = byteArrayWithPopulatedHeader(PacketTypeKillAvatar);
|
||||
QByteArray killPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeKillAvatar);
|
||||
killPacket += killedNode->getUUID().toRfc4122();
|
||||
|
||||
DependencyManager::get<NodeList>()->broadcastToNodes(killPacket,
|
||||
NodeSet() << NodeType::Agent);
|
||||
nodeList->broadcastToNodes(killPacket, NodeSet() << NodeType::Agent);
|
||||
|
||||
// we also want to remove sequence number data for this avatar on our other avatars
|
||||
// so invoke the appropriate method on the AvatarMixerClientData for other avatars
|
||||
nodeList->eachMatchingNode(
|
||||
[&](const SharedNodePointer& node)->bool {
|
||||
if (!node->getLinkedData()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node->getUUID() == killedNode->getUUID()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
[&](const SharedNodePointer& node) {
|
||||
QMetaObject::invokeMethod(node->getLinkedData(),
|
||||
"removeLastBroadcastSequenceNumber",
|
||||
Qt::AutoConnection,
|
||||
Q_ARG(const QUuid&, QUuid(killedNode->getUUID())));
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,11 +485,14 @@ void AvatarMixer::sendStatsPacket() {
|
|||
|
||||
AvatarMixerClientData* clientData = static_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||
if (clientData) {
|
||||
clientData->loadJSONStats(avatarStats);
|
||||
MutexTryLocker lock(clientData->getMutex());
|
||||
if (lock.isLocked()) {
|
||||
clientData->loadJSONStats(avatarStats);
|
||||
|
||||
// add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only
|
||||
avatarStats["delta_full_vs_avatar_data_kbps"] =
|
||||
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble();
|
||||
// add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only
|
||||
avatarStats["delta_full_vs_avatar_data_kbps"] =
|
||||
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
avatarsObject[uuidStringWithoutCurlyBraces(node->getUUID())] = avatarStats;
|
||||
|
@ -455,10 +513,12 @@ void AvatarMixer::run() {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;
|
||||
nodeList->linkedDataCreateCallback = [] (Node* node) {
|
||||
node->setLinkedData(new AvatarMixerClientData());
|
||||
};
|
||||
|
||||
// setup the timer that will be fired on the broadcast thread
|
||||
_broadcastTimer = new QTimer();
|
||||
_broadcastTimer = new QTimer;
|
||||
_broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS);
|
||||
_broadcastTimer->moveToThread(&_broadcastThread);
|
||||
|
||||
|
|
|
@ -25,11 +25,24 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() {
|
|||
return oldValue;
|
||||
}
|
||||
|
||||
PacketSequenceNumber 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;
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
||||
jsonObject["display_name"] = _avatar.getDisplayName();
|
||||
jsonObject["full_rate_distance"] = _fullRateDistance;
|
||||
jsonObject["max_avatar_distance"] = _maxAvatarDistance;
|
||||
jsonObject["num_avatars_sent_last_frame"] = _numAvatarsSentLastFrame;
|
||||
jsonObject["avg_other_avatar_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond();
|
||||
jsonObject["avg_other_avatar_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond();
|
||||
jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends;
|
||||
|
||||
jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cfloat>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QUrl>
|
||||
|
@ -21,7 +22,9 @@
|
|||
#include <AvatarData.h>
|
||||
#include <NodeData.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
#include <UUIDHasher.h>
|
||||
|
||||
const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps";
|
||||
|
||||
|
@ -32,7 +35,12 @@ public:
|
|||
AvatarData& getAvatar() { return _avatar; }
|
||||
|
||||
bool checkAndSetHasReceivedFirstPackets();
|
||||
|
||||
|
||||
PacketSequenceNumber getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
|
||||
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, PacketSequenceNumber sequenceNumber)
|
||||
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
|
||||
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
|
||||
|
||||
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
|
||||
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
|
||||
|
||||
|
@ -49,6 +57,14 @@ public:
|
|||
void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; }
|
||||
int getNumAvatarsSentLastFrame() const { return _numAvatarsSentLastFrame; }
|
||||
|
||||
void recordNumOtherAvatarStarves(int numAvatarsHeldBack) { _otherAvatarStarves.updateAverage((float) numAvatarsHeldBack); }
|
||||
float getAvgNumOtherAvatarStarvesPerSecond() const { return _otherAvatarStarves.getAverageSampleValuePerSecond(); }
|
||||
|
||||
void recordNumOtherAvatarSkips(int numOtherAvatarSkips) { _otherAvatarSkips.updateAverage((float) numOtherAvatarSkips); }
|
||||
float getAvgNumOtherAvatarSkipsPerSecond() const { return _otherAvatarSkips.getAverageSampleValuePerSecond(); }
|
||||
|
||||
void incrementNumOutOfOrderSends() { ++_numOutOfOrderSends; }
|
||||
|
||||
int getNumFramesSinceFRDAdjustment() const { return _numFramesSinceAdjustment; }
|
||||
void incrementNumFramesSinceFRDAdjustment() { ++_numFramesSinceAdjustment; }
|
||||
void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; }
|
||||
|
@ -61,13 +77,23 @@ public:
|
|||
void loadJSONStats(QJsonObject& jsonObject) const;
|
||||
private:
|
||||
AvatarData _avatar;
|
||||
|
||||
std::unordered_map<QUuid, PacketSequenceNumber, UUIDHasher> _lastBroadcastSequenceNumbers;
|
||||
|
||||
bool _hasReceivedFirstPackets = false;
|
||||
quint64 _billboardChangeTimestamp = 0;
|
||||
quint64 _identityChangeTimestamp = 0;
|
||||
|
||||
float _fullRateDistance = FLT_MAX;
|
||||
float _maxAvatarDistance = FLT_MAX;
|
||||
|
||||
int _numAvatarsSentLastFrame = 0;
|
||||
int _numFramesSinceAdjustment = 0;
|
||||
|
||||
SimpleMovingAverage _otherAvatarStarves;
|
||||
SimpleMovingAverage _otherAvatarSkips;
|
||||
int _numOutOfOrderSends = 0;
|
||||
|
||||
SimpleMovingAverage _avgOtherAvatarDataRate;
|
||||
};
|
||||
|
||||
|
|
|
@ -64,7 +64,9 @@ void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePo
|
|||
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
||||
unsigned char* copyAt = outputBuffer;
|
||||
|
||||
int numBytesPacketHeader = populatePacketHeader(reinterpret_cast<char*>(outputBuffer), PacketTypeEntityAddResponse);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
int numBytesPacketHeader = nodeList->populatePacketHeader(reinterpret_cast<char*>(outputBuffer), PacketTypeEntityAddResponse);
|
||||
int packetLength = numBytesPacketHeader;
|
||||
copyAt += numBytesPacketHeader;
|
||||
|
||||
|
@ -81,7 +83,7 @@ void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePo
|
|||
copyAt += sizeof(entityID);
|
||||
packetLength += sizeof(entityID);
|
||||
|
||||
DependencyManager::get<NodeList>()->writeDatagram((char*) outputBuffer, packetLength, senderNode);
|
||||
nodeList->writeDatagram((char*) outputBuffer, packetLength, senderNode);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,10 +9,15 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#include "AssignmentClientApp.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
AssignmentClientApp app(argc, argv);
|
||||
return 0;
|
||||
|
||||
int acReturn = app.exec();
|
||||
qDebug() << "assignment-client process" << app.applicationPid() << "exiting with status code" << acReturn;
|
||||
|
||||
return acReturn;
|
||||
}
|
||||
|
|
|
@ -277,9 +277,11 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
|
||||
char* dataAt = packet;
|
||||
int bytesRemaining = MAX_PACKET_SIZE;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// pack header
|
||||
int numBytesPacketHeader = populatePacketHeader(packet, _myServer->getMyEditNackType());
|
||||
int numBytesPacketHeader = nodeList->populatePacketHeader(packet, _myServer->getMyEditNackType());
|
||||
dataAt += numBytesPacketHeader;
|
||||
bytesRemaining -= numBytesPacketHeader;
|
||||
|
||||
|
@ -301,7 +303,7 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
numSequenceNumbersAvailable -= numSequenceNumbers;
|
||||
|
||||
// send it
|
||||
DependencyManager::get<NodeList>()->writeUnverifiedDatagram(packet, dataAt - packet, destinationNode);
|
||||
nodeList->writeUnverifiedDatagram(packet, dataAt - packet, destinationNode);
|
||||
packetsSent++;
|
||||
|
||||
qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID;
|
||||
|
|
|
@ -9,11 +9,15 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "PacketHeaders.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "OctreeQueryNode.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "OctreeSendThread.h"
|
||||
|
||||
OctreeQueryNode::OctreeQueryNode() :
|
||||
|
@ -91,8 +95,8 @@ void OctreeQueryNode::sendThreadFinished() {
|
|||
}
|
||||
}
|
||||
|
||||
void OctreeQueryNode::initializeOctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node) {
|
||||
_octreeSendThread = new OctreeSendThread(myAssignment, node);
|
||||
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) {
|
||||
_octreeSendThread = new OctreeSendThread(myServer, node);
|
||||
|
||||
// we want to be notified when the thread finishes
|
||||
connect(_octreeSendThread, &GenericThread::finished, this, &OctreeQueryNode::sendThreadFinished);
|
||||
|
@ -189,7 +193,9 @@ void OctreeQueryNode::resetOctreePacket() {
|
|||
}
|
||||
|
||||
_octreePacketAvailableBytes = MAX_PACKET_SIZE;
|
||||
int numBytesPacketHeader = populatePacketHeader(reinterpret_cast<char*>(_octreePacket), _myPacketType);
|
||||
int numBytesPacketHeader = DependencyManager::get<NodeList>()->populatePacketHeader(reinterpret_cast<char*>(_octreePacket),
|
||||
_myPacketType);
|
||||
|
||||
_octreePacketAt = _octreePacket + numBytesPacketHeader;
|
||||
_octreePacketAvailableBytes -= numBytesPacketHeader;
|
||||
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
#include <OctreePacketData.h>
|
||||
#include <OctreeQuery.h>
|
||||
#include <OctreeSceneStats.h>
|
||||
#include <ThreadedAssignment.h> // for SharedAssignmentPointer
|
||||
#include "SentPacketHistory.h"
|
||||
#include <qqueue.h>
|
||||
|
||||
class OctreeSendThread;
|
||||
class OctreeServer;
|
||||
|
||||
class OctreeQueryNode : public OctreeQuery {
|
||||
Q_OBJECT
|
||||
|
@ -89,7 +89,7 @@ public:
|
|||
|
||||
OctreeSceneStats stats;
|
||||
|
||||
void initializeOctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node);
|
||||
void initializeOctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node);
|
||||
bool isOctreeSendThreadInitalized() { return _octreeSendThread; }
|
||||
|
||||
void dumpOutOfView();
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
quint64 startSceneSleepTime = 0;
|
||||
quint64 endSceneSleepTime = 0;
|
||||
|
||||
OctreeSendThread::OctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node) :
|
||||
_myAssignment(myAssignment),
|
||||
_myServer(static_cast<OctreeServer*>(myAssignment.data())),
|
||||
OctreeSendThread::OctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) :
|
||||
_myServer(myServer),
|
||||
_node(node),
|
||||
_nodeUUID(node->getUUID()),
|
||||
_packetData(),
|
||||
|
@ -31,9 +30,14 @@ OctreeSendThread::OctreeSendThread(const SharedAssignmentPointer& myAssignment,
|
|||
_isShuttingDown(false)
|
||||
{
|
||||
QString safeServerName("Octree");
|
||||
|
||||
// set our QThread object name so we can identify this thread while debugging
|
||||
setObjectName(QString("Octree Send Thread (%1)").arg(uuidStringWithoutCurlyBraces(node->getUUID())));
|
||||
|
||||
if (_myServer) {
|
||||
safeServerName = _myServer->getMyServerName();
|
||||
}
|
||||
|
||||
qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client connected "
|
||||
"- starting sending thread [" << this << "]";
|
||||
|
||||
|
@ -53,7 +57,6 @@ OctreeSendThread::~OctreeSendThread() {
|
|||
OctreeServer::stopTrackingThread(this);
|
||||
|
||||
_node.clear();
|
||||
_myAssignment.clear();
|
||||
}
|
||||
|
||||
void OctreeSendThread::setIsShuttingDown() {
|
||||
|
@ -66,15 +69,13 @@ bool OctreeSendThread::process() {
|
|||
return false; // exit early if we're shutting down
|
||||
}
|
||||
|
||||
// check that our server and assignment is still valid
|
||||
if (!_myServer || !_myAssignment) {
|
||||
return false; // exit early if it's not, it means the server is shutting down
|
||||
}
|
||||
|
||||
OctreeServer::didProcess(this);
|
||||
|
||||
quint64 start = usecTimestampNow();
|
||||
|
||||
// we'd better have a server at this point, or we're in trouble
|
||||
assert(_myServer);
|
||||
|
||||
// don't do any send processing until the initial load of the octree is complete...
|
||||
if (_myServer->isInitialLoadComplete()) {
|
||||
if (_node) {
|
||||
|
@ -290,7 +291,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
}
|
||||
|
||||
// calculate max number of packets that can be sent during this interval
|
||||
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND));
|
||||
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
|
||||
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
|
||||
|
||||
int truePacketsSent = 0;
|
||||
|
|
|
@ -26,7 +26,7 @@ class OctreeServer;
|
|||
class OctreeSendThread : public GenericThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OctreeSendThread(const SharedAssignmentPointer& myAssignment, const SharedNodePointer& node);
|
||||
OctreeSendThread(OctreeServer* myServer, const SharedNodePointer& node);
|
||||
virtual ~OctreeSendThread();
|
||||
|
||||
void setIsShuttingDown();
|
||||
|
@ -43,7 +43,6 @@ protected:
|
|||
virtual bool process();
|
||||
|
||||
private:
|
||||
SharedAssignmentPointer _myAssignment;
|
||||
OctreeServer* _myServer;
|
||||
SharedNodePointer _node;
|
||||
QUuid _nodeUUID;
|
||||
|
|
|
@ -213,14 +213,6 @@ void OctreeServer::trackProcessWaitTime(float time) {
|
|||
_averageProcessWaitTime.updateAverage(time);
|
||||
}
|
||||
|
||||
void OctreeServer::attachQueryNodeToNode(Node* newNode) {
|
||||
if (!newNode->getLinkedData() && _instance) {
|
||||
OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode();
|
||||
newQueryNodeData->init();
|
||||
newNode->setLinkedData(newQueryNodeData);
|
||||
}
|
||||
}
|
||||
|
||||
OctreeServer::OctreeServer(const QByteArray& packet) :
|
||||
ThreadedAssignment(packet),
|
||||
_argc(0),
|
||||
|
@ -836,42 +828,42 @@ void OctreeServer::parsePayload() {
|
|||
|
||||
void OctreeServer::readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// If we know we're shutting down we just drop these packets on the floor.
|
||||
// This stops us from initializing send threads we just shut down.
|
||||
|
||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
PacketType packetType = packetTypeForPacket(receivedPacket);
|
||||
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
if (packetType == getMyQueryMessageType()) {
|
||||
// If we got a query packet, then we're talking to an agent, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
if (matchingNode) {
|
||||
nodeList->updateNodeWithDataFromPacket(matchingNode, receivedPacket);
|
||||
OctreeQueryNode* nodeData = (OctreeQueryNode*)matchingNode->getLinkedData();
|
||||
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
|
||||
if (!_isShuttingDown) {
|
||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
PacketType packetType = packetTypeForPacket(receivedPacket);
|
||||
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
if (packetType == getMyQueryMessageType()) {
|
||||
// If we got a query packet, then we're talking to an agent, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
if (matchingNode) {
|
||||
nodeList->updateNodeWithDataFromPacket(matchingNode, receivedPacket);
|
||||
|
||||
// NOTE: this is an important aspect of the proper ref counting. The send threads/node data need to
|
||||
// know that the OctreeServer/Assignment will not get deleted on it while it's still active. The
|
||||
// solution is to get the shared pointer for the current assignment. We need to make sure this is the
|
||||
// same SharedAssignmentPointer that was ref counted by the assignment client.
|
||||
SharedAssignmentPointer sharedAssignment = AssignmentClient::getCurrentAssignment();
|
||||
nodeData->initializeOctreeSendThread(sharedAssignment, matchingNode);
|
||||
OctreeQueryNode* nodeData = (OctreeQueryNode*) matchingNode->getLinkedData();
|
||||
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
|
||||
nodeData->initializeOctreeSendThread(this, matchingNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (packetType == PacketTypeOctreeDataNack) {
|
||||
// If we got a nack packet, then we're talking to an agent, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
if (matchingNode) {
|
||||
OctreeQueryNode* nodeData = (OctreeQueryNode*)matchingNode->getLinkedData();
|
||||
if (nodeData) {
|
||||
nodeData->parseNackPacket(receivedPacket);
|
||||
} else if (packetType == PacketTypeOctreeDataNack) {
|
||||
// If we got a nack packet, then we're talking to an agent, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
if (matchingNode) {
|
||||
OctreeQueryNode* nodeData = (OctreeQueryNode*)matchingNode->getLinkedData();
|
||||
if (nodeData) {
|
||||
nodeData->parseNackPacket(receivedPacket);
|
||||
}
|
||||
}
|
||||
} else if (packetType == PacketTypeJurisdictionRequest) {
|
||||
_jurisdictionSender->queueReceivedPacket(matchingNode, receivedPacket);
|
||||
} else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) {
|
||||
_octreeInboundPacketProcessor->queueReceivedPacket(matchingNode, receivedPacket);
|
||||
} else {
|
||||
// let processNodeData handle it.
|
||||
DependencyManager::get<NodeList>()->processNodeData(senderSockAddr, receivedPacket);
|
||||
}
|
||||
} else if (packetType == PacketTypeJurisdictionRequest) {
|
||||
_jurisdictionSender->queueReceivedPacket(matchingNode, receivedPacket);
|
||||
} else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) {
|
||||
_octreeInboundPacketProcessor->queueReceivedPacket(matchingNode, receivedPacket);
|
||||
} else {
|
||||
// let processNodeData handle it.
|
||||
DependencyManager::get<NodeList>()->processNodeData(senderSockAddr, receivedPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1132,7 +1124,11 @@ void OctreeServer::run() {
|
|||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
||||
nodeList->linkedDataCreateCallback = &OctreeServer::attachQueryNodeToNode;
|
||||
nodeList->linkedDataCreateCallback = [] (Node* node) {
|
||||
OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode();
|
||||
newQueryNodeData->init();
|
||||
node->setLinkedData(newQueryNodeData);
|
||||
};
|
||||
|
||||
srand((unsigned)time(0));
|
||||
|
||||
|
@ -1220,8 +1216,16 @@ void OctreeServer::forceNodeShutdown(SharedNodePointer node) {
|
|||
|
||||
void OctreeServer::aboutToFinish() {
|
||||
qDebug() << qPrintable(_safeServerName) << "server STARTING about to finish...";
|
||||
|
||||
_isShuttingDown = true;
|
||||
|
||||
qDebug() << qPrintable(_safeServerName) << "inform Octree Inbound Packet Processor that we are shutting down...";
|
||||
|
||||
// we're going down - set the NodeList linkedDataCallback to NULL so we do not create any more OctreeQueryNode objects.
|
||||
// This ensures that when we forceNodeShutdown below for each node we don't get any more newly connecting nodes
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->linkedDataCreateCallback = NULL;
|
||||
|
||||
if (_octreeInboundPacketProcessor) {
|
||||
_octreeInboundPacketProcessor->terminating();
|
||||
}
|
||||
|
@ -1230,7 +1234,9 @@ void OctreeServer::aboutToFinish() {
|
|||
_jurisdictionSender->terminating();
|
||||
}
|
||||
|
||||
DependencyManager::get<NodeList>()->eachNode([this](const SharedNodePointer& node) {
|
||||
// force a shutdown of all of our OctreeSendThreads - at this point it has to be impossible for a
|
||||
// linkedDataCreateCallback to be called for a new node
|
||||
nodeList->eachNode([this](const SharedNodePointer& node) {
|
||||
qDebug() << qPrintable(_safeServerName) << "server about to finish while node still connected node:" << *node;
|
||||
forceNodeShutdown(node);
|
||||
});
|
||||
|
|
|
@ -75,8 +75,6 @@ public:
|
|||
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node) { return false; }
|
||||
virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { return 0; }
|
||||
|
||||
static void attachQueryNodeToNode(Node* newNode);
|
||||
|
||||
static float SKIP_TIME; // use this for trackXXXTime() calls for non-times
|
||||
|
||||
static void trackLoopTime(float time) { _averageLoopTime.updateAverage(time); }
|
||||
|
@ -146,12 +144,14 @@ protected:
|
|||
QString getStatusLink();
|
||||
|
||||
void setupDatagramProcessingThread();
|
||||
|
||||
|
||||
int _argc;
|
||||
const char** _argv;
|
||||
char** _parsedArgV;
|
||||
QJsonObject _settings;
|
||||
|
||||
bool _isShuttingDown = false;
|
||||
|
||||
HTTPManager* _httpManager;
|
||||
int _statusPort;
|
||||
QString _statusHost;
|
||||
|
|
|
@ -69,6 +69,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
_iceServerSocket(ICE_SERVER_DEFAULT_HOSTNAME, ICE_SERVER_DEFAULT_PORT)
|
||||
{
|
||||
LogUtils::init();
|
||||
Setting::init();
|
||||
|
||||
setOrganizationName("High Fidelity");
|
||||
setOrganizationDomain("highfidelity.io");
|
||||
|
@ -619,10 +620,12 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
|
||||
packetStream >> nodeInterestList >> username >> usernameSignature;
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
QString reason;
|
||||
if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr, reason)) {
|
||||
// this is an agent and we've decided we won't let them connect - send them a packet to deny connection
|
||||
QByteArray connectionDeniedByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied);
|
||||
QByteArray connectionDeniedByteArray = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied);
|
||||
QDataStream out(&connectionDeniedByteArray, QIODevice::WriteOnly | QIODevice::Append);
|
||||
out << reason;
|
||||
// tell client it has been refused.
|
||||
|
@ -664,10 +667,9 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
canRez = canAdjustLocks;
|
||||
}
|
||||
|
||||
SharedNodePointer newNode =
|
||||
DependencyManager::get<LimitedNodeList>()->addOrUpdateNode(nodeUUID, nodeType,
|
||||
publicSockAddr, localSockAddr,
|
||||
canAdjustLocks, canRez);
|
||||
SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeUUID, nodeType,
|
||||
publicSockAddr, localSockAddr,
|
||||
canAdjustLocks, canRez);
|
||||
// when the newNode is created the linked data is also created
|
||||
// if this was a static assignment set the UUID, set the sendingSockAddr
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||
|
@ -926,8 +928,8 @@ NodeSet DomainServer::nodeInterestListFromPacket(const QByteArray& packet, int n
|
|||
|
||||
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr,
|
||||
const NodeSet& nodeInterestList) {
|
||||
|
||||
QByteArray broadcastPacket = byteArrayWithPopulatedHeader(PacketTypeDomainList);
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
QByteArray broadcastPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainList);
|
||||
|
||||
// always send the node their own UUID back
|
||||
QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append);
|
||||
|
@ -939,8 +941,6 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
// if we've established a connection via ICE with this peer, use that socket
|
||||
// otherwise just try to reply back to them on their sending socket (although that may not work)
|
||||
HifiSockAddr destinationSockAddr = _connectedICEPeers.value(node->getUUID());
|
||||
|
@ -955,7 +955,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
|
||||
if (nodeData->isAuthenticated()) {
|
||||
// if this authenticated node has any interest types, send back those nodes as well
|
||||
nodeList->eachNode([&](const SharedNodePointer& otherNode){
|
||||
limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){
|
||||
// reset our nodeByteArray and nodeDataStream
|
||||
QByteArray nodeByteArray;
|
||||
QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append);
|
||||
|
@ -986,7 +986,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
// we need to break here and start a new packet
|
||||
// so send the current one
|
||||
|
||||
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
|
||||
limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr);
|
||||
|
||||
// reset the broadcastPacket structure
|
||||
broadcastPacket.resize(numBroadcastPacketLeadBytes);
|
||||
|
@ -999,26 +999,26 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// always write the last broadcastPacket
|
||||
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
|
||||
limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr);
|
||||
}
|
||||
|
||||
void DomainServer::readAvailableDatagrams() {
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
HifiSockAddr senderSockAddr;
|
||||
QByteArray receivedPacket;
|
||||
|
||||
static QByteArray assignmentPacket = byteArrayWithPopulatedHeader(PacketTypeCreateAssignment);
|
||||
static QByteArray assignmentPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeCreateAssignment);
|
||||
static int numAssignmentPacketHeaderBytes = assignmentPacket.size();
|
||||
|
||||
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
|
||||
receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
|
||||
nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(),
|
||||
while (limitedNodeList->getNodeSocket().hasPendingDatagrams()) {
|
||||
receivedPacket.resize(limitedNodeList->getNodeSocket().pendingDatagramSize());
|
||||
limitedNodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(),
|
||||
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
|
||||
if (packetTypeForPacket(receivedPacket) == PacketTypeRequestAssignment
|
||||
&& nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
&& limitedNodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
|
||||
// construct the requested assignment from the packet data
|
||||
Assignment requestAssignment(receivedPacket);
|
||||
|
@ -1059,7 +1059,7 @@ void DomainServer::readAvailableDatagrams() {
|
|||
|
||||
assignmentStream << uniqueAssignment;
|
||||
|
||||
nodeList->getNodeSocket().writeDatagram(assignmentPacket,
|
||||
limitedNodeList->getNodeSocket().writeDatagram(assignmentPacket,
|
||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
|
||||
// add the information for that deployed assignment to the hash of pending assigned nodes
|
||||
|
@ -1081,16 +1081,16 @@ void DomainServer::readAvailableDatagrams() {
|
|||
processDatagram(receivedPacket, senderSockAddr);
|
||||
} else {
|
||||
// we're using DTLS, so tell the sender to get back to us using DTLS
|
||||
static QByteArray dtlsRequiredPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerRequireDTLS);
|
||||
static QByteArray dtlsRequiredPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainServerRequireDTLS);
|
||||
static int numBytesDTLSHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainServerRequireDTLS);
|
||||
|
||||
if (dtlsRequiredPacket.size() == numBytesDTLSHeader) {
|
||||
// pack the port that we accept DTLS traffic on
|
||||
unsigned short dtlsPort = nodeList->getDTLSSocket().localPort();
|
||||
unsigned short dtlsPort = limitedNodeList->getDTLSSocket().localPort();
|
||||
dtlsRequiredPacket.replace(numBytesDTLSHeader, sizeof(dtlsPort), reinterpret_cast<const char*>(&dtlsPort));
|
||||
}
|
||||
|
||||
nodeList->writeUnverifiedDatagram(dtlsRequiredPacket, senderSockAddr);
|
||||
limitedNodeList->writeUnverifiedDatagram(dtlsRequiredPacket, senderSockAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1197,7 +1197,11 @@ PropertiesTool = function(opts) {
|
|||
|
||||
webView.eventBridge.webEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
if (data.type == "update") {
|
||||
if (data.type == "print") {
|
||||
if (data.message) {
|
||||
print(data.message);
|
||||
}
|
||||
} else if (data.type == "update") {
|
||||
selectionManager.saveProperties();
|
||||
if (selectionManager.selections.length > 1) {
|
||||
properties = {
|
||||
|
|
|
@ -35,12 +35,14 @@ var zoneEntityA = Entities.addEntity({
|
|||
scatteringWavelengths: { x: 0.650, y: 0.570, z: 0.475 },
|
||||
hasStars: true
|
||||
},
|
||||
stageLatitude: 37.777,
|
||||
stageLongitude: 122.407,
|
||||
stageAltitude: 0.03,
|
||||
stageDay: 183,
|
||||
stageHour: 5,
|
||||
stageSunModelEnabled: true
|
||||
stage: {
|
||||
latitude: 37.777,
|
||||
longitude: 122.407,
|
||||
altitude: 0.03,
|
||||
day: 183,
|
||||
hour: 5,
|
||||
sunModelEnabled: true
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -33,12 +33,14 @@ var zoneEntityA = Entities.addEntity({
|
|||
scatteringWavelengths: { x: 0.650, y: 0.570, z: 0.475 },
|
||||
hasStars: false
|
||||
},
|
||||
stageLatitude: 37.777,
|
||||
stageLongitude: 122.407,
|
||||
stageAltitude: 0.03,
|
||||
stageDay: 60,
|
||||
stageHour: 0,
|
||||
stageSunModelEnabled: true
|
||||
stage: {
|
||||
latitude: 37.777,
|
||||
longitude: 122.407,
|
||||
altitude: 0.03,
|
||||
day: 60,
|
||||
hour: 0,
|
||||
sunModelEnabled: true
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -34,17 +34,18 @@ var zoneEntityB = Entities.addEntity({
|
|||
dimensions: { x: 2, y: 2, z: 2 },
|
||||
keyLightColor: { red: 0, green: 255, blue: 0 },
|
||||
keyLightIntensity: 0.9,
|
||||
stageLatitude: 37.777,
|
||||
stageLongitude: 122.407,
|
||||
stageAltitude: 0.03,
|
||||
stageDay: 60,
|
||||
stageHour: 12,
|
||||
stageSunModelEnabled: true
|
||||
stage: {
|
||||
latitude: 37.777,
|
||||
longitude: 122.407,
|
||||
altitude: 0.03,
|
||||
day: 60,
|
||||
hour: 0,
|
||||
sunModelEnabled: true
|
||||
}
|
||||
});
|
||||
|
||||
print("zoneEntityB:" + zoneEntityB);
|
||||
|
||||
|
||||
var zoneEntityC = Entities.addEntity({
|
||||
type: "Zone",
|
||||
position: { x: 5, y: 10, z: 5 },
|
||||
|
@ -59,7 +60,6 @@ var zoneEntityC = Entities.addEntity({
|
|||
|
||||
print("zoneEntityC:" + zoneEntityC);
|
||||
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Script.update.connect(function(deltaTime) {
|
||||
// stop it...
|
||||
|
|
|
@ -37,12 +37,14 @@ var zoneEntityA = Entities.addEntity({
|
|||
color: { red: 255, green: 0, blue: 255 },
|
||||
url: ""
|
||||
},
|
||||
stageLatitude: 37.777,
|
||||
stageLongitude: 122.407,
|
||||
stageAltitude: 0.03,
|
||||
stageDay: 60,
|
||||
stageHour: 0,
|
||||
stageSunModelEnabled: true
|
||||
stage: {
|
||||
latitude: 37.777,
|
||||
longitude: 122.407,
|
||||
altitude: 0.03,
|
||||
day: 60,
|
||||
hour: 0,
|
||||
sunModelEnabled: true
|
||||
}
|
||||
});
|
||||
|
||||
var props = Entities.getEntityProperties(zoneEntityA);
|
||||
|
|
|
@ -27,6 +27,7 @@ var ANGULAR_DAMPING_RATE = 0.40;
|
|||
var SCREEN_TO_METERS = 0.001;
|
||||
var currentPosition, currentVelocity, cameraEntityDistance, currentRotation;
|
||||
var velocityTowardTarget, desiredVelocity, addedVelocity, newVelocity, dPosition, camYaw, distanceToTarget, targetPosition;
|
||||
var originalGravity = {x: 0, y: 0, z: 0};
|
||||
var shouldRotate = false;
|
||||
var dQ, theta, axisAngle, dT;
|
||||
var angularVelocity = {
|
||||
|
@ -55,6 +56,15 @@ var dropLine = Overlays.addOverlay("line3d", {
|
|||
});
|
||||
|
||||
|
||||
function vectorIsZero(v) {
|
||||
return v.x == 0 && v.y == 0 && v.z == 0;
|
||||
}
|
||||
|
||||
function vectorToString(v) {
|
||||
return "(" + v.x + ", " + v.y + ", " + v.z + ")"
|
||||
}
|
||||
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (!event.isLeftButton) {
|
||||
return;
|
||||
|
@ -65,10 +75,17 @@ function mousePressEvent(event) {
|
|||
grabbedEntity = intersection.entityID;
|
||||
var props = Entities.getEntityProperties(grabbedEntity)
|
||||
isGrabbing = true;
|
||||
originalGravity = props.gravity;
|
||||
print("mouse-press setting originalGravity " + originalGravity + " " + vectorToString(originalGravity));
|
||||
targetPosition = props.position;
|
||||
currentPosition = props.position;
|
||||
currentVelocity = props.velocity;
|
||||
updateDropLine(targetPosition);
|
||||
|
||||
Entities.editEntity(grabbedEntity, {
|
||||
gravity: {x: 0, y: 0, z: 0}
|
||||
});
|
||||
|
||||
Audio.playSound(grabSound, {
|
||||
position: props.position,
|
||||
volume: 0.4
|
||||
|
@ -96,6 +113,22 @@ function updateDropLine(position) {
|
|||
function mouseReleaseEvent() {
|
||||
if (isGrabbing) {
|
||||
isGrabbing = false;
|
||||
|
||||
// only restore the original gravity if it's not zero. This is to avoid...
|
||||
// 1. interface A grabs an entity and locally saves off its gravity
|
||||
// 2. interface A sets the entity's gravity to zero
|
||||
// 3. interface B grabs the entity and saves off its gravity (which is zero)
|
||||
// 4. interface A releases the entity and puts the original gravity back
|
||||
// 5. interface B releases the entity and puts the original gravity back (to zero)
|
||||
if (!vectorIsZero(originalGravity)) {
|
||||
print("mouse-release restoring originalGravity" + vectorToString(originalGravity));
|
||||
Entities.editEntity(grabbedEntity, {
|
||||
gravity: originalGravity
|
||||
});
|
||||
} else {
|
||||
print("mouse-release not restoring originalGravity of zero");
|
||||
}
|
||||
|
||||
Overlays.editOverlay(dropLine, {
|
||||
visible: false
|
||||
});
|
||||
|
@ -110,6 +143,13 @@ function mouseReleaseEvent() {
|
|||
|
||||
function mouseMoveEvent(event) {
|
||||
if (isGrabbing) {
|
||||
// see if something added/restored gravity
|
||||
var props = Entities.getEntityProperties(grabbedEntity);
|
||||
if (!vectorIsZero(props.gravity)) {
|
||||
originalGravity = props.gravity;
|
||||
print("mouse-move adopting originalGravity" + vectorToString(originalGravity));
|
||||
}
|
||||
|
||||
deltaMouse.x = event.x - prevMouse.x;
|
||||
if (!moveUpDown) {
|
||||
deltaMouse.z = event.y - prevMouse.y;
|
||||
|
@ -194,7 +234,7 @@ function update(deltaTime) {
|
|||
newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE));
|
||||
// Update entity
|
||||
} else {
|
||||
newVelocity = entityProps.velocity;
|
||||
newVelocity = {x: 0, y: 0, z: 0};
|
||||
}
|
||||
if (shouldRotate) {
|
||||
angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE));
|
||||
|
@ -215,4 +255,4 @@ Controller.mousePressEvent.connect(mousePressEvent);
|
|||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
Script.update.connect(update);
|
||||
Script.update.connect(update);
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
els[i].setAttribute('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function showElements(els, show) {
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
els[i].style.display = (show) ? 'block' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function createEmitCheckedPropertyUpdateFunction(propertyName) {
|
||||
return function() {
|
||||
|
@ -280,6 +286,7 @@
|
|||
var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude");
|
||||
var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude");
|
||||
var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude");
|
||||
var elZoneStageAutomaticHourDay = document.getElementById("property-zone-stage-automatic-hour-day");
|
||||
var elZoneStageDay = document.getElementById("property-zone-stage-day");
|
||||
var elZoneStageHour = document.getElementById("property-zone-stage-hour");
|
||||
|
||||
|
@ -467,7 +474,7 @@
|
|||
elZoneSections[i].style.display = 'block';
|
||||
}
|
||||
|
||||
elZoneStageSunModelEnabled.checked = properties.stageSunModelEnabled;
|
||||
elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled;
|
||||
elZoneKeyLightColorRed.value = properties.keyLightColor.red;
|
||||
elZoneKeyLightColorGreen.value = properties.keyLightColor.green;
|
||||
elZoneKeyLightColorBlue.value = properties.keyLightColor.blue;
|
||||
|
@ -477,11 +484,12 @@
|
|||
elZoneKeyLightDirectionY.value = properties.keyLightDirection.y.toFixed(2);
|
||||
elZoneKeyLightDirectionZ.value = properties.keyLightDirection.z.toFixed(2);
|
||||
|
||||
elZoneStageLatitude.value = properties.stageLatitude.toFixed(2);
|
||||
elZoneStageLongitude.value = properties.stageLongitude.toFixed(2);
|
||||
elZoneStageAltitude.value = properties.stageAltitude.toFixed(2);
|
||||
elZoneStageDay.value = properties.stageDay;
|
||||
elZoneStageHour.value = properties.stageHour;
|
||||
elZoneStageLatitude.value = properties.stage.latitude.toFixed(2);
|
||||
elZoneStageLongitude.value = properties.stage.longitude.toFixed(2);
|
||||
elZoneStageAltitude.value = properties.stage.altitude.toFixed(2);
|
||||
elZoneStageAutomaticHourDay.checked = properties.stage.automaticHourDay;
|
||||
elZoneStageDay.value = properties.stage.day;
|
||||
elZoneStageHour.value = properties.stage.hour;
|
||||
elShapeType.value = properties.shapeType;
|
||||
elCompoundShapeURL.value = properties.compoundShapeURL;
|
||||
|
||||
|
@ -504,6 +512,8 @@
|
|||
elZoneAtmosphereScatteringWavelengthsZ.value = properties.atmosphere.scatteringWavelengths.z;
|
||||
elZoneAtmosphereHasStars.checked = properties.atmosphere.hasStars;
|
||||
|
||||
showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox');
|
||||
showElements(document.getElementsByClassName('atmosphere-section'), elZoneBackgroundMode.value == 'atmosphere');
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
|
@ -619,7 +629,7 @@
|
|||
elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction);
|
||||
elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
|
||||
|
||||
elZoneStageSunModelEnabled.addEventListener('change', createEmitCheckedPropertyUpdateFunction('stageSunModelEnabled'));
|
||||
elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled'));
|
||||
var zoneKeyLightColorChangeFunction = createEmitColorPropertyUpdateFunction(
|
||||
'keyLightColor', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue);
|
||||
elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction);
|
||||
|
@ -633,11 +643,12 @@
|
|||
elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction);
|
||||
elZoneKeyLightDirectionZ.addEventListener('change', zoneKeyLightDirectionChangeFunction);
|
||||
|
||||
elZoneStageLatitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLatitude'));
|
||||
elZoneStageLongitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLongitude'));
|
||||
elZoneStageAltitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageAltitude'));
|
||||
elZoneStageDay.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageDay'));
|
||||
elZoneStageHour.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageHour'));
|
||||
elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude'));
|
||||
elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude'));
|
||||
elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','altitude'));
|
||||
elZoneStageAutomaticHourDay.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','automaticHourDay'));
|
||||
elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','day'));
|
||||
elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','hour'));
|
||||
|
||||
|
||||
elZoneBackgroundMode.addEventListener('change', createEmitTextPropertyUpdateFunction('backgroundMode'));
|
||||
|
@ -1094,6 +1105,14 @@
|
|||
<input class="coord" type='number' id="property-zone-stage-altitude" step="1"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zone-section property">
|
||||
<span class="label">Automatically calculate stage hour and day from location and clock.</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-zone-stage-automatic-hour-day">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="zone-section property">
|
||||
<div class="label">Stage Day</div>
|
||||
<div class="value">
|
||||
|
@ -1117,7 +1136,7 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="zone-section skybox-section property">
|
||||
<div class="label">Skybox Color</div>
|
||||
<div class="value">
|
||||
<div class="input-area">R <input class="coord" type='number' id="property-zone-skybox-color-red"></input></div>
|
||||
|
@ -1125,13 +1144,13 @@
|
|||
<div class="input-area">B <input class="coord" type='number' id="property-zone-skybox-color-blue"></input></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="zone-section skybox-section property">
|
||||
<div class="label">Skybox URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-zone-skybox-url" class="url"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="zone-section atmosphere-section property">
|
||||
<div class="label">Atmosphere Center</div>
|
||||
<div class="value">
|
||||
<div class="input-area">X <br><input class="coord" type='number' id="property-zone-atmosphere-center-x"></input></div>
|
||||
|
@ -1139,31 +1158,31 @@
|
|||
<div class="input-area">Z <br><input class="coord" type='number' id="property-zone-atmosphere-center-z"></input></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="zone-section atmosphere-section property">
|
||||
<div class="label">Atmosphere Inner Radius</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-atmosphere-inner-radius" step="1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="zone-section atmosphere-section property">
|
||||
<div class="label">Atmosphere Outer Radius</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-zone-atmosphere-outer-radius" step="1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="zone-section atmosphere-section property">
|
||||
<div class="label">Atmosphere Mie Scattering</div>
|
||||
<div class="value">
|
||||
<input class="coord no-spin" type='number' id="property-zone-atmosphere-mie-scattering" min="0" max="0.5" step="any"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="zone-section atmosphere-section property">
|
||||
<div class="label">Atmosphere Rayleigh Scattering</div>
|
||||
<div class="value">
|
||||
<input class="coord no-spin" type='number' id="property-zone-atmosphere-rayleigh-scattering" min="0" max="0.5" step="any"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="zone-section atmosphere-section property">
|
||||
<div class="label">Atmosphere Scattering Wavelenghts</div>
|
||||
<div class="value">
|
||||
<div class="input-area">X <br><input class="coord no-spin" type='number' id="property-zone-atmosphere-scattering-wavelengths-x" min="0" max="1" step="any"></input></div>
|
||||
|
@ -1171,7 +1190,7 @@
|
|||
<div class="input-area">Z <br><input class="coord no-spin" type='number' id="property-zone-atmosphere-scattering-wavelengths-z" min="0" max="1" step="any"></input></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-section property">
|
||||
<div class="zone-section atmosphere-section property" style="display:none">
|
||||
<span class="label">Atmosphere Has Stars</span>
|
||||
<span class="value">
|
||||
<input type='checkbox' id="property-zone-atmosphere-has-stars">
|
||||
|
|
68
examples/utilities/diagnostics/loadTestServers.js
Normal file
68
examples/utilities/diagnostics/loadTestServers.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// loadTestServers.js
|
||||
// examples/utilities/diagnostics
|
||||
//
|
||||
// Created by Stephen Birarda on 05/08/15.
|
||||
// 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
|
||||
//
|
||||
// This script is made for load testing HF servers. It connects to the HF servers and sends/receives data.
|
||||
// Run this on an assignment-client.
|
||||
//
|
||||
|
||||
var count = 0;
|
||||
var yawDirection = -1;
|
||||
var yaw = 45;
|
||||
var yawMax = 70;
|
||||
var yawMin = 20;
|
||||
var vantagePoint = {x: 5000, y: 500, z: 5000};
|
||||
|
||||
var isLocal = false;
|
||||
|
||||
// set up our EntityViewer with a position and orientation
|
||||
var orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0);
|
||||
|
||||
EntityViewer.setPosition(vantagePoint);
|
||||
EntityViewer.setOrientation(orientation);
|
||||
EntityViewer.queryOctree();
|
||||
|
||||
Agent.isListeningToAudioStream = true;
|
||||
Agent.isAvatar = true;
|
||||
|
||||
function getRandomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function keepLooking(deltaTime) {
|
||||
count++;
|
||||
|
||||
if (count % getRandomInt(5, 15) == 0) {
|
||||
yaw += yawDirection;
|
||||
orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0);
|
||||
|
||||
if (yaw > yawMax || yaw < yawMin) {
|
||||
yawDirection = yawDirection * -1;
|
||||
}
|
||||
|
||||
EntityViewer.setOrientation(orientation);
|
||||
EntityViewer.queryOctree();
|
||||
|
||||
}
|
||||
|
||||
// approximately every second, consider stopping
|
||||
if (count % 60 == 0) {
|
||||
print("considering stop.... elementCount:" + EntityViewer.getOctreeElementsCount());
|
||||
|
||||
var stopProbability = 0.05; // 5% chance of stopping
|
||||
|
||||
if (Math.random() < stopProbability) {
|
||||
print("stopping.... elementCount:" + EntityViewer.getOctreeElementsCount());
|
||||
Script.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Script.update.connect(keepLooking);
|
|
@ -117,7 +117,7 @@ void IceServer::sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, Q
|
|||
QSet<QUuid>::iterator peerID = connections.begin();
|
||||
|
||||
QByteArray outgoingPacket(MAX_PACKET_SIZE, 0);
|
||||
int currentPacketSize = populatePacketHeader(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id);
|
||||
int currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id);
|
||||
int numHeaderBytes = currentPacketSize;
|
||||
|
||||
// go through the connections, sending packets containing connection information for those nodes
|
||||
|
@ -136,7 +136,7 @@ void IceServer::sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, Q
|
|||
destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
||||
|
||||
// reset the packet size to our number of header bytes
|
||||
currentPacketSize = populatePacketHeader(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id);
|
||||
currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id);
|
||||
}
|
||||
|
||||
// append the current peer bytes
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <QAbstractNativeEventFilter>
|
||||
#include <QActionGroup>
|
||||
#include <QColorDialog>
|
||||
#include <QCoreApplication>
|
||||
#include <QDesktopWidget>
|
||||
#include <QCheckBox>
|
||||
#include <QImage>
|
||||
|
@ -183,6 +184,8 @@ const QString CHECK_VERSION_URL = "https://highfidelity.com/latestVersion.xml";
|
|||
const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
|
||||
|
||||
const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js";
|
||||
Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS);
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
class MyNativeEventFilter : public QAbstractNativeEventFilter {
|
||||
|
@ -248,6 +251,8 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
|
||||
|
||||
Setting::init();
|
||||
|
||||
// Set dependencies
|
||||
auto addressManager = DependencyManager::set<AddressManager>();
|
||||
|
@ -333,12 +338,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_isVSyncOn(true),
|
||||
_aboutToQuit(false),
|
||||
_notifiedPacketVersionMismatchThisDomain(false),
|
||||
_domainConnectionRefusals(QList<QString>())
|
||||
_domainConnectionRefusals(QList<QString>()),
|
||||
_maxOctreePPS(maxOctreePacketsPerSecond.get())
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
installNativeEventFilter(&MyNativeEventFilter::getInstance());
|
||||
#endif
|
||||
|
||||
|
||||
_logger = new FileLogger(this); // After setting organization name in order to get correct directory
|
||||
|
||||
|
@ -2577,7 +2582,7 @@ int Application::sendNackPackets() {
|
|||
int bytesRemaining = MAX_PACKET_SIZE;
|
||||
|
||||
// pack header
|
||||
int numBytesPacketHeader = populatePacketHeader(packet, PacketTypeOctreeDataNack);
|
||||
int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeOctreeDataNack);
|
||||
dataAt += numBytesPacketHeader;
|
||||
bytesRemaining -= numBytesPacketHeader;
|
||||
|
||||
|
@ -2683,7 +2688,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
int perServerPPS = 0;
|
||||
const int SMALL_BUDGET = 10;
|
||||
int perUnknownServer = SMALL_BUDGET;
|
||||
int totalPPS = _octreeQuery.getMaxOctreePacketsPerSecond();
|
||||
int totalPPS = getMaxOctreePacketsPerSecond();
|
||||
|
||||
// determine PPS based on number of servers
|
||||
if (inViewServers >= 1) {
|
||||
|
@ -2746,7 +2751,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
}
|
||||
|
||||
if (inView) {
|
||||
_octreeQuery.setMaxOctreePacketsPerSecond(perServerPPS);
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perServerPPS);
|
||||
} else if (unknownView) {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "no known jurisdiction for node " << *node << ", give it budget of "
|
||||
|
@ -2770,15 +2775,15 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
qCDebug(interfaceapp) << "Using regular camera position for node" << *node;
|
||||
}
|
||||
}
|
||||
_octreeQuery.setMaxOctreePacketsPerSecond(perUnknownServer);
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perUnknownServer);
|
||||
} else {
|
||||
_octreeQuery.setMaxOctreePacketsPerSecond(0);
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(0);
|
||||
}
|
||||
// set up the packet for sending...
|
||||
unsigned char* endOfQueryPacket = queryPacket;
|
||||
|
||||
// insert packet type/version and node UUID
|
||||
endOfQueryPacket += populatePacketHeader(reinterpret_cast<char*>(endOfQueryPacket), packetType);
|
||||
endOfQueryPacket += nodeList->populatePacketHeader(reinterpret_cast<char*>(endOfQueryPacket), packetType);
|
||||
|
||||
// encode the query data...
|
||||
endOfQueryPacket += _octreeQuery.getBroadcastData(endOfQueryPacket);
|
||||
|
@ -3203,6 +3208,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
|
|||
|
||||
// Background rendering decision
|
||||
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
||||
auto skybox = model::SkyboxPointer();
|
||||
if (skyStage->getBackgroundMode() == model::SunSkyStage::NO_BACKGROUND) {
|
||||
} else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_DOME) {
|
||||
if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
|
||||
|
@ -3250,7 +3256,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
|
|||
|
||||
}
|
||||
} else if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_BOX) {
|
||||
auto skybox = skyStage->getSkybox();
|
||||
skybox = skyStage->getSkybox();
|
||||
if (skybox) {
|
||||
gpu::Batch batch;
|
||||
model::Skybox::render(batch, _viewFrustum, *skybox);
|
||||
|
@ -3329,6 +3335,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
|
|||
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
||||
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity());
|
||||
DependencyManager::get<DeferredLightingEffect>()->setGlobalAtmosphere(skyStage->getAtmosphere());
|
||||
// NOt yet DependencyManager::get<DeferredLightingEffect>()->setGlobalSkybox(skybox);
|
||||
|
||||
PROFILE_RANGE("DeferredLighting");
|
||||
PerformanceTimer perfTimer("lighting");
|
||||
|
@ -4648,3 +4655,14 @@ PickRay Application::computePickRay() const {
|
|||
bool Application::hasFocus() const {
|
||||
return _glWidget->hasFocus();
|
||||
}
|
||||
|
||||
void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) {
|
||||
if (maxOctreePPS != _maxOctreePPS) {
|
||||
_maxOctreePPS = maxOctreePPS;
|
||||
maxOctreePacketsPerSecond.set(_maxOctreePPS);
|
||||
}
|
||||
}
|
||||
|
||||
int Application::getMaxOctreePacketsPerSecond() {
|
||||
return _maxOctreePPS;
|
||||
}
|
||||
|
|
|
@ -344,6 +344,9 @@ public:
|
|||
bool canAcceptURL(const QString& url);
|
||||
bool acceptURL(const QString& url);
|
||||
|
||||
void setMaxOctreePacketsPerSecond(int maxOctreePPS);
|
||||
int getMaxOctreePacketsPerSecond();
|
||||
|
||||
signals:
|
||||
|
||||
/// Fired when we're simulating; allows external parties to hook in.
|
||||
|
@ -662,6 +665,8 @@ private:
|
|||
|
||||
QList<QString> _domainConnectionRefusals;
|
||||
glm::uvec2 _renderResolution;
|
||||
|
||||
int _maxOctreePPS = DEFAULT_MAX_OCTREE_PPS;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -847,8 +847,9 @@ int MyAvatar::parseDataAtOffset(const QByteArray& packet, int offset) {
|
|||
}
|
||||
|
||||
void MyAvatar::sendKillAvatar() {
|
||||
QByteArray killPacket = byteArrayWithPopulatedHeader(PacketTypeKillAvatar);
|
||||
DependencyManager::get<NodeList>()->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
QByteArray killPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeKillAvatar);
|
||||
nodeList->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer);
|
||||
}
|
||||
|
||||
void MyAvatar::updateLookAtTargetAvatar() {
|
||||
|
|
|
@ -37,7 +37,6 @@ static BOOL CALLBACK enumWindowsCallback(HWND hWnd, LPARAM lParam) {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
#ifdef Q_OS_WIN
|
||||
// Run only one instance of Interface at a time.
|
||||
|
|
|
@ -889,15 +889,9 @@ void ApplicationOverlay::renderAudioMeter() {
|
|||
}
|
||||
bool isClipping = ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME));
|
||||
|
||||
auto canvasSize = qApp->getCanvasSize();
|
||||
if ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)) {
|
||||
const float MAX_MAGNITUDE = 0.7f;
|
||||
float magnitude = MAX_MAGNITUDE * (1 - audio->getTimeSinceLastClip() / CLIPPING_INDICATOR_TIME);
|
||||
renderCollisionOverlay(canvasSize.x, canvasSize.y, magnitude, 1.0f);
|
||||
}
|
||||
|
||||
DependencyManager::get<AudioToolBox>()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, cameraSpace, boxed);
|
||||
|
||||
auto canvasSize = qApp->getCanvasSize();
|
||||
DependencyManager::get<AudioScope>()->render(canvasSize.x, canvasSize.y);
|
||||
DependencyManager::get<AudioIOStatsRenderer>()->render(WHITE_TEXT, canvasSize.x, canvasSize.y);
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ void PreferencesDialog::loadPreferences() {
|
|||
|
||||
ui.avatarScaleSpin->setValue(myAvatar->getScale());
|
||||
|
||||
ui.maxOctreePPSSpin->setValue(qApp->getOctreeQuery().getMaxOctreePacketsPerSecond());
|
||||
ui.maxOctreePPSSpin->setValue(qApp->getMaxOctreePacketsPerSecond());
|
||||
|
||||
ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationOverlay().getHmdUIAngularSize());
|
||||
|
||||
|
@ -228,7 +228,7 @@ void PreferencesDialog::savePreferences() {
|
|||
|
||||
faceshift->setHostname(ui.faceshiftHostnameEdit->text());
|
||||
|
||||
qApp->getOctreeQuery().setMaxOctreePacketsPerSecond(ui.maxOctreePPSSpin->value());
|
||||
qApp->setMaxOctreePacketsPerSecond(ui.maxOctreePPSSpin->value());
|
||||
|
||||
qApp->getApplicationOverlay().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value());
|
||||
|
||||
|
|
|
@ -837,7 +837,7 @@ void AudioClient::handleAudioInput() {
|
|||
}
|
||||
}
|
||||
|
||||
char* currentPacketPtr = audioDataPacket + populatePacketHeader(audioDataPacket, packetType);
|
||||
char* currentPacketPtr = audioDataPacket + nodeList->populatePacketHeader(audioDataPacket, packetType);
|
||||
|
||||
// pack sequence number
|
||||
memcpy(currentPacketPtr, &_outgoingAvatarAudioSequenceNumber, sizeof(quint16));
|
||||
|
@ -899,7 +899,9 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr
|
|||
}
|
||||
|
||||
void AudioClient::sendMuteEnvironmentPacket() {
|
||||
QByteArray mutePacket = byteArrayWithPopulatedHeader(PacketTypeMuteEnvironment);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
QByteArray mutePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeMuteEnvironment);
|
||||
int headerSize = mutePacket.size();
|
||||
|
||||
const float MUTE_RADIUS = 50;
|
||||
|
@ -910,12 +912,11 @@ void AudioClient::sendMuteEnvironmentPacket() {
|
|||
memcpy(mutePacket.data() + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float));
|
||||
|
||||
// grab our audio mixer from the NodeList, if it exists
|
||||
auto nodelist = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer audioMixer = nodelist->soloNodeOfType(NodeType::AudioMixer);
|
||||
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
|
||||
if (audioMixer) {
|
||||
// send off this mute packet
|
||||
nodelist->writeDatagram(mutePacket, audioMixer);
|
||||
nodeList->writeDatagram(mutePacket, audioMixer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,10 +104,12 @@ void AudioIOStats::sendDownstreamAudioStatsPacket() {
|
|||
// also, call _receivedAudioStream's per-second callback
|
||||
_receivedAudioStream->perSecondCallbackForUpdatingStats();
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
char packet[MAX_PACKET_SIZE];
|
||||
|
||||
// pack header
|
||||
int numBytesPacketHeader = populatePacketHeader(packet, PacketTypeAudioStreamStats);
|
||||
int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeAudioStreamStats);
|
||||
char* dataAt = packet + numBytesPacketHeader;
|
||||
|
||||
// pack append flag
|
||||
|
@ -126,7 +128,6 @@ void AudioIOStats::sendDownstreamAudioStatsPacket() {
|
|||
dataAt += sizeof(AudioStreamStats);
|
||||
|
||||
// send packet
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
nodeList->writeDatagram(packet, dataAt - packet, audioMixer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,11 +141,13 @@ void AudioInjector::injectToMixer() {
|
|||
_currentSendPosition = 0;
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// make sure we actually have samples downloaded to inject
|
||||
if (_audioData.size()) {
|
||||
|
||||
// setup the packet for injected audio
|
||||
QByteArray injectAudioPacket = byteArrayWithPopulatedHeader(PacketTypeInjectAudio);
|
||||
QByteArray injectAudioPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeInjectAudio);
|
||||
QDataStream packetStream(&injectAudioPacket, QIODevice::Append);
|
||||
|
||||
// pack some placeholder sequence number for now
|
||||
|
@ -226,7 +228,6 @@ void AudioInjector::injectToMixer() {
|
|||
_audioData.data() + _currentSendPosition, bytesToCopy);
|
||||
|
||||
// grab our audio mixer from the NodeList, if it exists
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
|
||||
// send off this audio packet
|
||||
|
|
|
@ -1077,25 +1077,31 @@ void AvatarData::setJointMappingsFromNetworkReply() {
|
|||
}
|
||||
|
||||
void AvatarData::sendAvatarDataPacket() {
|
||||
QByteArray dataPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
QByteArray dataPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarData);
|
||||
dataPacket.append(toByteArray());
|
||||
|
||||
DependencyManager::get<NodeList>()->broadcastToNodes(dataPacket, NodeSet() << NodeType::AvatarMixer);
|
||||
nodeList->broadcastToNodes(dataPacket, NodeSet() << NodeType::AvatarMixer);
|
||||
}
|
||||
|
||||
void AvatarData::sendIdentityPacket() {
|
||||
QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
QByteArray identityPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);
|
||||
identityPacket.append(identityByteArray());
|
||||
|
||||
DependencyManager::get<NodeList>()->broadcastToNodes(identityPacket, NodeSet() << NodeType::AvatarMixer);
|
||||
nodeList->broadcastToNodes(identityPacket, NodeSet() << NodeType::AvatarMixer);
|
||||
}
|
||||
|
||||
void AvatarData::sendBillboardPacket() {
|
||||
if (!_billboard.isEmpty()) {
|
||||
QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
QByteArray billboardPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
|
||||
billboardPacket.append(_billboard);
|
||||
|
||||
DependencyManager::get<NodeList>()->broadcastToNodes(billboardPacket, NodeSet() << NodeType::AvatarMixer);
|
||||
nodeList->broadcastToNodes(billboardPacket, NodeSet() << NodeType::AvatarMixer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -142,7 +142,9 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe
|
|||
QUrl url(scriptMaybeURLorText);
|
||||
|
||||
// If the url is not valid, this must be script text...
|
||||
if (!url.isValid()) {
|
||||
// We document "direct injection" scripts as starting with "(function...", and that would never be a valid url.
|
||||
// But QUrl thinks it is.
|
||||
if (!url.isValid() || scriptMaybeURLorText.startsWith("(")) {
|
||||
isURL = false;
|
||||
return scriptMaybeURLorText;
|
||||
}
|
||||
|
@ -423,11 +425,11 @@ void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode,
|
|||
scene->setKeyLightIntensity(_bestZone->getKeyLightIntensity());
|
||||
scene->setKeyLightAmbientIntensity(_bestZone->getKeyLightAmbientIntensity());
|
||||
scene->setKeyLightDirection(_bestZone->getKeyLightDirection());
|
||||
scene->setStageSunModelEnable(_bestZone->getStageSunModelEnabled());
|
||||
scene->setStageLocation(_bestZone->getStageLongitude(), _bestZone->getStageLatitude(),
|
||||
_bestZone->getStageAltitude());
|
||||
scene->setStageDayTime(_bestZone->getStageHour());
|
||||
scene->setStageYearTime(_bestZone->getStageDay());
|
||||
scene->setStageSunModelEnable(_bestZone->getStageProperties().getSunModelEnabled());
|
||||
scene->setStageLocation(_bestZone->getStageProperties().getLongitude(), _bestZone->getStageProperties().getLatitude(),
|
||||
_bestZone->getStageProperties().getAltitude());
|
||||
scene->setStageDayTime(_bestZone->getStageProperties().calculateHour());
|
||||
scene->setStageYearTime(_bestZone->getStageProperties().calculateDay());
|
||||
|
||||
if (_bestZone->getBackgroundMode() == BACKGROUND_MODE_ATMOSPHERE) {
|
||||
EnvironmentData data = _bestZone->getEnvironmentData();
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
AtmospherePropertyGroup EntityItemProperties::_staticAtmosphere;
|
||||
SkyboxPropertyGroup EntityItemProperties::_staticSkybox;
|
||||
StagePropertyGroup EntityItemProperties::_staticStage;
|
||||
|
||||
EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1);
|
||||
|
||||
|
@ -83,12 +84,6 @@ EntityItemProperties::EntityItemProperties() :
|
|||
CONSTRUCT_PROPERTY(keyLightIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_INTENSITY),
|
||||
CONSTRUCT_PROPERTY(keyLightAmbientIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY),
|
||||
CONSTRUCT_PROPERTY(keyLightDirection, ZoneEntityItem::DEFAULT_KEYLIGHT_DIRECTION),
|
||||
CONSTRUCT_PROPERTY(stageSunModelEnabled, ZoneEntityItem::DEFAULT_STAGE_SUN_MODEL_ENABLED),
|
||||
CONSTRUCT_PROPERTY(stageLatitude, ZoneEntityItem::DEFAULT_STAGE_LATITUDE),
|
||||
CONSTRUCT_PROPERTY(stageLongitude, ZoneEntityItem::DEFAULT_STAGE_LONGITUDE),
|
||||
CONSTRUCT_PROPERTY(stageAltitude, ZoneEntityItem::DEFAULT_STAGE_ALTITUDE),
|
||||
CONSTRUCT_PROPERTY(stageDay, ZoneEntityItem::DEFAULT_STAGE_DAY),
|
||||
CONSTRUCT_PROPERTY(stageHour, ZoneEntityItem::DEFAULT_STAGE_HOUR),
|
||||
CONSTRUCT_PROPERTY(name, ENTITY_ITEM_DEFAULT_NAME),
|
||||
CONSTRUCT_PROPERTY(backgroundMode, BACKGROUND_MODE_INHERIT),
|
||||
|
||||
|
@ -332,15 +327,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_INTENSITY, keyLightIntensity);
|
||||
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_AMBIENT_INTENSITY, keyLightAmbientIntensity);
|
||||
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, keyLightDirection);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_SUN_MODEL_ENABLED, stageSunModelEnabled);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_LATITUDE, stageLatitude);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_LONGITUDE, stageLongitude);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_ALTITUDE, stageAltitude);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_DAY, stageDay);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_HOUR, stageHour);
|
||||
|
||||
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode);
|
||||
|
||||
|
||||
changedProperties += _stage.getChangedProperties();
|
||||
changedProperties += _atmosphere.getChangedProperties();
|
||||
changedProperties += _skybox.getChangedProperties();
|
||||
|
||||
|
@ -419,12 +408,6 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightIntensity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightAmbientIntensity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(keyLightDirection);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(stageSunModelEnabled);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(stageLatitude);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(stageLongitude);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(stageAltitude);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(stageDay);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(stageHour);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(backgroundMode, getBackgroundModeAsString());
|
||||
|
||||
// Sitting properties support
|
||||
|
@ -460,6 +443,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(originalTextures, textureNamesList); // gettable, but not settable
|
||||
}
|
||||
|
||||
_stage.copyToScriptValue(properties, engine, skipDefaults, defaultEntityProperties);
|
||||
_atmosphere.copyToScriptValue(properties, engine, skipDefaults, defaultEntityProperties);
|
||||
_skybox.copyToScriptValue(properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
|
@ -525,13 +509,9 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(keyLightIntensity, setKeyLightIntensity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(keyLightAmbientIntensity, setKeyLightAmbientIntensity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(keyLightDirection, setKeyLightDirection);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(stageSunModelEnabled, setStageSunModelEnabled);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stageLatitude, setStageLatitude);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stageLongitude, setStageLongitude);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stageAltitude, setStageAltitude);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_INT(stageDay, setStageDay);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stageHour, setStageHour);
|
||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(backgroundMode, BackgroundMode);
|
||||
|
||||
_stage.copyFromScriptValue(object, _defaultSettings);
|
||||
_atmosphere.copyFromScriptValue(object, _defaultSettings);
|
||||
_skybox.copyFromScriptValue(object, _defaultSettings);
|
||||
_lastEdited = usecTimestampNow();
|
||||
|
@ -729,13 +709,10 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, appendValue, properties.getKeyLightIntensity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, appendValue, properties.getKeyLightAmbientIntensity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, appendValue, properties.getKeyLightDirection());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, appendValue, properties.getStageSunModelEnabled());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, appendValue, properties.getStageLatitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, appendValue, properties.getStageLongitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, appendValue, properties.getStageAltitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, appendValue, properties.getStageDay());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, appendValue, properties.getStageHour());
|
||||
|
||||
|
||||
_staticStage.setProperties(properties);
|
||||
_staticStage.appentToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState );
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)properties.getShapeType());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, properties.getCompoundShapeURL());
|
||||
|
||||
|
@ -987,12 +964,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_INTENSITY, float, setKeyLightIntensity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, setKeyLightAmbientIntensity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_DIRECTION, glm::vec3, setKeyLightDirection);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_SUN_MODEL_ENABLED, bool, setStageSunModelEnabled);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_LATITUDE, float, setStageLatitude);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_LONGITUDE, float, setStageLongitude);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_ALTITUDE, float, setStageAltitude);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_DAY, quint16, setStageDay);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_HOUR, float, setStageHour);
|
||||
|
||||
properties.getStage().decodeFromEditPacket(propertyFlags, dataAt , processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType);
|
||||
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode);
|
||||
|
@ -1093,14 +1067,9 @@ void EntityItemProperties::markAllChanged() {
|
|||
_keyLightIntensityChanged = true;
|
||||
_keyLightAmbientIntensityChanged = true;
|
||||
_keyLightDirectionChanged = true;
|
||||
_stageSunModelEnabledChanged = true;
|
||||
_stageLatitudeChanged = true;
|
||||
_stageLongitudeChanged = true;
|
||||
_stageAltitudeChanged = true;
|
||||
_stageDayChanged = true;
|
||||
_stageHourChanged = true;
|
||||
|
||||
_backgroundModeChanged = true;
|
||||
_stage.markAllChanged();
|
||||
_atmosphere.markAllChanged();
|
||||
_skybox.markAllChanged();
|
||||
|
||||
|
|
|
@ -30,11 +30,12 @@
|
|||
#include <ShapeInfo.h>
|
||||
|
||||
#include "AtmospherePropertyGroup.h"
|
||||
#include "SkyboxPropertyGroup.h"
|
||||
#include "EntityItemID.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
#include "EntityTypes.h"
|
||||
#include "EntityPropertyFlags.h"
|
||||
#include "SkyboxPropertyGroup.h"
|
||||
#include "StagePropertyGroup.h"
|
||||
|
||||
const quint64 UNKNOWN_CREATED_TIME = 0;
|
||||
|
||||
|
@ -132,14 +133,9 @@ public:
|
|||
DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float);
|
||||
DEFINE_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float);
|
||||
DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3);
|
||||
DEFINE_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, StageSunModelEnabled, stageSunModelEnabled, bool);
|
||||
DEFINE_PROPERTY(PROP_STAGE_LATITUDE, StageLatitude, stageLatitude, float);
|
||||
DEFINE_PROPERTY(PROP_STAGE_LONGITUDE, StageLongitude, stageLongitude, float);
|
||||
DEFINE_PROPERTY(PROP_STAGE_ALTITUDE, StageAltitude, stageAltitude, float);
|
||||
DEFINE_PROPERTY(PROP_STAGE_DAY, StageDay, stageDay, quint16);
|
||||
DEFINE_PROPERTY(PROP_STAGE_HOUR, StageHour, stageHour, float);
|
||||
DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_BACKGROUND_MODE, BackgroundMode, backgroundMode, BackgroundMode);
|
||||
DEFINE_PROPERTY_GROUP(Stage, stage, StagePropertyGroup);
|
||||
DEFINE_PROPERTY_GROUP(Atmosphere, atmosphere, AtmospherePropertyGroup);
|
||||
DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup);
|
||||
|
||||
|
@ -280,6 +276,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
|||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, MarketplaceID, marketplaceID, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundMode, backgroundMode, "");
|
||||
|
||||
properties.getStage().debugDump();
|
||||
properties.getAtmosphere().debugDump();
|
||||
properties.getSkybox().debugDump();
|
||||
|
||||
|
|
|
@ -302,6 +302,15 @@
|
|||
} \
|
||||
}
|
||||
|
||||
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT_GETTER(P, S, G) \
|
||||
QScriptValue P = object.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
float newValue = P.toVariant().toFloat(); \
|
||||
if (_defaultSettings || newValue != G()) { \
|
||||
S(newValue); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(G, P, S) \
|
||||
{ \
|
||||
QScriptValue G = object.property(#G); \
|
||||
|
@ -316,6 +325,20 @@
|
|||
} \
|
||||
}
|
||||
|
||||
#define COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_UINT16(G, P, S) \
|
||||
{ \
|
||||
QScriptValue G = object.property(#G); \
|
||||
if (G.isValid()) { \
|
||||
QScriptValue P = G.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
uint16_t newValue = P.toVariant().toInt(); \
|
||||
if (_defaultSettings || newValue != _##P) { \
|
||||
S(newValue); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_INT(P, S) \
|
||||
QScriptValue P = object.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
|
@ -325,6 +348,15 @@
|
|||
} \
|
||||
}
|
||||
|
||||
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_INT_GETTER(P, S, G) \
|
||||
QScriptValue P = object.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
int newValue = P.toVariant().toInt(); \
|
||||
if (_defaultSettings || newValue != G()) { \
|
||||
S(newValue); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(P, S) \
|
||||
QScriptValue P = object.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
|
@ -334,6 +366,16 @@
|
|||
} \
|
||||
}
|
||||
|
||||
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL_GETTER(P, S, G) \
|
||||
QScriptValue P = object.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
bool newValue = P.toVariant().toBool(); \
|
||||
if (_defaultSettings || newValue != G()) { \
|
||||
S(newValue); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#define COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_BOOL(G, P, S) \
|
||||
{ \
|
||||
QScriptValue G = object.property(#G); \
|
||||
|
|
|
@ -146,6 +146,7 @@ enum EntityPropertyList {
|
|||
PROP_BACKGROUND_MODE = PROP_MODEL_URL,
|
||||
PROP_SKYBOX_COLOR = PROP_ANIMATION_URL,
|
||||
PROP_SKYBOX_URL = PROP_ANIMATION_FPS,
|
||||
PROP_STAGE_AUTOMATIC_HOURDAY = PROP_ANIMATION_FRAME_INDEX,
|
||||
|
||||
// WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above
|
||||
};
|
||||
|
|
|
@ -792,7 +792,8 @@ bool EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumbe
|
|||
bool hasMoreToSend = true;
|
||||
|
||||
unsigned char* copyAt = outputBuffer;
|
||||
size_t numBytesPacketHeader = populatePacketHeader(reinterpret_cast<char*>(outputBuffer), PacketTypeEntityErase);
|
||||
size_t numBytesPacketHeader = DependencyManager::get<NodeList>()->populatePacketHeader(reinterpret_cast<char*>(outputBuffer),
|
||||
PacketTypeEntityErase);
|
||||
copyAt += numBytesPacketHeader;
|
||||
outputLength = numBytesPacketHeader;
|
||||
|
||||
|
|
257
libraries/entities/src/StagePropertyGroup.cpp
Normal file
257
libraries/entities/src/StagePropertyGroup.cpp
Normal file
|
@ -0,0 +1,257 @@
|
|||
//
|
||||
// StagePropertyGroup.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/4/13.
|
||||
// Copyright 2013 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 <QDateTime>
|
||||
#include <QDate>
|
||||
#include <QTime>
|
||||
|
||||
#include <OctreePacketData.h>
|
||||
|
||||
#include "StagePropertyGroup.h"
|
||||
#include "EntityItemProperties.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
|
||||
const bool StagePropertyGroup::DEFAULT_STAGE_SUN_MODEL_ENABLED = false;
|
||||
const float StagePropertyGroup::DEFAULT_STAGE_LATITUDE = 37.777f;
|
||||
const float StagePropertyGroup::DEFAULT_STAGE_LONGITUDE = 122.407f;
|
||||
const float StagePropertyGroup::DEFAULT_STAGE_ALTITUDE = 0.03f;
|
||||
const quint16 StagePropertyGroup::DEFAULT_STAGE_DAY = 60;
|
||||
const float StagePropertyGroup::DEFAULT_STAGE_HOUR = 12.0f;
|
||||
|
||||
StagePropertyGroup::StagePropertyGroup() {
|
||||
_sunModelEnabled = DEFAULT_STAGE_SUN_MODEL_ENABLED;
|
||||
_latitude = DEFAULT_STAGE_LATITUDE;
|
||||
_longitude = DEFAULT_STAGE_LONGITUDE;
|
||||
_altitude = DEFAULT_STAGE_ALTITUDE;
|
||||
_day = DEFAULT_STAGE_DAY;
|
||||
_hour = DEFAULT_STAGE_HOUR;
|
||||
_automaticHourDay = false;
|
||||
}
|
||||
|
||||
void StagePropertyGroup::copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, SunModelEnabled, sunModelEnabled);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, Latitude, latitude);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, Longitude, longitude);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, Altitude, altitude);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, Day, day);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, Hour, hour);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, AutomaticHourDay, automaticHourDay);
|
||||
}
|
||||
|
||||
void StagePropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
|
||||
|
||||
// Backward compatibility support for the old way of doing stage properties
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL_GETTER(stageSunModelEnabled, setSunModelEnabled, getSunModelEnabled);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT_GETTER(stageLatitude, setLatitude, getLatitude);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT_GETTER(stageLongitude, setLongitude, getLongitude);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT_GETTER(stageAltitude, setAltitude, getAltitude);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_INT_GETTER(stageDay, setDay, getDay);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT_GETTER(stageHour, setHour, getHour);
|
||||
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_BOOL(stage, sunModelEnabled, setSunModelEnabled);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stage, latitude, setLatitude);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stage, longitude, setLongitude);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stage, altitude, setAltitude);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_UINT16(stage, day, setDay);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stage, hour, setHour);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_BOOL(stage, automaticHourDay, setAutomaticHourDay);
|
||||
}
|
||||
|
||||
void StagePropertyGroup::debugDump() const {
|
||||
qDebug() << " StagePropertyGroup: ---------------------------------------------";
|
||||
qDebug() << " _sunModelEnabled:" << _sunModelEnabled;
|
||||
qDebug() << " _latitude:" << _latitude;
|
||||
qDebug() << " _longitude:" << _longitude;
|
||||
qDebug() << " _altitude:" << _altitude;
|
||||
qDebug() << " _day:" << _day;
|
||||
qDebug() << " _hour:" << _hour;
|
||||
qDebug() << " _automaticHourDay:" << _automaticHourDay;
|
||||
}
|
||||
|
||||
bool StagePropertyGroup::appentToEditPacket(OctreePacketData* packetData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const {
|
||||
|
||||
bool successPropertyFits = true;
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, appendValue, getSunModelEnabled());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, appendValue, getLatitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, appendValue, getLongitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, appendValue, getAltitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, appendValue, getDay());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, appendValue, getHour());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, appendValue, getAutomaticHourDay());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool StagePropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) {
|
||||
|
||||
int bytesRead = 0;
|
||||
bool overwriteLocalData = true;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, bool, _sunModelEnabled);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, float, _latitude);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, float, _longitude);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, float, _altitude);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_DAY, quint16, _day);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_HOUR, float, _hour);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, bool, _automaticHourDay);
|
||||
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_SUN_MODEL_ENABLED, SunModelEnabled);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_LATITUDE, Latitude);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_LONGITUDE, Longitude);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_ALTITUDE, Altitude);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_DAY, Day);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_HOUR, Hour);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_AUTOMATIC_HOURDAY, AutomaticHourDay);
|
||||
|
||||
processedBytes += bytesRead;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StagePropertyGroup::markAllChanged() {
|
||||
_sunModelEnabledChanged = true;
|
||||
_latitudeChanged = true;
|
||||
_longitudeChanged = true;
|
||||
_altitudeChanged = true;
|
||||
_dayChanged = true;
|
||||
_hourChanged = true;
|
||||
_automaticHourDayChanged = true;
|
||||
}
|
||||
|
||||
EntityPropertyFlags StagePropertyGroup::getChangedProperties() const {
|
||||
EntityPropertyFlags changedProperties;
|
||||
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_SUN_MODEL_ENABLED, sunModelEnabled);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_LATITUDE, latitude);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_LONGITUDE, longitude);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_ALTITUDE, altitude);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_DAY, day);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_HOUR, hour);
|
||||
CHECK_PROPERTY_CHANGE(PROP_STAGE_AUTOMATIC_HOURDAY, automaticHourDay);
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
||||
void StagePropertyGroup::getProperties(EntityItemProperties& properties) const {
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, SunModelEnabled, getSunModelEnabled);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Latitude, getLatitude);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Longitude, getLongitude);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Altitude, getAltitude);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Day, getDay);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Hour, getHour);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, AutomaticHourDay, getAutomaticHourDay);
|
||||
}
|
||||
|
||||
bool StagePropertyGroup::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, SunModelEnabled, sunModelEnabled, setSunModelEnabled);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Latitude, latitude, setLatitude);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Longitude, longitude, setLongitude);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Altitude, altitude, setAltitude);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Day, day, setDay);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Hour, hour, setHour);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, AutomaticHourDay, automaticHourDay, setAutomaticHourDay);
|
||||
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
EntityPropertyFlags StagePropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties;
|
||||
|
||||
requestedProperties += PROP_STAGE_SUN_MODEL_ENABLED;
|
||||
requestedProperties += PROP_STAGE_LATITUDE;
|
||||
requestedProperties += PROP_STAGE_LONGITUDE;
|
||||
requestedProperties += PROP_STAGE_ALTITUDE;
|
||||
requestedProperties += PROP_STAGE_DAY;
|
||||
requestedProperties += PROP_STAGE_HOUR;
|
||||
requestedProperties += PROP_STAGE_AUTOMATIC_HOURDAY;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
void StagePropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const {
|
||||
|
||||
bool successPropertyFits = true;
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, appendValue, getSunModelEnabled());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, appendValue, getLatitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, appendValue, getLongitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, appendValue, getAltitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, appendValue, getDay());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, appendValue, getHour());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, appendValue, getAutomaticHourDay());
|
||||
}
|
||||
|
||||
int StagePropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
|
||||
|
||||
int bytesRead = 0;
|
||||
const unsigned char* dataAt = data;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, bool, _sunModelEnabled);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, float, _latitude);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, float, _longitude);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, float, _altitude);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_DAY, quint16, _day);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_HOUR, float, _hour);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, bool, _automaticHourDay);
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
static const float TOTAL_LONGITUDES = 360.0f;
|
||||
static const float HOURS_PER_DAY = 24;
|
||||
static const float SECONDS_PER_DAY = 60 * 60 * HOURS_PER_DAY;
|
||||
static const float MSECS_PER_DAY = SECONDS_PER_DAY * MSECS_PER_SECOND;
|
||||
|
||||
float StagePropertyGroup::calculateHour() const {
|
||||
if (!_automaticHourDay) {
|
||||
return _hour;
|
||||
}
|
||||
|
||||
QDateTime utc(QDateTime::currentDateTimeUtc());
|
||||
float adjustFromUTC = (_longitude / TOTAL_LONGITUDES);
|
||||
float offsetFromUTCinMsecs = adjustFromUTC * MSECS_PER_DAY;
|
||||
int msecsSinceStartOfDay = utc.time().msecsSinceStartOfDay();
|
||||
float calutatedHour = ((msecsSinceStartOfDay + offsetFromUTCinMsecs) / MSECS_PER_DAY) * HOURS_PER_DAY;
|
||||
|
||||
// calculate hour based on longitude and time from GMT
|
||||
return calutatedHour;
|
||||
}
|
||||
|
||||
uint16_t StagePropertyGroup::calculateDay() const {
|
||||
|
||||
if (!_automaticHourDay) {
|
||||
return _day;
|
||||
}
|
||||
|
||||
QDateTime utc(QDateTime::currentDateTimeUtc());
|
||||
int calutatedDay = utc.date().dayOfYear();
|
||||
|
||||
// calculate day based on longitude and time from GMT
|
||||
return calutatedDay;
|
||||
}
|
||||
|
91
libraries/entities/src/StagePropertyGroup.h
Normal file
91
libraries/entities/src/StagePropertyGroup.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// StagePropertyGroup.h
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/4/13.
|
||||
// Copyright 2013 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_StagePropertyGroup_h
|
||||
#define hifi_StagePropertyGroup_h
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include "PropertyGroup.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
|
||||
class EntityItemProperties;
|
||||
class EncodeBitstreamParams;
|
||||
class OctreePacketData;
|
||||
class EntityTreeElementExtraEncodeData;
|
||||
class ReadBitstreamToTreeParams;
|
||||
|
||||
#include <stdint.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
||||
class StagePropertyGroup : public PropertyGroup {
|
||||
public:
|
||||
StagePropertyGroup();
|
||||
virtual ~StagePropertyGroup() {}
|
||||
|
||||
// EntityItemProperty related helpers
|
||||
virtual void copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const;
|
||||
virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings);
|
||||
virtual void debugDump() const;
|
||||
|
||||
virtual bool appentToEditPacket(OctreePacketData* packetData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const;
|
||||
|
||||
virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes);
|
||||
virtual void markAllChanged();
|
||||
virtual EntityPropertyFlags getChangedProperties() const;
|
||||
|
||||
// EntityItem related helpers
|
||||
// methods for getting/setting all properties of an entity
|
||||
virtual void getProperties(EntityItemProperties& propertiesOut) const;
|
||||
|
||||
/// returns true if something changed
|
||||
virtual bool setProperties(const EntityItemProperties& properties);
|
||||
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const;
|
||||
|
||||
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
|
||||
|
||||
static const bool DEFAULT_STAGE_SUN_MODEL_ENABLED;
|
||||
static const float DEFAULT_STAGE_LATITUDE;
|
||||
static const float DEFAULT_STAGE_LONGITUDE;
|
||||
static const float DEFAULT_STAGE_ALTITUDE;
|
||||
static const quint16 DEFAULT_STAGE_DAY;
|
||||
static const float DEFAULT_STAGE_HOUR;
|
||||
|
||||
float calculateHour() const;
|
||||
uint16_t calculateDay() const;
|
||||
|
||||
DEFINE_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, SunModelEnabled, sunModelEnabled, bool);
|
||||
DEFINE_PROPERTY(PROP_STAGE_LATITUDE, Latitude, latitude, float);
|
||||
DEFINE_PROPERTY(PROP_STAGE_LONGITUDE, Longitude, longitude, float);
|
||||
DEFINE_PROPERTY(PROP_STAGE_ALTITUDE, Altitude, altitude, float);
|
||||
DEFINE_PROPERTY(PROP_STAGE_DAY, Day, day, uint16_t);
|
||||
DEFINE_PROPERTY(PROP_STAGE_HOUR, Hour, hour, float);
|
||||
DEFINE_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, AutomaticHourDay, automaticHourDay, bool);
|
||||
};
|
||||
|
||||
#endif // hifi_StagePropertyGroup_h
|
|
@ -26,12 +26,6 @@ const xColor ZoneEntityItem::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 };
|
|||
const float ZoneEntityItem::DEFAULT_KEYLIGHT_INTENSITY = 1.0f;
|
||||
const float ZoneEntityItem::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f;
|
||||
const glm::vec3 ZoneEntityItem::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f };
|
||||
const bool ZoneEntityItem::DEFAULT_STAGE_SUN_MODEL_ENABLED = false;
|
||||
const float ZoneEntityItem::DEFAULT_STAGE_LATITUDE = 37.777f;
|
||||
const float ZoneEntityItem::DEFAULT_STAGE_LONGITUDE = 122.407f;
|
||||
const float ZoneEntityItem::DEFAULT_STAGE_ALTITUDE = 0.03f;
|
||||
const quint16 ZoneEntityItem::DEFAULT_STAGE_DAY = 60;
|
||||
const float ZoneEntityItem::DEFAULT_STAGE_HOUR = 12.0f;
|
||||
const ShapeType ZoneEntityItem::DEFAULT_SHAPE_TYPE = SHAPE_TYPE_BOX;
|
||||
const QString ZoneEntityItem::DEFAULT_COMPOUND_SHAPE_URL = "";
|
||||
|
||||
|
@ -53,12 +47,6 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID, const EntityIte
|
|||
_keyLightIntensity = DEFAULT_KEYLIGHT_INTENSITY;
|
||||
_keyLightAmbientIntensity = DEFAULT_KEYLIGHT_AMBIENT_INTENSITY;
|
||||
_keyLightDirection = DEFAULT_KEYLIGHT_DIRECTION;
|
||||
_stageSunModelEnabled = DEFAULT_STAGE_SUN_MODEL_ENABLED;
|
||||
_stageLatitude = DEFAULT_STAGE_LATITUDE;
|
||||
_stageLongitude = DEFAULT_STAGE_LONGITUDE;
|
||||
_stageAltitude = DEFAULT_STAGE_ALTITUDE;
|
||||
_stageDay = DEFAULT_STAGE_DAY;
|
||||
_stageHour = DEFAULT_STAGE_HOUR;
|
||||
_shapeType = DEFAULT_SHAPE_TYPE;
|
||||
_compoundShapeURL = DEFAULT_COMPOUND_SHAPE_URL;
|
||||
|
||||
|
@ -94,15 +82,11 @@ EntityItemProperties ZoneEntityItem::getProperties() const {
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightIntensity, getKeyLightIntensity);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightAmbientIntensity, getKeyLightAmbientIntensity);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightDirection, getKeyLightDirection);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageSunModelEnabled, getStageSunModelEnabled);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageLatitude, getStageLatitude);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageLongitude, getStageLongitude);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageAltitude, getStageAltitude);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageDay, getStageDay);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageHour, getStageHour);
|
||||
|
||||
_stageProperties.getProperties(properties);
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL);
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundMode, getBackgroundMode);
|
||||
|
||||
_atmosphereProperties.getProperties(properties);
|
||||
|
@ -119,12 +103,9 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightIntensity, setKeyLightIntensity);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightAmbientIntensity, setKeyLightAmbientIntensity);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightDirection, setKeyLightDirection);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageSunModelEnabled, setStageSunModelEnabled);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageLatitude, setStageLatitude);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageLongitude, setStageLongitude);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageAltitude, setStageAltitude);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageDay, setStageDay);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageHour, setStageHour);
|
||||
|
||||
bool somethingChangedInStage = _stageProperties.setProperties(properties);
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundMode, setBackgroundMode);
|
||||
|
@ -132,7 +113,7 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
bool somethingChangedInAtmosphere = _atmosphereProperties.setProperties(properties);
|
||||
bool somethingChangedInSkybox = _skyboxProperties.setProperties(properties);
|
||||
|
||||
somethingChanged = somethingChanged || somethingChangedInAtmosphere || somethingChangedInSkybox;
|
||||
somethingChanged = somethingChanged || somethingChangedInStage || somethingChangedInAtmosphere || somethingChangedInSkybox;
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
|
@ -158,12 +139,13 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, _keyLightIntensity);
|
||||
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, _keyLightAmbientIntensity);
|
||||
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, _keyLightDirection);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, bool, _stageSunModelEnabled);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, float, _stageLatitude);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, float, _stageLongitude);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, float, _stageAltitude);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_DAY, quint16, _stageDay);
|
||||
READ_ENTITY_PROPERTY(PROP_STAGE_HOUR, float, _stageHour);
|
||||
|
||||
int bytesFromStage = _stageProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData);
|
||||
|
||||
bytesRead += bytesFromStage;
|
||||
dataAt += bytesFromStage;
|
||||
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_SHAPE_TYPE, ShapeType, updateShapeType);
|
||||
READ_ENTITY_PROPERTY_STRING(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode);
|
||||
|
@ -191,15 +173,10 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p
|
|||
requestedProperties += PROP_KEYLIGHT_INTENSITY;
|
||||
requestedProperties += PROP_KEYLIGHT_AMBIENT_INTENSITY;
|
||||
requestedProperties += PROP_KEYLIGHT_DIRECTION;
|
||||
requestedProperties += PROP_STAGE_SUN_MODEL_ENABLED;
|
||||
requestedProperties += PROP_STAGE_LATITUDE;
|
||||
requestedProperties += PROP_STAGE_LONGITUDE;
|
||||
requestedProperties += PROP_STAGE_ALTITUDE;
|
||||
requestedProperties += PROP_STAGE_DAY;
|
||||
requestedProperties += PROP_STAGE_HOUR;
|
||||
requestedProperties += PROP_SHAPE_TYPE;
|
||||
requestedProperties += PROP_COMPOUND_SHAPE_URL;
|
||||
requestedProperties += PROP_BACKGROUND_MODE;
|
||||
requestedProperties += _stageProperties.getEntityProperties(params);
|
||||
requestedProperties += _atmosphereProperties.getEntityProperties(params);
|
||||
requestedProperties += _skyboxProperties.getEntityProperties(params);
|
||||
|
||||
|
@ -220,12 +197,11 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
|
|||
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, appendValue, getKeyLightIntensity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, appendValue, getKeyLightAmbientIntensity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, appendValue, getKeyLightDirection());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, appendValue, getStageSunModelEnabled());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, appendValue, getStageLatitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, appendValue, getStageLongitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, appendValue, getStageAltitude());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, appendValue, getStageDay());
|
||||
APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, appendValue, getStageHour());
|
||||
|
||||
_stageProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)getShapeType());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, getCompoundShapeURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, appendValue, (uint32_t)getBackgroundMode()); // could this be a uint16??
|
||||
|
@ -248,14 +224,9 @@ void ZoneEntityItem::debugDump() const {
|
|||
qCDebug(entities) << " _keyLightIntensity:" << _keyLightIntensity;
|
||||
qCDebug(entities) << " _keyLightAmbientIntensity:" << _keyLightAmbientIntensity;
|
||||
qCDebug(entities) << " _keyLightDirection:" << _keyLightDirection;
|
||||
qCDebug(entities) << " _stageSunModelEnabled:" << _stageSunModelEnabled;
|
||||
qCDebug(entities) << " _stageLatitude:" << _stageLatitude;
|
||||
qCDebug(entities) << " _stageLongitude:" << _stageLongitude;
|
||||
qCDebug(entities) << " _stageAltitude:" << _stageAltitude;
|
||||
qCDebug(entities) << " _stageDay:" << _stageDay;
|
||||
qCDebug(entities) << " _stageHour:" << _stageHour;
|
||||
qCDebug(entities) << " _backgroundMode:" << EntityItemProperties::getBackgroundModeString(_backgroundMode);
|
||||
|
||||
_stageProperties.debugDump();
|
||||
_atmosphereProperties.debugDump();
|
||||
_skyboxProperties.debugDump();
|
||||
}
|
||||
|
|
|
@ -69,24 +69,6 @@ public:
|
|||
const glm::vec3& getKeyLightDirection() const { return _keyLightDirection; }
|
||||
void setKeyLightDirection(const glm::vec3& value) { _keyLightDirection = value; }
|
||||
|
||||
bool getStageSunModelEnabled() const { return _stageSunModelEnabled; }
|
||||
void setStageSunModelEnabled(bool value) { _stageSunModelEnabled = value; }
|
||||
|
||||
float getStageLatitude() const { return _stageLatitude; }
|
||||
void setStageLatitude(float value) { _stageLatitude = value; }
|
||||
|
||||
float getStageLongitude() const { return _stageLongitude; }
|
||||
void setStageLongitude(float value) { _stageLongitude = value; }
|
||||
|
||||
float getStageAltitude() const { return _stageAltitude; }
|
||||
void setStageAltitude(float value) { _stageAltitude = value; }
|
||||
|
||||
uint16_t getStageDay() const { return _stageDay; }
|
||||
void setStageDay(uint16_t value) { _stageDay = value; }
|
||||
|
||||
float getStageHour() const { return _stageHour; }
|
||||
void setStageHour(float value) { _stageHour = value; }
|
||||
|
||||
static bool getZonesArePickable() { return _zonesArePickable; }
|
||||
static void setZonesArePickable(bool value) { _zonesArePickable = value; }
|
||||
|
||||
|
@ -107,6 +89,7 @@ public:
|
|||
EnvironmentData getEnvironmentData() const;
|
||||
const AtmospherePropertyGroup& getAtmosphereProperties() const { return _atmosphereProperties; }
|
||||
const SkyboxPropertyGroup& getSkyboxProperties() const { return _skyboxProperties; }
|
||||
const StagePropertyGroup& getStageProperties() const { return _stageProperties; }
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
|
@ -119,12 +102,6 @@ public:
|
|||
static const float DEFAULT_KEYLIGHT_INTENSITY;
|
||||
static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY;
|
||||
static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION;
|
||||
static const bool DEFAULT_STAGE_SUN_MODEL_ENABLED;
|
||||
static const float DEFAULT_STAGE_LATITUDE;
|
||||
static const float DEFAULT_STAGE_LONGITUDE;
|
||||
static const float DEFAULT_STAGE_ALTITUDE;
|
||||
static const quint16 DEFAULT_STAGE_DAY;
|
||||
static const float DEFAULT_STAGE_HOUR;
|
||||
static const ShapeType DEFAULT_SHAPE_TYPE;
|
||||
static const QString DEFAULT_COMPOUND_SHAPE_URL;
|
||||
|
||||
|
@ -134,17 +111,13 @@ protected:
|
|||
float _keyLightIntensity;
|
||||
float _keyLightAmbientIntensity;
|
||||
glm::vec3 _keyLightDirection;
|
||||
bool _stageSunModelEnabled;
|
||||
float _stageLatitude;
|
||||
float _stageLongitude;
|
||||
float _stageAltitude;
|
||||
uint16_t _stageDay;
|
||||
float _stageHour;
|
||||
|
||||
ShapeType _shapeType = DEFAULT_SHAPE_TYPE;
|
||||
QString _compoundShapeURL;
|
||||
|
||||
BackgroundMode _backgroundMode = BACKGROUND_MODE_INHERIT;
|
||||
|
||||
StagePropertyGroup _stageProperties;
|
||||
AtmospherePropertyGroup _atmosphereProperties;
|
||||
SkyboxPropertyGroup _skyboxProperties;
|
||||
|
||||
|
|
|
@ -386,8 +386,9 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
|
|||
done:
|
||||
if (faces.count() == 0) { // empty mesh
|
||||
mesh.parts.pop_back();
|
||||
} else {
|
||||
faceGroups.append(faces); // We're done with this group. Add the faces.
|
||||
}
|
||||
faceGroups.append(faces); // We're done with this group. Add the faces.
|
||||
//qCDebug(modelformat) << "end group:" << meshPart.materialID << " original faces:" << originalFaceCountForDebugging << " triangles:" << faces.count() << " keep going:" << result;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<@if GLPROFILE == PC_GL @>
|
||||
<@def GPU_FEATURE_PROFILE GPU_CORE@>
|
||||
<@def GPU_TRANSFORM_PROFILE GPU_CORE@>
|
||||
<@def VERSION_HEADER #version 330 compatibility@>
|
||||
<@def VERSION_HEADER #version 430 compatibility@>
|
||||
<@elif GLPROFILE == MAC_GL @>
|
||||
<@def GPU_FEATURE_PROFILE GPU_LEGACY@>
|
||||
<@def GPU_TRANSFORM_PROFILE GPU_LEGACY@>
|
||||
|
|
|
@ -295,8 +295,8 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
|
|||
glBindTexture(GL_TEXTURE_2D, object->_texture);
|
||||
|
||||
if (needUpdate) {
|
||||
if (texture.isStoredMipAvailable(0)) {
|
||||
Texture::PixelsPointer mip = texture.accessStoredMip(0);
|
||||
if (texture.isStoredMipFaceAvailable(0)) {
|
||||
Texture::PixelsPointer mip = texture.accessStoredMipFace(0);
|
||||
const GLvoid* bytes = mip->_sysmem.read<Byte>();
|
||||
Element srcFormat = mip->_format;
|
||||
|
||||
|
@ -318,15 +318,15 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
|
|||
|
||||
|
||||
// At this point the mip piels have been loaded, we can notify
|
||||
texture.notifyGPULoaded(0);
|
||||
texture.notifyMipFaceGPULoaded(0, 0);
|
||||
|
||||
object->_contentStamp = texture.getDataStamp();
|
||||
}
|
||||
} else {
|
||||
const GLvoid* bytes = 0;
|
||||
Element srcFormat = texture.getTexelFormat();
|
||||
if (texture.isStoredMipAvailable(0)) {
|
||||
Texture::PixelsPointer mip = texture.accessStoredMip(0);
|
||||
if (texture.isStoredMipFaceAvailable(0)) {
|
||||
Texture::PixelsPointer mip = texture.accessStoredMipFace(0);
|
||||
|
||||
bytes = mip->_sysmem.read<Byte>();
|
||||
srcFormat = mip->_format;
|
||||
|
@ -352,10 +352,11 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
|
|||
|
||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
||||
|
||||
// At this point the mip piels have been loaded, we can notify
|
||||
texture.notifyGPULoaded(0);
|
||||
// At this point the mip pixels have been loaded, we can notify
|
||||
texture.notifyMipFaceGPULoaded(0, 0);
|
||||
|
||||
object->_storageStamp = texture.getStamp();
|
||||
object->_contentStamp = texture.getDataStamp();
|
||||
object->_size = texture.getSize();
|
||||
}
|
||||
|
||||
|
@ -370,63 +371,57 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
|
|||
glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture);
|
||||
const int NUM_FACES = 6;
|
||||
const GLenum FACE_LAYOUT[] = {
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z };
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z };
|
||||
if (needUpdate) {
|
||||
if (texture.isStoredMipAvailable(0)) {
|
||||
Texture::PixelsPointer mip = texture.accessStoredMip(0);
|
||||
Element srcFormat = mip->_format;
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
||||
|
||||
uint16 width = texture.getWidth();
|
||||
int faceSize = mip->_sysmem.getSize() / NUM_FACES;
|
||||
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture);
|
||||
for (int f = 0; f < NUM_FACES; f++) {
|
||||
glTexSubImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, width, width, 0,
|
||||
texelFormat.format, texelFormat.type, (GLvoid*) (mip->_sysmem.read<Byte>() + f * faceSize));
|
||||
}
|
||||
|
||||
if (texture.isAutogenerateMips()) {
|
||||
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
|
||||
object->_target = GL_TEXTURE_CUBE_MAP;
|
||||
|
||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
||||
|
||||
// At this point the mip piels have been loaded, we can notify
|
||||
texture.notifyGPULoaded(0);
|
||||
|
||||
object->_contentStamp = texture.getDataStamp();
|
||||
}
|
||||
} else {
|
||||
const gpu::Byte* bytes = 0;
|
||||
Element srcFormat = texture.getTexelFormat();
|
||||
uint16 width = texture.getWidth();
|
||||
int faceSize = 0;
|
||||
|
||||
if (texture.isStoredMipAvailable(0)) {
|
||||
Texture::PixelsPointer mip = texture.accessStoredMip(0);
|
||||
|
||||
bytes = mip->_sysmem.read<Byte>();
|
||||
srcFormat = mip->_format;
|
||||
faceSize = mip->_sysmem.getSize() / NUM_FACES;
|
||||
|
||||
object->_contentStamp = texture.getDataStamp();
|
||||
}
|
||||
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
||||
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture);
|
||||
|
||||
// transfer pixels from each faces
|
||||
for (int f = 0; f < NUM_FACES; f++) {
|
||||
glTexImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, width, width, 0,
|
||||
texelFormat.format, texelFormat.type, (GLvoid*) (bytes + f * faceSize));
|
||||
if (texture.isStoredMipFaceAvailable(0, f)) {
|
||||
Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f);
|
||||
Element srcFormat = mipFace->_format;
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
||||
|
||||
glTexSubImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0,
|
||||
texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->_sysmem.read<Byte>()));
|
||||
|
||||
// At this point the mip pixels have been loaded, we can notify
|
||||
texture.notifyMipFaceGPULoaded(0, f);
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes && texture.isAutogenerateMips()) {
|
||||
if (texture.isAutogenerateMips()) {
|
||||
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
|
||||
object->_target = GL_TEXTURE_CUBE_MAP;
|
||||
|
||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
||||
|
||||
object->_contentStamp = texture.getDataStamp();
|
||||
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture);
|
||||
|
||||
// transfer pixels from each faces
|
||||
for (int f = 0; f < NUM_FACES; f++) {
|
||||
if (texture.isStoredMipFaceAvailable(0, f)) {
|
||||
Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f);
|
||||
Element srcFormat = mipFace->_format;
|
||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
||||
|
||||
glTexImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0,
|
||||
texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->_sysmem.read<Byte>()));
|
||||
|
||||
// At this point the mip pixels have been loaded, we can notify
|
||||
texture.notifyMipFaceGPULoaded(0, f);
|
||||
}
|
||||
}
|
||||
|
||||
if (texture.isAutogenerateMips()) {
|
||||
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
} else {
|
||||
|
@ -438,10 +433,8 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
|
|||
|
||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
||||
|
||||
// At this point the mip piels have been loaded, we can notify
|
||||
texture.notifyGPULoaded(0);
|
||||
|
||||
object->_storageStamp = texture.getStamp();
|
||||
object->_contentStamp = texture.getDataStamp();
|
||||
object->_size = texture.getSize();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,69 +28,103 @@ Texture::Pixels::~Pixels() {
|
|||
|
||||
void Texture::Storage::assignTexture(Texture* texture) {
|
||||
_texture = texture;
|
||||
}
|
||||
|
||||
Stamp Texture::Storage::getStamp(uint16 level) const {
|
||||
PixelsPointer mip = getMip(level);
|
||||
if (mip) {
|
||||
return mip->_sysmem.getStamp();
|
||||
if (_texture) {
|
||||
_type = _texture->getType();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Texture::Storage::reset() {
|
||||
_mips.clear();
|
||||
bumpStamp();
|
||||
}
|
||||
|
||||
Texture::PixelsPointer Texture::Storage::editMip(uint16 level) {
|
||||
Texture::PixelsPointer Texture::Storage::editMipFace(uint16 level, uint8 face) {
|
||||
if (level < _mips.size()) {
|
||||
return _mips[level];
|
||||
assert(face < _mips[level].size());
|
||||
bumpStamp();
|
||||
return _mips[level][face];
|
||||
}
|
||||
return PixelsPointer();
|
||||
}
|
||||
|
||||
const Texture::PixelsPointer Texture::Storage::getMip(uint16 level) const {
|
||||
const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 face) const {
|
||||
if (level < _mips.size()) {
|
||||
return _mips[level];
|
||||
assert(face < _mips[level].size());
|
||||
return _mips[level][face];
|
||||
}
|
||||
return PixelsPointer();
|
||||
}
|
||||
|
||||
void Texture::Storage::notifyGPULoaded(uint16 level) const {
|
||||
PixelsPointer mip = getMip(level);
|
||||
if (mip) {
|
||||
mip->_isGPULoaded = true;
|
||||
mip->_sysmem.resize(0);
|
||||
void Texture::Storage::notifyMipFaceGPULoaded(uint16 level, uint8 face) const {
|
||||
PixelsPointer mipFace = getMipFace(level, face);
|
||||
if (mipFace) {
|
||||
mipFace->_isGPULoaded = true;
|
||||
mipFace->_sysmem.resize(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture::Storage::isMipAvailable(uint16 level) const {
|
||||
PixelsPointer mip = getMip(level);
|
||||
return (mip && mip->_sysmem.getSize());
|
||||
bool Texture::Storage::isMipAvailable(uint16 level, uint8 face) const {
|
||||
PixelsPointer mipFace = getMipFace(level, face);
|
||||
return (mipFace && mipFace->_sysmem.getSize());
|
||||
}
|
||||
|
||||
bool Texture::Storage::allocateMip(uint16 level) {
|
||||
bool changed = false;
|
||||
if (level >= _mips.size()) {
|
||||
_mips.resize(level+1, PixelsPointer());
|
||||
_mips.resize(level+1, std::vector<PixelsPointer>(Texture::NUM_FACES_PER_TYPE[getType()]));
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!_mips[level]) {
|
||||
_mips[level] = PixelsPointer(new Pixels());
|
||||
changed = true;
|
||||
auto& mip = _mips[level];
|
||||
for (auto& face : mip) {
|
||||
if (!face) {
|
||||
face.reset(new Pixels());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
bumpStamp();
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes) {
|
||||
// Ok we should be able to do that...
|
||||
|
||||
allocateMip(level);
|
||||
auto& mip = _mips[level];
|
||||
|
||||
// here we grabbed an array of faces
|
||||
// The bytes assigned here are supposed to contain all the faces bytes of the mip.
|
||||
// For tex1D, 2D, 3D there is only one face
|
||||
// For Cube, we expect the 6 faces in the order X+, X-, Y+, Y-, Z+, Z-
|
||||
int sizePerFace = size / mip.size();
|
||||
auto faceBytes = bytes;
|
||||
Size allocated = 0;
|
||||
for (auto& face : mip) {
|
||||
face->_format = format;
|
||||
allocated += face->_sysmem.setData(sizePerFace, faceBytes);
|
||||
face->_isGPULoaded = false;
|
||||
faceBytes += sizePerFace;
|
||||
}
|
||||
|
||||
bumpStamp();
|
||||
|
||||
return allocated == size;
|
||||
}
|
||||
|
||||
|
||||
bool Texture::Storage::assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face) {
|
||||
|
||||
allocateMip(level);
|
||||
auto mip = _mips[level];
|
||||
mip->_format = format;
|
||||
Size allocated = mip->_sysmem.setData(size, bytes);
|
||||
mip->_isGPULoaded = false;
|
||||
Size allocated = 0;
|
||||
if (face < mip.size()) {
|
||||
auto mipFace = mip[face];
|
||||
mipFace->_format = format;
|
||||
allocated += mipFace->_sysmem.setData(size, bytes);
|
||||
mipFace->_isGPULoaded = false;
|
||||
bumpStamp();
|
||||
}
|
||||
|
||||
return allocated == size;
|
||||
}
|
||||
|
@ -115,8 +149,8 @@ Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, ui
|
|||
{
|
||||
Texture* tex = new Texture();
|
||||
tex->_storage.reset(new Storage());
|
||||
tex->_storage->_texture = tex;
|
||||
tex->_type = type;
|
||||
tex->_storage->assignTexture(tex);
|
||||
tex->_maxMip = 0;
|
||||
tex->resize(type, texelFormat, width, height, depth, numSamples, numSlices);
|
||||
|
||||
|
@ -275,6 +309,37 @@ bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, co
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Texture::assignStoredMipFace(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face) {
|
||||
// Check that level accessed make sense
|
||||
if (level != 0) {
|
||||
if (_autoGenerateMips) {
|
||||
return false;
|
||||
}
|
||||
if (level >= evalNumMips()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// THen check that the mem buffer passed make sense with its format
|
||||
Size expectedSize = evalStoredMipFaceSize(level, format);
|
||||
if (size == expectedSize) {
|
||||
_storage->assignMipFaceData(level, format, size, bytes, face);
|
||||
_stamp++;
|
||||
return true;
|
||||
} else if (size > expectedSize) {
|
||||
// NOTE: We are facing this case sometime because apparently QImage (from where we get the bits) is generating images
|
||||
// and alligning the line of pixels to 32 bits.
|
||||
// We should probably consider something a bit more smart to get the correct result but for now (UI elements)
|
||||
// it seems to work...
|
||||
_storage->assignMipFaceData(level, format, size, bytes, face);
|
||||
_stamp++;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16 Texture::autoGenerateMips(uint16 maxMip) {
|
||||
_autoGenerateMips = true;
|
||||
_maxMip = std::min((uint16) (evalNumMips() - 1), maxMip);
|
||||
|
@ -283,15 +348,15 @@ uint16 Texture::autoGenerateMips(uint16 maxMip) {
|
|||
}
|
||||
|
||||
uint16 Texture::getStoredMipWidth(uint16 level) const {
|
||||
PixelsPointer mip = accessStoredMip(level);
|
||||
if (mip && mip->_sysmem.getSize()) {
|
||||
PixelsPointer mipFace = accessStoredMipFace(level);
|
||||
if (mipFace && mipFace->_sysmem.getSize()) {
|
||||
return evalMipWidth(level);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 Texture::getStoredMipHeight(uint16 level) const {
|
||||
PixelsPointer mip = accessStoredMip(level);
|
||||
PixelsPointer mip = accessStoredMipFace(level);
|
||||
if (mip && mip->_sysmem.getSize()) {
|
||||
return evalMipHeight(level);
|
||||
}
|
||||
|
@ -299,24 +364,24 @@ uint16 Texture::getStoredMipHeight(uint16 level) const {
|
|||
}
|
||||
|
||||
uint16 Texture::getStoredMipDepth(uint16 level) const {
|
||||
PixelsPointer mip = accessStoredMip(level);
|
||||
if (mip && mip->_sysmem.getSize()) {
|
||||
PixelsPointer mipFace = accessStoredMipFace(level);
|
||||
if (mipFace && mipFace->_sysmem.getSize()) {
|
||||
return evalMipDepth(level);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 Texture::getStoredMipNumTexels(uint16 level) const {
|
||||
PixelsPointer mip = accessStoredMip(level);
|
||||
if (mip && mip->_sysmem.getSize()) {
|
||||
PixelsPointer mipFace = accessStoredMipFace(level);
|
||||
if (mipFace && mipFace->_sysmem.getSize()) {
|
||||
return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 Texture::getStoredMipSize(uint16 level) const {
|
||||
PixelsPointer mip = accessStoredMip(level);
|
||||
if (mip && mip->_sysmem.getSize()) {
|
||||
PixelsPointer mipFace = accessStoredMipFace(level);
|
||||
if (mipFace && mipFace->_sysmem.getSize()) {
|
||||
return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level) * getTexelFormat().getSize();
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -56,12 +56,12 @@ public:
|
|||
glm::vec4 _borderColor{ 1.0f };
|
||||
uint32 _maxAnisotropy = 16;
|
||||
|
||||
uint8 _filter = FILTER_MIN_MAG_POINT;
|
||||
uint8 _comparisonFunc = ALWAYS;
|
||||
|
||||
uint8 _wrapModeU = WRAP_REPEAT;
|
||||
uint8 _wrapModeV = WRAP_REPEAT;
|
||||
uint8 _wrapModeW = WRAP_REPEAT;
|
||||
|
||||
uint8 _filter = FILTER_MIN_MAG_POINT;
|
||||
uint8 _comparisonFunc = ALWAYS;
|
||||
|
||||
uint8 _mipOffset = 0;
|
||||
uint8 _minMip = 0;
|
||||
|
@ -112,28 +112,6 @@ public:
|
|||
};
|
||||
typedef std::shared_ptr< Pixels > PixelsPointer;
|
||||
|
||||
class Storage {
|
||||
public:
|
||||
Storage() {}
|
||||
virtual ~Storage() {}
|
||||
virtual void reset();
|
||||
virtual PixelsPointer editMip(uint16 level);
|
||||
virtual const PixelsPointer getMip(uint16 level) const;
|
||||
virtual Stamp getStamp(uint16 level) const;
|
||||
virtual bool allocateMip(uint16 level);
|
||||
virtual bool assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes);
|
||||
virtual bool isMipAvailable(uint16 level) const;
|
||||
virtual void notifyGPULoaded(uint16 level) const;
|
||||
|
||||
protected:
|
||||
Texture* _texture;
|
||||
std::vector<PixelsPointer> _mips;
|
||||
|
||||
virtual void assignTexture(Texture* tex);
|
||||
|
||||
friend class Texture;
|
||||
};
|
||||
|
||||
enum Type {
|
||||
TEX_1D = 0,
|
||||
TEX_2D,
|
||||
|
@ -143,6 +121,51 @@ public:
|
|||
NUM_TYPES,
|
||||
};
|
||||
|
||||
// Definition of the cube face name and layout
|
||||
enum CubeFace {
|
||||
CUBE_FACE_RIGHT_POS_X = 0,
|
||||
CUBE_FACE_LEFT_NEG_X,
|
||||
CUBE_FACE_TOP_POS_Y,
|
||||
CUBE_FACE_BOTTOM_NEG_Y,
|
||||
CUBE_FACE_BACK_POS_X,
|
||||
CUBE_FACE_FRONT_NEG_Z,
|
||||
|
||||
NUM_CUBE_FACES, // Not a valid vace index
|
||||
};
|
||||
|
||||
class Storage {
|
||||
public:
|
||||
Storage() {}
|
||||
virtual ~Storage() {}
|
||||
virtual void reset();
|
||||
virtual PixelsPointer editMipFace(uint16 level, uint8 face = 0);
|
||||
virtual const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const;
|
||||
virtual bool allocateMip(uint16 level);
|
||||
virtual bool assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes);
|
||||
virtual bool assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face);
|
||||
virtual bool isMipAvailable(uint16 level, uint8 face = 0) const;
|
||||
|
||||
Texture::Type getType() const { return _type; }
|
||||
|
||||
Stamp getStamp() const { return _stamp; }
|
||||
Stamp bumpStamp() { return ++_stamp; }
|
||||
protected:
|
||||
Stamp _stamp = 0;
|
||||
Texture* _texture = nullptr;
|
||||
Texture::Type _type = Texture::TEX_2D; // The type of texture is needed to know the number of faces to expect
|
||||
std::vector<std::vector<PixelsPointer>> _mips; // an array of mips, each mip is an array of faces
|
||||
|
||||
virtual void assignTexture(Texture* tex); // Texture storage is pointing to ONE corrresponding Texture.
|
||||
const Texture* getTexture() const { return _texture; }
|
||||
|
||||
friend class Texture;
|
||||
|
||||
// THis should be only called by the Texture from the Backend to notify the storage that the specified mip face pixels
|
||||
// have been uploaded to the GPU memory. IT is possible for the storage to free the system memory then
|
||||
virtual void notifyMipFaceGPULoaded(uint16 level, uint8 face) const;
|
||||
};
|
||||
|
||||
|
||||
static Texture* create1D(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler());
|
||||
static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler());
|
||||
static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler());
|
||||
|
@ -155,7 +178,7 @@ public:
|
|||
~Texture();
|
||||
|
||||
Stamp getStamp() const { return _stamp; }
|
||||
Stamp getDataStamp(uint16 level = 0) const { return _storage->getStamp(level); }
|
||||
Stamp getDataStamp() const { return _storage->getStamp(); }
|
||||
|
||||
// The size in bytes of data stored in the texture
|
||||
Size getSize() const { return _size; }
|
||||
|
@ -214,8 +237,16 @@ public:
|
|||
uint16 evalMipWidth(uint16 level) const { return std::max(_width >> level, 1); }
|
||||
uint16 evalMipHeight(uint16 level) const { return std::max(_height >> level, 1); }
|
||||
uint16 evalMipDepth(uint16 level) const { return std::max(_depth >> level, 1); }
|
||||
uint32 evalMipNumTexels(uint16 level) const { return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level) * getNumFaces(); }
|
||||
|
||||
// Size for each face of a mip at a particular level
|
||||
uint32 evalMipFaceNumTexels(uint16 level) const { return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level); }
|
||||
uint32 evalMipFaceSize(uint16 level) const { return evalMipFaceNumTexels(level) * getTexelFormat().getSize(); }
|
||||
|
||||
// Total size for the mip
|
||||
uint32 evalMipNumTexels(uint16 level) const { return evalMipFaceNumTexels(level) * getNumFaces(); }
|
||||
uint32 evalMipSize(uint16 level) const { return evalMipNumTexels(level) * getTexelFormat().getSize(); }
|
||||
|
||||
uint32 evalStoredMipFaceSize(uint16 level, const Element& format) const { return evalMipFaceNumTexels(level) * format.getSize(); }
|
||||
uint32 evalStoredMipSize(uint16 level, const Element& format) const { return evalMipNumTexels(level) * format.getSize(); }
|
||||
|
||||
uint32 evalTotalSize() const {
|
||||
|
@ -256,11 +287,11 @@ public:
|
|||
// Explicitely assign mip data for a certain level
|
||||
// If Bytes is NULL then simply allocate the space so mip sysmem can be accessed
|
||||
bool assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes);
|
||||
bool assignStoredMipFace(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face);
|
||||
|
||||
// Access the the sub mips
|
||||
bool isStoredMipAvailable(uint16 level) const { return _storage->isMipAvailable(level); }
|
||||
const PixelsPointer accessStoredMip(uint16 level) const { return _storage->getMip(level); }
|
||||
void notifyGPULoaded(uint16 level) const { return _storage->notifyGPULoaded(level); }
|
||||
bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); }
|
||||
const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); }
|
||||
|
||||
// access sizes for the stored mips
|
||||
uint16 getStoredMipWidth(uint16 level) const;
|
||||
|
@ -277,6 +308,9 @@ public:
|
|||
const Sampler& getSampler() const { return _sampler; }
|
||||
Stamp getSamplerStamp() const { return _samplerStamp; }
|
||||
|
||||
// Only callable by the Backend
|
||||
void notifyMipFaceGPULoaded(uint16 level, uint8 face) const { return _storage->notifyMipFaceGPULoaded(level, face); }
|
||||
|
||||
protected:
|
||||
std::unique_ptr< Storage > _storage;
|
||||
|
||||
|
@ -310,6 +344,7 @@ protected:
|
|||
mutable GPUObject* _gpuObject = NULL;
|
||||
void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; }
|
||||
GPUObject* getGPUObject() const { return _gpuObject; }
|
||||
|
||||
friend class Backend;
|
||||
};
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
|
|||
static gpu::PipelinePointer thePipeline;
|
||||
static gpu::BufferPointer theBuffer;
|
||||
static gpu::Stream::FormatPointer theFormat;
|
||||
static gpu::BufferPointer theConstants;
|
||||
int SKYBOX_CONSTANTS_SLOT = 0; // need to be defined by the compilation of the shader
|
||||
if (!thePipeline) {
|
||||
auto skyVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(Skybox_vert)));
|
||||
auto skyFS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(Skybox_frag)));
|
||||
|
@ -59,11 +61,15 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
|
|||
|
||||
gpu::Shader::BindingSet bindings;
|
||||
bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), 0));
|
||||
|
||||
if (!gpu::Shader::makeProgram(*skyShader, bindings)) {
|
||||
|
||||
}
|
||||
|
||||
SKYBOX_CONSTANTS_SLOT = skyShader->getBuffers().findLocation("skyboxBuffer");
|
||||
if (SKYBOX_CONSTANTS_SLOT == gpu::Shader::INVALID_LOCATION) {
|
||||
SKYBOX_CONSTANTS_SLOT = skyShader->getUniforms().findLocation("skyboxBuffer");
|
||||
}
|
||||
|
||||
auto skyState = gpu::StatePointer(new gpu::State());
|
||||
|
||||
thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState));
|
||||
|
@ -74,6 +80,9 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
|
|||
|
||||
theFormat.reset(new gpu::Stream::Format());
|
||||
theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ));
|
||||
|
||||
auto color = glm::vec4(1.0f);
|
||||
theConstants.reset(new gpu::Buffer(sizeof(color), (const gpu::Byte*) &color));
|
||||
}
|
||||
|
||||
glm::mat4 projMat;
|
||||
|
@ -82,11 +91,19 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
|
|||
Transform viewTransform;
|
||||
viewFrustum.evalViewTransform(viewTransform);
|
||||
|
||||
if (glm::all(glm::equal(skybox.getColor(), glm::vec3(0.0f)))) {
|
||||
auto color = glm::vec4(1.0f);
|
||||
theConstants->setSubData(0, sizeof(color), (const gpu::Byte*) &color);
|
||||
} else {
|
||||
theConstants->setSubData(0, sizeof(Color), (const gpu::Byte*) &skybox.getColor());
|
||||
}
|
||||
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewTransform);
|
||||
batch.setModelTransform(Transform()); // only for Mac
|
||||
batch.setPipeline(thePipeline);
|
||||
batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8);
|
||||
batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, theConstants, 0, theConstants->getSize());
|
||||
batch.setInputFormat(theFormat);
|
||||
batch.setUniformTexture(0, skybox.getCubemap());
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
|
||||
protected:
|
||||
gpu::TexturePointer _cubemap;
|
||||
|
||||
Color _color{1.0f, 1.0f, 1.0f};
|
||||
};
|
||||
typedef std::shared_ptr< Skybox > SkyboxPointer;
|
||||
|
|
|
@ -20,6 +20,5 @@ varying vec3 color;
|
|||
void main(void) {
|
||||
vec3 coord = normalize(normal);
|
||||
vec4 texel = textureCube(cubeMap, coord);
|
||||
// gl_FragData[0] = vec4(texel.xyz * color, texel.w);
|
||||
gl_FragData[0] = vec4(texel.xyz, 1.0);
|
||||
gl_FragData[0] = vec4(texel.xyz * color, 0.0);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,26 @@
|
|||
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
struct Skybox {
|
||||
vec4 _color;
|
||||
};
|
||||
|
||||
<@if GPU_FEATURE_PROFILE == GPU_CORE @>
|
||||
uniform skyboxBuffer {
|
||||
Skybox _skybox;
|
||||
};
|
||||
Skybox getSkybox() {
|
||||
return _skybox;
|
||||
}
|
||||
<@else@>
|
||||
uniform vec4 skyboxBuffer[1];
|
||||
Skybox getSkybox() {
|
||||
Skybox _skybox;
|
||||
_skybox._color = skyboxBuffer[0];
|
||||
return _skybox;
|
||||
}
|
||||
<@endif@>
|
||||
|
||||
varying vec3 normal;
|
||||
varying vec2 texcoord;
|
||||
varying vec3 color;
|
||||
|
@ -22,8 +42,8 @@ varying vec3 color;
|
|||
void main(void) {
|
||||
texcoord = gl_Vertex.xy;
|
||||
|
||||
// pass along the diffuse color
|
||||
color = vec3(texcoord, 0.0);
|
||||
Skybox skybox = getSkybox();
|
||||
color = skybox._color.xyz;
|
||||
|
||||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
|
@ -31,9 +51,7 @@ void main(void) {
|
|||
vec3 eyeDir;
|
||||
|
||||
<$transformClipToEyeDir(cam, clipDir, eyeDir)$>;
|
||||
normal = normalize(eyeDir);
|
||||
<$transformEyeToWorldDir(cam, eyeDir, normal)$>;
|
||||
normal = normalize(normal);
|
||||
|
||||
// Position is supposed to cmoe in clip space
|
||||
gl_Position = vec4(texcoord.xy, 0.0, 1.0);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "NetworkLogging.h"
|
||||
#include "DataServerAccountInfo.h"
|
||||
|
||||
#ifndef __GNUC__
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "LimitedNodeList.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
@ -28,8 +30,6 @@
|
|||
#include "AccountManager.h"
|
||||
#include "Assignment.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include "LimitedNodeList.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "UUID.h"
|
||||
#include "NetworkLogging.h"
|
||||
|
||||
|
@ -230,21 +230,13 @@ qint64 LimitedNodeList::readDatagram(QByteArray& incomingPacket, QHostAddress* a
|
|||
return result;
|
||||
}
|
||||
|
||||
qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr,
|
||||
const QUuid& connectionSecret) {
|
||||
QByteArray datagramCopy = datagram;
|
||||
|
||||
if (!connectionSecret.isNull()) {
|
||||
// setup the MD5 hash for source verification in the header
|
||||
replaceHashInPacketGivenConnectionUUID(datagramCopy, connectionSecret);
|
||||
}
|
||||
|
||||
qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr) {
|
||||
// XXX can BandwidthRecorder be used for this?
|
||||
// stat collection for packets
|
||||
++_numCollectedPackets;
|
||||
_numCollectedBytes += datagram.size();
|
||||
|
||||
qint64 bytesWritten = _nodeSocket.writeDatagram(datagramCopy,
|
||||
qint64 bytesWritten = _nodeSocket.writeDatagram(datagram,
|
||||
destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
||||
|
||||
if (bytesWritten < 0) {
|
||||
|
@ -258,6 +250,12 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram,
|
|||
const SharedNodePointer& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr) {
|
||||
if (destinationNode) {
|
||||
PacketType packetType = packetTypeForPacket(datagram);
|
||||
|
||||
if (NON_VERIFIED_PACKETS.contains(packetType)) {
|
||||
return writeUnverifiedDatagram(datagram, destinationNode, overridenSockAddr);
|
||||
}
|
||||
|
||||
// if we don't have an overridden address, assume they want to send to the node's active socket
|
||||
const HifiSockAddr* destinationSockAddr = &overridenSockAddr;
|
||||
if (overridenSockAddr.isNull()) {
|
||||
|
@ -270,8 +268,26 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram,
|
|||
}
|
||||
}
|
||||
|
||||
QByteArray datagramCopy = datagram;
|
||||
|
||||
// if we're here and the connection secret is null, debug out - this could be a problem
|
||||
if (destinationNode->getConnectionSecret().isNull()) {
|
||||
qDebug() << "LimitedNodeList::writeDatagram called for verified datagram with null connection secret for"
|
||||
<< "destination node" << destinationNode->getUUID() << " - this is either not secure or will cause"
|
||||
<< "this packet to be unverifiable on the receiving side.";
|
||||
}
|
||||
|
||||
// perform replacement of hash and optionally also sequence number in the header
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
PacketSequenceNumber sequenceNumber = getNextSequenceNumberForPacket(destinationNode->getUUID(), packetType);
|
||||
replaceHashAndSequenceNumberInPacket(datagramCopy, destinationNode->getConnectionSecret(),
|
||||
sequenceNumber, packetType);
|
||||
} else {
|
||||
replaceHashInPacket(datagramCopy, destinationNode->getConnectionSecret(), packetType);
|
||||
}
|
||||
|
||||
emit dataSent(destinationNode->getType(), datagram.size());
|
||||
auto bytesWritten = writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret());
|
||||
auto bytesWritten = writeDatagram(datagramCopy, *destinationSockAddr);
|
||||
// Keep track of per-destination-node bandwidth
|
||||
destinationNode->recordBytesSent(bytesWritten);
|
||||
return bytesWritten;
|
||||
|
@ -296,8 +312,21 @@ qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, cons
|
|||
}
|
||||
}
|
||||
|
||||
// don't use the node secret!
|
||||
return writeDatagram(datagram, *destinationSockAddr, QUuid());
|
||||
PacketType packetType = packetTypeForPacket(datagram);
|
||||
|
||||
// optionally peform sequence number replacement in the header
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
|
||||
QByteArray datagramCopy = datagram;
|
||||
|
||||
PacketSequenceNumber sequenceNumber = getNextSequenceNumberForPacket(destinationNode->getUUID(), packetType);
|
||||
replaceSequenceNumberInPacket(datagramCopy, sequenceNumber, packetType);
|
||||
|
||||
// send the datagram with sequence number replaced in header
|
||||
return writeDatagram(datagramCopy, *destinationSockAddr);
|
||||
} else {
|
||||
return writeDatagram(datagram, *destinationSockAddr);
|
||||
}
|
||||
}
|
||||
|
||||
// didn't have a destinationNode to send to, return 0
|
||||
|
@ -305,7 +334,7 @@ qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, cons
|
|||
}
|
||||
|
||||
qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr) {
|
||||
return writeDatagram(datagram, destinationSockAddr, QUuid());
|
||||
return writeDatagram(datagram, destinationSockAddr);
|
||||
}
|
||||
|
||||
qint64 LimitedNodeList::writeDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
|
||||
|
@ -318,6 +347,15 @@ qint64 LimitedNodeList::writeUnverifiedDatagram(const char* data, qint64 size, c
|
|||
return writeUnverifiedDatagram(QByteArray(data, size), destinationNode, overridenSockAddr);
|
||||
}
|
||||
|
||||
PacketSequenceNumber LimitedNodeList::getNextSequenceNumberForPacket(const QUuid& nodeUUID, PacketType packetType) {
|
||||
// Thanks to std::map and std::unordered_map this line either default constructs the
|
||||
// PacketTypeSequenceMap and the PacketSequenceNumber or returns the existing value.
|
||||
// We use the postfix increment so that the stored value is incremented and the next
|
||||
// return gives the correct value.
|
||||
|
||||
return _packetSequenceNumbers[nodeUUID][packetType]++;
|
||||
}
|
||||
|
||||
void LimitedNodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
|
||||
// the node decided not to do anything with this packet
|
||||
// if it comes from a known source we should keep that node alive
|
||||
|
@ -332,6 +370,13 @@ int LimitedNodeList::updateNodeWithDataFromPacket(const SharedNodePointer& match
|
|||
|
||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
// if this was a sequence numbered packet we should store the last seq number for
|
||||
// a packet of this type for this node
|
||||
PacketType packetType = packetTypeForPacket(packet);
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
matchingNode->setLastSequenceNumberForPacketType(sequenceNumberFromHeader(packet, packetType), packetType);
|
||||
}
|
||||
|
||||
NodeData* linkedData = matchingNode->getLinkedData();
|
||||
if (!linkedData && linkedDataCreateCallback) {
|
||||
linkedDataCreateCallback(matchingNode.data());
|
||||
|
@ -466,8 +511,11 @@ unsigned LimitedNodeList::broadcastToNodes(const QByteArray& packet, const NodeS
|
|||
}
|
||||
|
||||
QByteArray LimitedNodeList::constructPingPacket(PingType_t pingType, bool isVerified, const QUuid& packetHeaderID) {
|
||||
QByteArray pingPacket = byteArrayWithPopulatedHeader(isVerified ? PacketTypePing : PacketTypeUnverifiedPing,
|
||||
packetHeaderID);
|
||||
|
||||
QUuid packetUUID = packetHeaderID.isNull() ? _sessionUUID : packetHeaderID;
|
||||
|
||||
QByteArray pingPacket = byteArrayWithUUIDPopulatedHeader(isVerified ? PacketTypePing : PacketTypeUnverifiedPing,
|
||||
packetUUID);
|
||||
|
||||
QDataStream packetStream(&pingPacket, QIODevice::Append);
|
||||
|
||||
|
@ -489,8 +537,10 @@ QByteArray LimitedNodeList::constructPingReplyPacket(const QByteArray& pingPacke
|
|||
|
||||
PacketType replyType = (packetTypeForPacket(pingPacket) == PacketTypePing)
|
||||
? PacketTypePingReply : PacketTypeUnverifiedPingReply;
|
||||
|
||||
QByteArray replyPacket = byteArrayWithPopulatedHeader(replyType, packetHeaderID);
|
||||
|
||||
QUuid packetUUID = packetHeaderID.isNull() ? _sessionUUID : packetHeaderID;
|
||||
|
||||
QByteArray replyPacket = byteArrayWithUUIDPopulatedHeader(replyType, packetUUID);
|
||||
QDataStream packetStream(&replyPacket, QIODevice::Append);
|
||||
|
||||
packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow();
|
||||
|
@ -522,11 +572,11 @@ void LimitedNodeList::removeSilentNodes() {
|
|||
eachNodeHashIterator([&](NodeHash::iterator& it){
|
||||
SharedNodePointer node = it->second;
|
||||
node->getMutex().lock();
|
||||
|
||||
|
||||
if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > (NODE_SILENCE_THRESHOLD_MSECS * USECS_PER_MSEC)) {
|
||||
// call the NodeHash erase to get rid of this node
|
||||
it = _nodeHash.unsafe_erase(it);
|
||||
|
||||
|
||||
killedNodes.insert(node);
|
||||
} else {
|
||||
// we didn't erase this node, push the iterator forwards
|
||||
|
@ -680,7 +730,7 @@ void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSock
|
|||
headerID = _sessionUUID;
|
||||
}
|
||||
|
||||
QByteArray iceRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeIceServerHeartbeat, headerID);
|
||||
QByteArray iceRequestByteArray = byteArrayWithUUIDPopulatedHeader(PacketTypeIceServerHeartbeat, headerID);
|
||||
QDataStream iceDataStream(&iceRequestByteArray, QIODevice::Append);
|
||||
|
||||
iceDataStream << _publicSockAddr << _localSockAddr;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <stdint.h>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h> // not on windows, not needed for mac or windows
|
||||
|
@ -34,6 +35,7 @@
|
|||
|
||||
#include "DomainHandler.h"
|
||||
#include "Node.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "UUIDHasher.h"
|
||||
|
||||
const int MAX_PACKET_SIZE = 1450;
|
||||
|
@ -50,10 +52,8 @@ const unsigned short STUN_SERVER_PORT = 3478;
|
|||
const QString DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY = "domain-server.local-port";
|
||||
const QString DOMAIN_SERVER_LOCAL_HTTP_PORT_SMEM_KEY = "domain-server.local-http-port";
|
||||
const QString DOMAIN_SERVER_LOCAL_HTTPS_PORT_SMEM_KEY = "domain-server.local-https-port";
|
||||
const QString ASSIGNMENT_CLIENT_MONITOR_LOCAL_PORT_SMEM_KEY = "assignment-client-monitor.local-port";
|
||||
|
||||
const char DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME[] = "localhost";
|
||||
const unsigned short DEFAULT_ASSIGNMENT_CLIENT_MONITOR_PORT = 40104;
|
||||
const QHostAddress DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME = QHostAddress::LocalHost;
|
||||
|
||||
const QString USERNAME_UUID_REPLACEMENT_STATS_KEY = "$username";
|
||||
|
||||
|
@ -96,6 +96,13 @@ public:
|
|||
|
||||
bool packetVersionAndHashMatch(const QByteArray& packet);
|
||||
|
||||
QByteArray byteArrayWithPopulatedHeader(PacketType packetType)
|
||||
{ return byteArrayWithUUIDPopulatedHeader(packetType, _sessionUUID); }
|
||||
int populatePacketHeader(QByteArray& packet, PacketType packetType)
|
||||
{ return populatePacketHeaderWithUUID(packet, packetType, _sessionUUID); }
|
||||
int populatePacketHeader(char* packet, PacketType packetType)
|
||||
{ return populatePacketHeaderWithUUID(packet, packetType, _sessionUUID); }
|
||||
|
||||
qint64 readDatagram(QByteArray& incomingPacket, QHostAddress* address, quint16 * port);
|
||||
|
||||
qint64 writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,
|
||||
|
@ -105,13 +112,14 @@ public:
|
|||
const HifiSockAddr& overridenSockAddr = HifiSockAddr());
|
||||
|
||||
qint64 writeUnverifiedDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr);
|
||||
|
||||
qint64 writeDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr = HifiSockAddr());
|
||||
|
||||
qint64 writeUnverifiedDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr = HifiSockAddr());
|
||||
|
||||
void(*linkedDataCreateCallback)(Node *);
|
||||
void (*linkedDataCreateCallback)(Node *);
|
||||
|
||||
int size() const { return _nodeHash.size(); }
|
||||
|
||||
|
@ -224,8 +232,9 @@ protected:
|
|||
LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
void operator=(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
|
||||
qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr,
|
||||
const QUuid& connectionSecret);
|
||||
qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr);
|
||||
|
||||
PacketSequenceNumber getNextSequenceNumberForPacket(const QUuid& nodeUUID, PacketType packetType);
|
||||
|
||||
void changeSocketBufferSizes(int numBytes);
|
||||
|
||||
|
@ -247,6 +256,8 @@ protected:
|
|||
QElapsedTimer _packetStatTimer;
|
||||
bool _thisNodeCanAdjustLocks;
|
||||
bool _thisNodeCanRez;
|
||||
|
||||
std::unordered_map<QUuid, PacketTypeSequenceMap, UUIDHasher> _packetSequenceNumbers;
|
||||
|
||||
template<typename IteratorLambda>
|
||||
void eachNodeHashIterator(IteratorLambda functor) {
|
||||
|
|
|
@ -129,6 +129,15 @@ void Node::activateSymmetricSocket() {
|
|||
_activeSocket = &_symmetricSocket;
|
||||
}
|
||||
|
||||
PacketSequenceNumber Node::getLastSequenceNumberForPacketType(PacketType packetType) const {
|
||||
auto typeMatch = _lastSequenceNumbers.find(packetType);
|
||||
if (typeMatch != _lastSequenceNumbers.end()) {
|
||||
return typeMatch->second;
|
||||
} else {
|
||||
return DEFAULT_SEQUENCE_NUMBER;
|
||||
}
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream& out, const Node& node) {
|
||||
out << node._type;
|
||||
out << node._uuid;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "HifiSockAddr.h"
|
||||
#include "NetworkPeer.h"
|
||||
#include "NodeData.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "SimpleMovingAverage.h"
|
||||
#include "MovingPercentile.h"
|
||||
|
||||
|
@ -43,7 +44,7 @@ namespace NodeType {
|
|||
|
||||
class Node : public NetworkPeer {
|
||||
Q_OBJECT
|
||||
public:
|
||||
public:
|
||||
Node(const QUuid& uuid, NodeType_t type,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez);
|
||||
~Node();
|
||||
|
@ -86,6 +87,10 @@ public:
|
|||
void activatePublicSocket();
|
||||
void activateLocalSocket();
|
||||
void activateSymmetricSocket();
|
||||
|
||||
void setLastSequenceNumberForPacketType(PacketSequenceNumber sequenceNumber, PacketType packetType)
|
||||
{ _lastSequenceNumbers[packetType] = sequenceNumber; }
|
||||
PacketSequenceNumber getLastSequenceNumberForPacketType(PacketType packetType) const;
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& out, const Node& node);
|
||||
friend QDataStream& operator>>(QDataStream& in, Node& node);
|
||||
|
@ -109,6 +114,8 @@ private:
|
|||
MovingPercentile _clockSkewMovingPercentile;
|
||||
bool _canAdjustLocks;
|
||||
bool _canRez;
|
||||
|
||||
PacketTypeSequenceMap _lastSequenceNumbers;
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, const Node &message);
|
||||
|
|
|
@ -150,7 +150,11 @@ void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer&
|
|||
void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
|
||||
switch (packetTypeForPacket(packet)) {
|
||||
case PacketTypeDomainList: {
|
||||
processDomainServerList(packet);
|
||||
if (!_domainHandler.getSockAddr().isNull()) {
|
||||
// only process a list from domain-server if we're talking to a domain
|
||||
// TODO: how do we make sure this is actually the domain we want the list from (DTLS probably)
|
||||
processDomainServerList(packet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PacketTypeDomainServerRequireDTLS: {
|
||||
|
@ -183,7 +187,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
|||
}
|
||||
case PacketTypePingReply: {
|
||||
SharedNodePointer sendingNode = sendingNodeForPacket(packet);
|
||||
|
||||
|
||||
if (sendingNode) {
|
||||
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
|
@ -348,7 +352,7 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
}
|
||||
}
|
||||
|
||||
QByteArray domainServerPacket = byteArrayWithPopulatedHeader(domainPacketType, packetUUID);
|
||||
QByteArray domainServerPacket = byteArrayWithUUIDPopulatedHeader(domainPacketType, packetUUID);
|
||||
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
|
||||
|
||||
// pack our data to send to the domain-server
|
||||
|
@ -369,7 +373,7 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
}
|
||||
|
||||
if (!isUsingDTLS) {
|
||||
writeDatagram(domainServerPacket, _domainHandler.getSockAddr(), QUuid());
|
||||
writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr());
|
||||
}
|
||||
|
||||
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
|
||||
|
@ -424,10 +428,9 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
|
|||
_domainHandler.setUUID(uuidFromPacketHeader(packet));
|
||||
_domainHandler.setIsConnected(true);
|
||||
}
|
||||
|
||||
|
||||
int readNodes = 0;
|
||||
|
||||
|
||||
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
|
@ -497,7 +500,7 @@ void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
|
|||
|
||||
QByteArray publicPingPacket = constructPingPacket(PingType::Public);
|
||||
writeDatagram(publicPingPacket, node, node->getPublicSocket());
|
||||
|
||||
|
||||
if (!node->getSymmetricSocket().isNull()) {
|
||||
QByteArray symmetricPingPacket = constructPingPacket(PingType::Symmetric);
|
||||
writeDatagram(symmetricPingPacket, node, node->getSymmetricSocket());
|
||||
|
|
|
@ -9,14 +9,12 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "PacketHeaders.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "NodeList.h"
|
||||
|
||||
#include "PacketHeaders.h"
|
||||
|
||||
int arithmeticCodingValueFromBuffer(const char* checkValue) {
|
||||
if (((uchar) *checkValue) < 255) {
|
||||
return *checkValue;
|
||||
|
@ -45,8 +43,8 @@ int packArithmeticallyCodedValue(int value, char* destination) {
|
|||
}
|
||||
}
|
||||
|
||||
PacketVersion versionForPacketType(PacketType type) {
|
||||
switch (type) {
|
||||
PacketVersion versionForPacketType(PacketType packetType) {
|
||||
switch (packetType) {
|
||||
case PacketTypeMicrophoneAudioNoEcho:
|
||||
case PacketTypeMicrophoneAudioWithEcho:
|
||||
return 2;
|
||||
|
@ -57,7 +55,7 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
case PacketTypeInjectAudio:
|
||||
return 1;
|
||||
case PacketTypeAvatarData:
|
||||
return 5;
|
||||
return 6;
|
||||
case PacketTypeAvatarIdentity:
|
||||
return 1;
|
||||
case PacketTypeEnvironmentData:
|
||||
|
@ -86,8 +84,8 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
|
||||
#define PACKET_TYPE_NAME_LOOKUP(x) case x: return QString(#x);
|
||||
|
||||
QString nameForPacketType(PacketType type) {
|
||||
switch (type) {
|
||||
QString nameForPacketType(PacketType packetType) {
|
||||
switch (packetType) {
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnknown);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeStunResponse);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainList);
|
||||
|
@ -132,71 +130,81 @@ QString nameForPacketType(PacketType type) {
|
|||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPing);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPingReply);
|
||||
default:
|
||||
return QString("Type: ") + QString::number((int)type);
|
||||
return QString("Type: ") + QString::number((int)packetType);
|
||||
}
|
||||
return QString("unexpected");
|
||||
}
|
||||
|
||||
|
||||
|
||||
QByteArray byteArrayWithPopulatedHeader(PacketType type, const QUuid& connectionUUID) {
|
||||
QByteArray byteArrayWithUUIDPopulatedHeader(PacketType packetType, const QUuid& connectionUUID) {
|
||||
QByteArray freshByteArray(MAX_PACKET_HEADER_BYTES, 0);
|
||||
freshByteArray.resize(populatePacketHeader(freshByteArray, type, connectionUUID));
|
||||
freshByteArray.resize(populatePacketHeaderWithUUID(freshByteArray, packetType, connectionUUID));
|
||||
return freshByteArray;
|
||||
}
|
||||
|
||||
int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID) {
|
||||
if (packet.size() < numBytesForPacketHeaderGivenPacketType(type)) {
|
||||
packet.resize(numBytesForPacketHeaderGivenPacketType(type));
|
||||
int populatePacketHeaderWithUUID(QByteArray& packet, PacketType packetType, const QUuid& connectionUUID) {
|
||||
if (packet.size() < numBytesForPacketHeaderGivenPacketType(packetType)) {
|
||||
packet.resize(numBytesForPacketHeaderGivenPacketType(packetType));
|
||||
}
|
||||
|
||||
return populatePacketHeader(packet.data(), type, connectionUUID);
|
||||
return populatePacketHeaderWithUUID(packet.data(), packetType, connectionUUID);
|
||||
}
|
||||
|
||||
int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionUUID) {
|
||||
int numTypeBytes = packArithmeticallyCodedValue(type, packet);
|
||||
packet[numTypeBytes] = versionForPacketType(type);
|
||||
int populatePacketHeaderWithUUID(char* packet, PacketType packetType, const QUuid& connectionUUID) {
|
||||
int numTypeBytes = packArithmeticallyCodedValue(packetType, packet);
|
||||
packet[numTypeBytes] = versionForPacketType(packetType);
|
||||
|
||||
char* position = packet + numTypeBytes + sizeof(PacketVersion);
|
||||
|
||||
QUuid packUUID = connectionUUID.isNull() ? DependencyManager::get<LimitedNodeList>()->getSessionUUID() : connectionUUID;
|
||||
|
||||
QByteArray rfcUUID = packUUID.toRfc4122();
|
||||
QByteArray rfcUUID = connectionUUID.toRfc4122();
|
||||
memcpy(position, rfcUUID.constData(), NUM_BYTES_RFC4122_UUID);
|
||||
position += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
if (!NON_VERIFIED_PACKETS.contains(type)) {
|
||||
if (!NON_VERIFIED_PACKETS.contains(packetType)) {
|
||||
// pack 16 bytes of zeros where the md5 hash will be placed once data is packed
|
||||
memset(position, 0, NUM_BYTES_MD5_HASH);
|
||||
position += NUM_BYTES_MD5_HASH;
|
||||
}
|
||||
|
||||
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
// Pack zeros for the number of bytes that the sequence number requires.
|
||||
// The LimitedNodeList will handle packing in the sequence number when sending out the packet.
|
||||
memset(position, 0, sizeof(PacketSequenceNumber));
|
||||
position += sizeof(PacketSequenceNumber);
|
||||
}
|
||||
|
||||
// return the number of bytes written for pointer pushing
|
||||
return position - packet;
|
||||
}
|
||||
|
||||
int numBytesForPacketHeader(const QByteArray& packet) {
|
||||
// returns the number of bytes used for the type, version, and UUID
|
||||
return numBytesArithmeticCodingFromBuffer(packet.data())
|
||||
+ numHashBytesInPacketHeaderGivenPacketType(packetTypeForPacket(packet))
|
||||
+ NUM_STATIC_HEADER_BYTES;
|
||||
PacketType packetType = packetTypeForPacket(packet);
|
||||
return numBytesForPacketHeaderGivenPacketType(packetType);
|
||||
}
|
||||
|
||||
int numBytesForPacketHeader(const char* packet) {
|
||||
// returns the number of bytes used for the type, version, and UUID
|
||||
return numBytesArithmeticCodingFromBuffer(packet)
|
||||
+ numHashBytesInPacketHeaderGivenPacketType(packetTypeForPacket(packet))
|
||||
PacketType packetType = packetTypeForPacket(packet);
|
||||
return numBytesForPacketHeaderGivenPacketType(packetType);
|
||||
}
|
||||
|
||||
int numBytesForArithmeticCodedPacketType(PacketType packetType) {
|
||||
return (int) ceilf((float) packetType / 255);
|
||||
}
|
||||
|
||||
int numBytesForPacketHeaderGivenPacketType(PacketType packetType) {
|
||||
return numBytesForArithmeticCodedPacketType(packetType)
|
||||
+ numHashBytesForType(packetType)
|
||||
+ numSequenceNumberBytesForType(packetType)
|
||||
+ NUM_STATIC_HEADER_BYTES;
|
||||
}
|
||||
|
||||
int numBytesForPacketHeaderGivenPacketType(PacketType type) {
|
||||
return (int) ceilf((float)type / 255)
|
||||
+ numHashBytesInPacketHeaderGivenPacketType(type)
|
||||
+ NUM_STATIC_HEADER_BYTES;
|
||||
int numHashBytesForType(PacketType packetType) {
|
||||
return (NON_VERIFIED_PACKETS.contains(packetType) ? 0 : NUM_BYTES_MD5_HASH);
|
||||
}
|
||||
|
||||
int numHashBytesInPacketHeaderGivenPacketType(PacketType type) {
|
||||
return (NON_VERIFIED_PACKETS.contains(type) ? 0 : NUM_BYTES_MD5_HASH);
|
||||
int numSequenceNumberBytesForType(PacketType packetType) {
|
||||
return (SEQUENCE_NUMBERED_PACKETS.contains(packetType) ? sizeof(PacketSequenceNumber) : 0);
|
||||
}
|
||||
|
||||
QUuid uuidFromPacketHeader(const QByteArray& packet) {
|
||||
|
@ -204,8 +212,16 @@ QUuid uuidFromPacketHeader(const QByteArray& packet) {
|
|||
NUM_BYTES_RFC4122_UUID));
|
||||
}
|
||||
|
||||
int hashOffsetForPacketType(PacketType packetType) {
|
||||
return numBytesForArithmeticCodedPacketType(packetType) + NUM_STATIC_HEADER_BYTES;
|
||||
}
|
||||
|
||||
int sequenceNumberOffsetForPacketType(PacketType packetType) {
|
||||
return numBytesForPacketHeaderGivenPacketType(packetType) - sizeof(PacketSequenceNumber);
|
||||
}
|
||||
|
||||
QByteArray hashFromPacketHeader(const QByteArray& packet) {
|
||||
return packet.mid(numBytesForPacketHeader(packet) - NUM_BYTES_MD5_HASH, NUM_BYTES_MD5_HASH);
|
||||
return packet.mid(hashOffsetForPacketType(packetTypeForPacket(packet)), NUM_BYTES_MD5_HASH);
|
||||
}
|
||||
|
||||
QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid& connectionUUID) {
|
||||
|
@ -213,11 +229,48 @@ QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid&
|
|||
QCryptographicHash::Md5);
|
||||
}
|
||||
|
||||
void replaceHashInPacketGivenConnectionUUID(QByteArray& packet, const QUuid& connectionUUID) {
|
||||
packet.replace(numBytesForPacketHeader(packet) - NUM_BYTES_MD5_HASH, NUM_BYTES_MD5_HASH,
|
||||
PacketSequenceNumber sequenceNumberFromHeader(const QByteArray& packet, PacketType packetType) {
|
||||
if (packetType == PacketTypeUnknown) {
|
||||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
PacketSequenceNumber result = DEFAULT_SEQUENCE_NUMBER;
|
||||
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
memcpy(&result, packet.data() + sequenceNumberOffsetForPacketType(packetType), sizeof(PacketSequenceNumber));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void replaceHashInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketType packetType) {
|
||||
if (packetType == PacketTypeUnknown) {
|
||||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
packet.replace(hashOffsetForPacketType(packetType), NUM_BYTES_MD5_HASH,
|
||||
hashForPacketAndConnectionUUID(packet, connectionUUID));
|
||||
}
|
||||
|
||||
void replaceSequenceNumberInPacket(QByteArray& packet, PacketSequenceNumber sequenceNumber, PacketType packetType) {
|
||||
if (packetType == PacketTypeUnknown) {
|
||||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
packet.replace(sequenceNumberOffsetForPacketType(packetType),
|
||||
sizeof(PacketSequenceNumber), reinterpret_cast<char*>(&sequenceNumber), sizeof(PacketSequenceNumber));
|
||||
}
|
||||
|
||||
void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber,
|
||||
PacketType packetType) {
|
||||
if (packetType == PacketTypeUnknown) {
|
||||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
replaceHashInPacket(packet, connectionUUID, packetType);
|
||||
replaceSequenceNumberInPacket(packet, sequenceNumber, packetType);
|
||||
}
|
||||
|
||||
PacketType packetTypeForPacket(const QByteArray& packet) {
|
||||
return (PacketType) arithmeticCodingValueFromBuffer(packet.data());
|
||||
}
|
||||
|
|
|
@ -12,14 +12,19 @@
|
|||
#ifndef hifi_PacketHeaders_h
|
||||
#define hifi_PacketHeaders_h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
|
||||
#include <QtCore/QCryptographicHash>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "UUID.h"
|
||||
|
||||
// NOTE: if adding a new packet type, you can replace one marked usable or add at the end
|
||||
// NOTE: if you want the name of the packet type to be available for debugging or logging, update nameForPacketType() as well
|
||||
// NOTE: if adding a new packet packetType, you can replace one marked usable or add at the end
|
||||
// NOTE: if you want the name of the packet packetType to be available for debugging or logging, update nameForPacketType() as well
|
||||
enum PacketType {
|
||||
PacketTypeUnknown, // 0
|
||||
PacketTypeStunResponse,
|
||||
|
@ -79,6 +84,11 @@ enum PacketType {
|
|||
|
||||
typedef char PacketVersion;
|
||||
|
||||
typedef uint16_t PacketSequenceNumber;
|
||||
const PacketSequenceNumber DEFAULT_SEQUENCE_NUMBER = 0;
|
||||
|
||||
typedef std::map<PacketType, PacketSequenceNumber> PacketTypeSequenceMap;
|
||||
|
||||
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||
<< PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest
|
||||
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied
|
||||
|
@ -88,33 +98,53 @@ const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
|||
<< PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse
|
||||
<< PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode;
|
||||
|
||||
const QSet<PacketType> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType>()
|
||||
<< PacketTypeAvatarData;
|
||||
|
||||
const int NUM_BYTES_MD5_HASH = 16;
|
||||
const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
|
||||
const int MAX_PACKET_HEADER_BYTES = sizeof(PacketType) + NUM_BYTES_MD5_HASH + NUM_STATIC_HEADER_BYTES;
|
||||
|
||||
PacketVersion versionForPacketType(PacketType type);
|
||||
QString nameForPacketType(PacketType type);
|
||||
PacketType packetTypeForPacket(const QByteArray& packet);
|
||||
PacketType packetTypeForPacket(const char* packet);
|
||||
|
||||
PacketVersion versionForPacketType(PacketType packetType);
|
||||
QString nameForPacketType(PacketType packetType);
|
||||
|
||||
const QUuid nullUUID = QUuid();
|
||||
|
||||
QByteArray byteArrayWithPopulatedHeader(PacketType type, const QUuid& connectionUUID = nullUUID);
|
||||
int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID = nullUUID);
|
||||
int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionUUID = nullUUID);
|
||||
QByteArray byteArrayWithUUIDPopulatedHeader(PacketType packetType, const QUuid& connectionUUID);
|
||||
int populatePacketHeaderWithUUID(QByteArray& packet, PacketType packetType, const QUuid& connectionUUID);
|
||||
int populatePacketHeaderWithUUID(char* packet, PacketType packetType, const QUuid& connectionUUID);
|
||||
|
||||
int numHashBytesInPacketHeaderGivenPacketType(PacketType type);
|
||||
int numHashBytesForType(PacketType packetType);
|
||||
int numSequenceNumberBytesForType(PacketType packetType);
|
||||
|
||||
int numBytesForPacketHeader(const QByteArray& packet);
|
||||
int numBytesForPacketHeader(const char* packet);
|
||||
int numBytesForPacketHeaderGivenPacketType(PacketType type);
|
||||
int numBytesForArithmeticCodedPacketType(PacketType packetType);
|
||||
int numBytesForPacketHeaderGivenPacketType(PacketType packetType);
|
||||
|
||||
QUuid uuidFromPacketHeader(const QByteArray& packet);
|
||||
|
||||
int hashOffsetForPacketType(PacketType packetType);
|
||||
int sequenceNumberOffsetForPacketType(PacketType packetType);
|
||||
|
||||
QByteArray hashFromPacketHeader(const QByteArray& packet);
|
||||
QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid& connectionUUID);
|
||||
void replaceHashInPacketGivenConnectionUUID(QByteArray& packet, const QUuid& connectionUUID);
|
||||
|
||||
PacketType packetTypeForPacket(const QByteArray& packet);
|
||||
PacketType packetTypeForPacket(const char* packet);
|
||||
// NOTE: The following four methods accept a PacketType which defaults to PacketTypeUnknown.
|
||||
// If the caller has already looked at the packet type and can provide it then the methods below won't have to look it up.
|
||||
|
||||
PacketSequenceNumber sequenceNumberFromHeader(const QByteArray& packet, PacketType packetType = PacketTypeUnknown);
|
||||
|
||||
void replaceHashInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketType packetType = PacketTypeUnknown);
|
||||
|
||||
void replaceSequenceNumberInPacket(QByteArray& packet, PacketSequenceNumber sequenceNumber,
|
||||
PacketType packetType = PacketTypeUnknown);
|
||||
|
||||
void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber,
|
||||
PacketType packetType = PacketTypeUnknown);
|
||||
|
||||
int arithmeticCodingValueFromBuffer(const char* checkValue);
|
||||
int numBytesArithmeticCodingFromBuffer(const char* checkValue);
|
||||
|
@ -142,6 +172,7 @@ const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_DYNAMIC_SHAPE = 18;
|
|||
const PacketVersion VERSION_ENTITIES_HAVE_NAMES = 19;
|
||||
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_ATMOSPHERE = 20;
|
||||
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_SKYBOX = 21;
|
||||
const PacketVersion VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES = 22;
|
||||
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_STAGE_HAS_AUTOMATIC_HOURDAY = 22;
|
||||
const PacketVersion VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES = 23;
|
||||
|
||||
#endif // hifi_PacketHeaders_h
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "NetworkLogging.h"
|
||||
|
||||
#include "RSAKeypairGenerator.h"
|
||||
#ifndef __GNUC__
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
|
|
|
@ -241,7 +241,7 @@ float Resource::getLoadPriority() {
|
|||
}
|
||||
|
||||
void Resource::refresh() {
|
||||
if (_reply == nullptr && !(_loaded || _failedToLoad)) {
|
||||
if (_reply && !(_loaded || _failedToLoad)) {
|
||||
return;
|
||||
}
|
||||
if (_reply) {
|
||||
|
@ -350,7 +350,8 @@ void Resource::maybeRefresh() {
|
|||
QDateTime lastModified = variant.value<QDateTime>();
|
||||
QDateTime lastModifiedOld = metaData.lastModified();
|
||||
if (lastModified.isValid() && lastModifiedOld.isValid() &&
|
||||
lastModifiedOld == lastModified) {
|
||||
lastModifiedOld >= lastModified) { // With >=, cache won't thrash in eventually-consistent cdn.
|
||||
qCDebug(networking) << "Using cached version of" << _url.fileName();
|
||||
// We don't need to update, return
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -27,40 +27,47 @@ ThreadedAssignment::ThreadedAssignment(const QByteArray& packet) :
|
|||
}
|
||||
|
||||
void ThreadedAssignment::setFinished(bool isFinished) {
|
||||
_isFinished = isFinished;
|
||||
if (_isFinished != isFinished) {
|
||||
_isFinished = isFinished;
|
||||
|
||||
if (_isFinished) {
|
||||
if (_domainServerTimer) {
|
||||
_domainServerTimer->stop();
|
||||
delete _domainServerTimer;
|
||||
_domainServerTimer = nullptr;
|
||||
}
|
||||
if (_statsTimer) {
|
||||
_statsTimer->stop();
|
||||
delete _statsTimer;
|
||||
_statsTimer = nullptr;
|
||||
}
|
||||
if (_isFinished) {
|
||||
|
||||
aboutToFinish();
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// if we have a datagram processing thread, quit it and wait on it to make sure that
|
||||
// the node socket is back on the same thread as the NodeList
|
||||
|
||||
if (_datagramProcessingThread) {
|
||||
// tell the datagram processing thread to quit and wait until it is done, then return the node socket to the NodeList
|
||||
_datagramProcessingThread->quit();
|
||||
_datagramProcessingThread->wait();
|
||||
qDebug() << "ThreadedAssignment::setFinished(true) called - finishing up.";
|
||||
|
||||
if (_domainServerTimer) {
|
||||
_domainServerTimer->stop();
|
||||
}
|
||||
|
||||
if (_statsTimer) {
|
||||
_statsTimer->stop();
|
||||
}
|
||||
|
||||
// set node socket parent back to NodeList
|
||||
nodeList->getNodeSocket().setParent(nodeList.data());
|
||||
// stop processing datagrams from the node socket
|
||||
// this ensures we won't process a domain list while we are going down
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
|
||||
|
||||
// call our virtual aboutToFinish method - this gives the ThreadedAssignment subclass a chance to cleanup
|
||||
aboutToFinish();
|
||||
|
||||
// if we have a datagram processing thread, quit it and wait on it to make sure that
|
||||
// the node socket is back on the same thread as the NodeList
|
||||
|
||||
if (_datagramProcessingThread) {
|
||||
// tell the datagram processing thread to quit and wait until it is done,
|
||||
// then return the node socket to the NodeList
|
||||
_datagramProcessingThread->quit();
|
||||
_datagramProcessingThread->wait();
|
||||
|
||||
// set node socket parent back to NodeList
|
||||
nodeList->getNodeSocket().setParent(nodeList.data());
|
||||
}
|
||||
|
||||
// move the NodeList back to the QCoreApplication instance's thread
|
||||
nodeList->moveToThread(QCoreApplication::instance()->thread());
|
||||
|
||||
emit finished();
|
||||
}
|
||||
|
||||
// move the NodeList back to the QCoreApplication instance's thread
|
||||
nodeList->moveToThread(QCoreApplication::instance()->thread());
|
||||
|
||||
emit finished();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ class ThreadedAssignment : public Assignment {
|
|||
Q_OBJECT
|
||||
public:
|
||||
ThreadedAssignment(const QByteArray& packet);
|
||||
~ThreadedAssignment() { stop(); }
|
||||
|
||||
void setFinished(bool isFinished);
|
||||
virtual void aboutToFinish() { };
|
||||
|
@ -32,11 +33,6 @@ public slots:
|
|||
virtual void readPendingDatagrams() = 0;
|
||||
virtual void sendStatsPacket();
|
||||
|
||||
public slots:
|
||||
virtual void aboutToQuit() {
|
||||
QMetaObject::invokeMethod(this, "stop");
|
||||
}
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
|
|
|
@ -35,10 +35,13 @@ void JurisdictionListener::nodeKilled(SharedNodePointer node) {
|
|||
bool JurisdictionListener::queueJurisdictionRequest() {
|
||||
static unsigned char buffer[MAX_PACKET_SIZE];
|
||||
unsigned char* bufferOut = &buffer[0];
|
||||
int sizeOut = populatePacketHeader(reinterpret_cast<char*>(bufferOut), PacketTypeJurisdictionRequest);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
int sizeOut = nodeList->populatePacketHeader(reinterpret_cast<char*>(bufferOut), PacketTypeJurisdictionRequest);
|
||||
int nodeCount = 0;
|
||||
|
||||
DependencyManager::get<NodeList>()->eachNode([&](const SharedNodePointer& node) {
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
if (node->getType() == getNodeType() && node->getActiveSocket()) {
|
||||
_packetSender.queuePacketForSending(node, QByteArray(reinterpret_cast<char*>(bufferOut), sizeOut));
|
||||
nodeCount++;
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <OctalCode.h>
|
||||
|
||||
|
@ -268,8 +270,9 @@ bool JurisdictionMap::writeToFile(const char* filename) {
|
|||
|
||||
int JurisdictionMap::packEmptyJurisdictionIntoMessage(NodeType_t type, unsigned char* destinationBuffer, int availableBytes) {
|
||||
unsigned char* bufferStart = destinationBuffer;
|
||||
|
||||
int headerLength = populatePacketHeader(reinterpret_cast<char*>(destinationBuffer), PacketTypeJurisdiction);
|
||||
|
||||
int headerLength = DependencyManager::get<NodeList>()->populatePacketHeader(reinterpret_cast<char*>(destinationBuffer),
|
||||
PacketTypeJurisdiction);
|
||||
destinationBuffer += headerLength;
|
||||
|
||||
// Pack the Node Type in first byte
|
||||
|
@ -287,7 +290,8 @@ int JurisdictionMap::packEmptyJurisdictionIntoMessage(NodeType_t type, unsigned
|
|||
int JurisdictionMap::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) {
|
||||
unsigned char* bufferStart = destinationBuffer;
|
||||
|
||||
int headerLength = populatePacketHeader(reinterpret_cast<char*>(destinationBuffer), PacketTypeJurisdiction);
|
||||
int headerLength = DependencyManager::get<NodeList>()->populatePacketHeader(reinterpret_cast<char*>(destinationBuffer),
|
||||
PacketTypeJurisdiction);
|
||||
destinationBuffer += headerLength;
|
||||
|
||||
// Pack the Node Type in first byte
|
||||
|
|
|
@ -328,7 +328,8 @@ void OctreeEditPacketSender::releaseQueuedPacket(EditPacketBuffer& packetBuffer)
|
|||
}
|
||||
|
||||
void OctreeEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, PacketType type, int nodeClockSkew) {
|
||||
packetBuffer._currentSize = populatePacketHeader(reinterpret_cast<char*>(&packetBuffer._currentBuffer[0]), type);
|
||||
packetBuffer._currentSize =
|
||||
DependencyManager::get<NodeList>()->populatePacketHeader(reinterpret_cast<char*>(&packetBuffer._currentBuffer[0]), type);
|
||||
|
||||
// skip over sequence number for now; will be packed when packet is ready to be sent out
|
||||
packetBuffer._currentSize += sizeof(quint16);
|
||||
|
|
|
@ -186,7 +186,7 @@ void OctreeHeadlessViewer::queryOctree() {
|
|||
}
|
||||
|
||||
if (inView) {
|
||||
_octreeQuery.setMaxOctreePacketsPerSecond(perServerPPS);
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perServerPPS);
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "inView for node " << *node << ", give it budget of " << perServerPPS;
|
||||
}
|
||||
|
@ -213,15 +213,15 @@ void OctreeHeadlessViewer::queryOctree() {
|
|||
qCDebug(octree) << "Using regular camera position for node" << *node;
|
||||
}
|
||||
}
|
||||
_octreeQuery.setMaxOctreePacketsPerSecond(perUnknownServer);
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perUnknownServer);
|
||||
} else {
|
||||
_octreeQuery.setMaxOctreePacketsPerSecond(0);
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(0);
|
||||
}
|
||||
// set up the packet for sending...
|
||||
unsigned char* endOfQueryPacket = queryPacket;
|
||||
|
||||
// insert packet type/version and node UUID
|
||||
endOfQueryPacket += populatePacketHeader(reinterpret_cast<char*>(endOfQueryPacket), packetType);
|
||||
endOfQueryPacket += nodeList->populatePacketHeader(reinterpret_cast<char*>(endOfQueryPacket), packetType);
|
||||
|
||||
// encode the query data...
|
||||
endOfQueryPacket += _octreeQuery.getBroadcastData(endOfQueryPacket);
|
||||
|
|
|
@ -11,25 +11,15 @@
|
|||
|
||||
#include <GLMHelpers.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SettingHandle.h>
|
||||
|
||||
#include "OctreeConstants.h"
|
||||
#include "OctreeQuery.h"
|
||||
|
||||
Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPSSpin", DEFAULT_MAX_OCTREE_PPS);
|
||||
|
||||
OctreeQuery::OctreeQuery() {
|
||||
_maxOctreePPS = maxOctreePacketsPerSecond.get();
|
||||
_maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
|
||||
}
|
||||
|
||||
void OctreeQuery::setMaxOctreePacketsPerSecond(int maxOctreePPS) {
|
||||
if (maxOctreePPS != _maxOctreePPS) {
|
||||
_maxOctreePPS = maxOctreePPS;
|
||||
maxOctreePacketsPerSecond.set(_maxOctreePPS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
|
||||
unsigned char* bufferStart = destinationBuffer;
|
||||
|
||||
|
@ -59,8 +49,8 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
|
|||
*destinationBuffer++ = bitItems;
|
||||
|
||||
// desired Max Octree PPS
|
||||
memcpy(destinationBuffer, &_maxOctreePPS, sizeof(_maxOctreePPS));
|
||||
destinationBuffer += sizeof(_maxOctreePPS);
|
||||
memcpy(destinationBuffer, &_maxQueryPPS, sizeof(_maxQueryPPS));
|
||||
destinationBuffer += sizeof(_maxQueryPPS);
|
||||
|
||||
// desired voxelSizeScale
|
||||
memcpy(destinationBuffer, &_octreeElementSizeScale, sizeof(_octreeElementSizeScale));
|
||||
|
@ -103,8 +93,8 @@ int OctreeQuery::parseData(const QByteArray& packet) {
|
|||
_wantCompression = oneAtBit(bitItems, WANT_COMPRESSION);
|
||||
|
||||
// desired Max Octree PPS
|
||||
memcpy(&_maxOctreePPS, sourceBuffer, sizeof(_maxOctreePPS));
|
||||
sourceBuffer += sizeof(_maxOctreePPS);
|
||||
memcpy(&_maxQueryPPS, sourceBuffer, sizeof(_maxQueryPPS));
|
||||
sourceBuffer += sizeof(_maxQueryPPS);
|
||||
|
||||
// desired _octreeElementSizeScale
|
||||
memcpy(&_octreeElementSizeScale, sourceBuffer, sizeof(_octreeElementSizeScale));
|
||||
|
|
|
@ -76,7 +76,7 @@ public:
|
|||
bool getWantLowResMoving() const { return _wantLowResMoving; }
|
||||
bool getWantOcclusionCulling() const { return _wantOcclusionCulling; }
|
||||
bool getWantCompression() const { return _wantCompression; }
|
||||
int getMaxOctreePacketsPerSecond() const { return _maxOctreePPS; }
|
||||
int getMaxQueryPacketsPerSecond() const { return _maxQueryPPS; }
|
||||
float getOctreeSizeScale() const { return _octreeElementSizeScale; }
|
||||
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||
|
||||
|
@ -86,7 +86,7 @@ public slots:
|
|||
void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; }
|
||||
void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; }
|
||||
void setWantCompression(bool wantCompression) { _wantCompression = wantCompression; }
|
||||
void setMaxOctreePacketsPerSecond(int maxOctreePPS);
|
||||
void setMaxQueryPacketsPerSecond(int maxQueryPPS) { _maxQueryPPS = maxQueryPPS; }
|
||||
void setOctreeSizeScale(float octreeSizeScale) { _octreeElementSizeScale = octreeSizeScale; }
|
||||
void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
|
||||
|
||||
|
@ -106,7 +106,7 @@ protected:
|
|||
bool _wantLowResMoving = true;
|
||||
bool _wantOcclusionCulling = false;
|
||||
bool _wantCompression = false;
|
||||
int _maxOctreePPS = DEFAULT_MAX_OCTREE_PPS;
|
||||
int _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
|
||||
float _octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE; /// used for LOD calculations
|
||||
int _boundaryLevelAdjust = 0; /// used for LOD calculations
|
||||
|
||||
|
|
|
@ -385,7 +385,8 @@ void OctreeSceneStats::childBitsRemoved(bool includesExistsBits, bool includesCo
|
|||
int OctreeSceneStats::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) {
|
||||
unsigned char* bufferStart = destinationBuffer;
|
||||
|
||||
int headerLength = populatePacketHeader(reinterpret_cast<char*>(destinationBuffer), PacketTypeOctreeStats);
|
||||
int headerLength = DependencyManager::get<NodeList>()->populatePacketHeader(reinterpret_cast<char*>(destinationBuffer),
|
||||
PacketTypeOctreeStats);
|
||||
destinationBuffer += headerLength;
|
||||
|
||||
memcpy(destinationBuffer, &_start, sizeof(_start));
|
||||
|
|
|
@ -13,39 +13,50 @@
|
|||
|
||||
<@include DeferredLighting.slh@>
|
||||
|
||||
uniform samplerCube skyboxMap;
|
||||
|
||||
struct SphericalHarmonics {
|
||||
vec4 L00;
|
||||
vec4 L1m1;
|
||||
vec4 L10;
|
||||
vec4 L11;
|
||||
vec4 L2m2;
|
||||
vec4 L2m1;
|
||||
vec4 L20;
|
||||
vec4 L21;
|
||||
vec4 L22;
|
||||
vec4 evalSkyboxLight(vec3 direction, float lod) {
|
||||
|
||||
<@if GPU_TRANSFORM_PROFILE == GPU_CORE@>
|
||||
vec4 skytexel = textureCubeLod(skyboxMap, direction, lod * textureQueryLevels(skyboxMap));
|
||||
<@else@>
|
||||
vec4 skytexel = textureCube(skyboxMap, direction);
|
||||
<@endif@>
|
||||
return skytexel;
|
||||
}
|
||||
|
||||
struct SphericalHarmonics {
|
||||
vec4 L00;
|
||||
vec4 L1m1;
|
||||
vec4 L10;
|
||||
vec4 L11;
|
||||
vec4 L2m2;
|
||||
vec4 L2m1;
|
||||
vec4 L20;
|
||||
vec4 L21;
|
||||
vec4 L22;
|
||||
};
|
||||
|
||||
vec4 evalSphericalLight(SphericalHarmonics sh, vec3 direction ) {
|
||||
|
||||
vec3 dir = direction.xzy; // we don;t understand why yet but we need to use z as vertical axis?
|
||||
|
||||
const float C1 = 0.429043;
|
||||
const float C2 = 0.511664;
|
||||
const float C3 = 0.743125;
|
||||
const float C4 = 0.886227;
|
||||
const float C5 = 0.247708;
|
||||
|
||||
vec4 value = C1 * sh.L22 * (dir.x * dir.x - dir.y * dir.y) +
|
||||
C3 * sh.L20 * dir.z * dir.z +
|
||||
C4 * sh.L00 - C5 * sh.L20 +
|
||||
2.0 * C1 * ( sh.L2m2 * dir.x * dir.y +
|
||||
sh.L21 * dir.x * dir.z +
|
||||
sh.L2m1 * dir.y * dir.z ) +
|
||||
2.0 * C2 * ( sh.L11 * dir.x +
|
||||
sh.L1m1 * dir.y +
|
||||
sh.L10 * dir.z ) ;
|
||||
return value;
|
||||
|
||||
vec4 evalSphericalLight(SphericalHarmonics sh, vec3 direction ) {
|
||||
|
||||
vec3 dir = direction.xzy; // we don;t understand why yet but we need to use z as vertical axis?
|
||||
|
||||
const float C1 = 0.429043;
|
||||
const float C2 = 0.511664;
|
||||
const float C3 = 0.743125;
|
||||
const float C4 = 0.886227;
|
||||
const float C5 = 0.247708;
|
||||
|
||||
vec4 value = C1 * sh.L22 * (dir.x * dir.x - dir.y * dir.y) +
|
||||
C3 * sh.L20 * dir.z * dir.z +
|
||||
C4 * sh.L00 - C5 * sh.L20 +
|
||||
2.0 * C1 * ( sh.L2m2 * dir.x * dir.y +
|
||||
sh.L21 * dir.x * dir.z +
|
||||
sh.L2m1 * dir.y * dir.z ) +
|
||||
2.0 * C2 * ( sh.L11 * dir.x +
|
||||
sh.L1m1 * dir.y +
|
||||
sh.L10 * dir.z ) ;
|
||||
return value;
|
||||
}
|
||||
|
||||
// Need one SH
|
||||
|
@ -92,6 +103,23 @@ vec3 evalAmbienSphereGlobalColor(float shadowAttenuation, vec3 position, vec3 no
|
|||
return color;
|
||||
}
|
||||
|
||||
vec3 evalSkyboxGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 diffuse, vec3 specular, float gloss) {
|
||||
// Need the light now
|
||||
Light light = getLight();
|
||||
|
||||
vec3 fragNormal = normalize(vec3(invViewMat * vec4(normal, 0.0)));
|
||||
vec4 fragEyeVector = invViewMat * vec4(-position, 0.0);
|
||||
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
|
||||
|
||||
vec3 color = diffuse.rgb * evalSkyboxLight(fragNormal, 0.75).xyz * getLightAmbientIntensity(light);
|
||||
|
||||
vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss);
|
||||
|
||||
color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 evalLightmappedColor(float shadowAttenuation, vec3 normal, vec3 diffuse, vec3 lightmap) {
|
||||
|
||||
Light light = getLight();
|
||||
|
|
|
@ -41,6 +41,10 @@
|
|||
#include "directional_ambient_light_shadow_map_frag.h"
|
||||
#include "directional_ambient_light_cascaded_shadow_map_frag.h"
|
||||
|
||||
#include "directional_skybox_light_frag.h"
|
||||
#include "directional_skybox_light_shadow_map_frag.h"
|
||||
#include "directional_skybox_light_cascaded_shadow_map_frag.h"
|
||||
|
||||
#include "point_light_frag.h"
|
||||
#include "spot_light_frag.h"
|
||||
|
||||
|
@ -66,6 +70,12 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
|
|||
loadLightProgram(directional_ambient_light_cascaded_shadow_map_frag, false, _directionalAmbientSphereLightCascadedShadowMap,
|
||||
_directionalAmbientSphereLightCascadedShadowMapLocations);
|
||||
|
||||
loadLightProgram(directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations);
|
||||
loadLightProgram(directional_skybox_light_shadow_map_frag, false, _directionalSkyboxLightShadowMap,
|
||||
_directionalSkyboxLightShadowMapLocations);
|
||||
loadLightProgram(directional_skybox_light_cascaded_shadow_map_frag, false, _directionalSkyboxLightCascadedShadowMap,
|
||||
_directionalSkyboxLightCascadedShadowMapLocations);
|
||||
|
||||
loadLightProgram(point_light_frag, true, _pointLight, _pointLightLocations);
|
||||
loadLightProgram(spot_light_frag, true, _spotLight, _spotLightLocations);
|
||||
|
||||
|
@ -217,6 +227,7 @@ void DeferredLightingEffect::render() {
|
|||
float tMin = viewport[VIEWPORT_Y_INDEX] / (float)framebufferSize.height();
|
||||
float tHeight = viewport[VIEWPORT_HEIGHT_INDEX] / (float)framebufferSize.height();
|
||||
|
||||
bool useSkyboxCubemap = (_skybox) && (_skybox->getCubemap());
|
||||
|
||||
// Fetch the ViewMatrix;
|
||||
glm::mat4 invViewMat;
|
||||
|
@ -234,7 +245,10 @@ void DeferredLightingEffect::render() {
|
|||
if (_viewState->getCascadeShadowsEnabled()) {
|
||||
program = &_directionalLightCascadedShadowMap;
|
||||
locations = &_directionalLightCascadedShadowMapLocations;
|
||||
if (_ambientLightMode > -1) {
|
||||
if (useSkyboxCubemap) {
|
||||
program = &_directionalSkyboxLightCascadedShadowMap;
|
||||
locations = &_directionalSkyboxLightCascadedShadowMapLocations;
|
||||
} else if (_ambientLightMode > -1) {
|
||||
program = &_directionalAmbientSphereLightCascadedShadowMap;
|
||||
locations = &_directionalAmbientSphereLightCascadedShadowMapLocations;
|
||||
}
|
||||
|
@ -242,7 +256,10 @@ void DeferredLightingEffect::render() {
|
|||
program->setUniform(locations->shadowDistances, _viewState->getShadowDistances());
|
||||
|
||||
} else {
|
||||
if (_ambientLightMode > -1) {
|
||||
if (useSkyboxCubemap) {
|
||||
program = &_directionalSkyboxLightShadowMap;
|
||||
locations = &_directionalSkyboxLightShadowMapLocations;
|
||||
} else if (_ambientLightMode > -1) {
|
||||
program = &_directionalAmbientSphereLightShadowMap;
|
||||
locations = &_directionalAmbientSphereLightShadowMapLocations;
|
||||
}
|
||||
|
@ -252,7 +269,10 @@ void DeferredLightingEffect::render() {
|
|||
1.0f / textureCache->getShadowFramebuffer()->getWidth());
|
||||
|
||||
} else {
|
||||
if (_ambientLightMode > -1) {
|
||||
if (useSkyboxCubemap) {
|
||||
program = &_directionalSkyboxLight;
|
||||
locations = &_directionalSkyboxLightLocations;
|
||||
} else if (_ambientLightMode > -1) {
|
||||
program = &_directionalAmbientSphereLight;
|
||||
locations = &_directionalAmbientSphereLightLocations;
|
||||
}
|
||||
|
@ -269,6 +289,11 @@ void DeferredLightingEffect::render() {
|
|||
}
|
||||
}
|
||||
|
||||
if (useSkyboxCubemap) {
|
||||
glActiveTexture(GL_TEXTURE5);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, gpu::GLBackend::getTextureID(_skybox->getCubemap()));
|
||||
}
|
||||
|
||||
if (locations->lightBufferUnit >= 0) {
|
||||
gpu::Batch batch;
|
||||
batch.setUniformBuffer(locations->lightBufferUnit, globalLight->getSchemaBuffer());
|
||||
|
@ -300,7 +325,14 @@ void DeferredLightingEffect::render() {
|
|||
renderFullscreenQuad(sMin, sMin + sWidth, tMin, tMin + tHeight);
|
||||
|
||||
program->release();
|
||||
|
||||
|
||||
if (useSkyboxCubemap) {
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||
if (!shadowsEnabled) {
|
||||
glActiveTexture(GL_TEXTURE3);
|
||||
}
|
||||
}
|
||||
|
||||
if (shadowsEnabled) {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE3);
|
||||
|
@ -492,6 +524,7 @@ void DeferredLightingEffect::loadLightProgram(const char* fragSource, bool limit
|
|||
program.setUniformValue("specularMap", 2);
|
||||
program.setUniformValue("depthMap", 3);
|
||||
program.setUniformValue("shadowMap", 4);
|
||||
program.setUniformValue("skyboxMap", 5);
|
||||
locations.shadowDistances = program.uniformLocation("shadowDistances");
|
||||
locations.shadowScale = program.uniformLocation("shadowScale");
|
||||
locations.nearLocation = program.uniformLocation("near");
|
||||
|
|
|
@ -101,6 +101,13 @@ private:
|
|||
ProgramObject _simpleProgram;
|
||||
int _glowIntensityLocation;
|
||||
|
||||
ProgramObject _directionalSkyboxLight;
|
||||
LightLocations _directionalSkyboxLightLocations;
|
||||
ProgramObject _directionalSkyboxLightShadowMap;
|
||||
LightLocations _directionalSkyboxLightShadowMapLocations;
|
||||
ProgramObject _directionalSkyboxLightCascadedShadowMap;
|
||||
LightLocations _directionalSkyboxLightCascadedShadowMapLocations;
|
||||
|
||||
ProgramObject _directionalAmbientSphereLight;
|
||||
LightLocations _directionalAmbientSphereLightLocations;
|
||||
ProgramObject _directionalAmbientSphereLightShadowMap;
|
||||
|
|
|
@ -2068,6 +2068,12 @@ void Model::segregateMeshGroups() {
|
|||
bool hasSpecular = mesh.hasSpecularTexture();
|
||||
bool hasLightmap = mesh.hasEmissiveTexture();
|
||||
bool isSkinned = state.clusterMatrices.size() > 1;
|
||||
bool wireframe = isWireframe();
|
||||
|
||||
if (wireframe) {
|
||||
translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false;
|
||||
}
|
||||
|
||||
QString materialID;
|
||||
|
||||
// create a material name from all the parts. If there's one part, this will be a single material and its
|
||||
|
@ -2085,7 +2091,7 @@ void Model::segregateMeshGroups() {
|
|||
qCDebug(renderutils) << "materialID:" << materialID << "parts:" << mesh.parts.size();
|
||||
}
|
||||
|
||||
RenderKey key(translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe());
|
||||
RenderKey key(translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe);
|
||||
|
||||
// reuse or create the bucket corresponding to that key and insert the mesh as unsorted
|
||||
_renderBuckets[key.getRaw()]._unsortedMeshes.insertMulti(materialID, i);
|
||||
|
|
|
@ -513,7 +513,7 @@ void NetworkTexture::setImage(const QImage& image, bool translucent, const QColo
|
|||
imageLoaded(image);
|
||||
|
||||
if ((_width > 0) && (_height > 0)) {
|
||||
|
||||
|
||||
bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
|
||||
|
||||
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));
|
||||
|
@ -524,10 +524,94 @@ void NetworkTexture::setImage(const QImage& image, bool translucent, const QColo
|
|||
}
|
||||
|
||||
if (_type == CUBE_TEXTURE) {
|
||||
if (_height >= (6 * _width)) {
|
||||
_gpuTexture = gpu::TexturePointer(gpu::Texture::createCube(formatGPU, image.width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)));
|
||||
_gpuTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
|
||||
|
||||
std::vector<QImage> faces;
|
||||
if (_height == (6 * _width)) {
|
||||
int faceWidth = _width;
|
||||
|
||||
// Here is the expected layout for the faces in an image with the 1/6 aspect ratio:
|
||||
//
|
||||
// WIDTH
|
||||
// <------>
|
||||
// ^ +------+
|
||||
// | | |
|
||||
// | | +X |
|
||||
// | | |
|
||||
// H +------+
|
||||
// E | |
|
||||
// I | -X |
|
||||
// G | |
|
||||
// H +------+
|
||||
// T | |
|
||||
// | | +Y |
|
||||
// | | |
|
||||
// | +------+
|
||||
// | | |
|
||||
// | | -Y |
|
||||
// | | |
|
||||
// H +------+
|
||||
// E | |
|
||||
// I | +Z |
|
||||
// G | |
|
||||
// H +------+
|
||||
// T | |
|
||||
// | | -Z |
|
||||
// | | |
|
||||
// V +------+
|
||||
//
|
||||
// FaceWidth = width = height / 6
|
||||
|
||||
faces.push_back(image.copy(QRect(0, 0 * faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||
faces.push_back(image.copy(QRect(0, 1 * faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||
faces.push_back(image.copy(QRect(0, 2 * faceWidth, faceWidth, faceWidth)).mirrored(false, true));
|
||||
faces.push_back(image.copy(QRect(0, 3 * faceWidth, faceWidth, faceWidth)).mirrored(false, true));
|
||||
faces.push_back(image.copy(QRect(0, 4 * faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||
faces.push_back(image.copy(QRect(0, 5 * faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||
|
||||
} else if ((_height / 3) == (_width / 4)) {
|
||||
int faceWidth = _height / 3;
|
||||
|
||||
// Here is the expected layout for the faces in an image with the 3/4 aspect ratio:
|
||||
//
|
||||
// <-----------WIDTH----------->
|
||||
// ^ +------+------+------+------+
|
||||
// | | | | | |
|
||||
// | | | +Y | | |
|
||||
// | | | | | |
|
||||
// H +------+------+------+------+
|
||||
// E | | | | |
|
||||
// I | -X | -Z | +X | +Z |
|
||||
// G | | | | |
|
||||
// H +------+------+------+------+
|
||||
// T | | | | |
|
||||
// | | | -Y | | |
|
||||
// | | | | | |
|
||||
// V +------+------+------+------+
|
||||
//
|
||||
// FaceWidth = width / 4 = height / 3
|
||||
|
||||
// Right = +X
|
||||
faces.push_back(image.copy(QRect(2 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||
// Left = -X
|
||||
faces.push_back(image.copy(QRect(0 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||
// Top = +Y
|
||||
faces.push_back(image.copy(QRect(1 * faceWidth, 0, faceWidth, faceWidth)).mirrored(false, true));
|
||||
// Bottom = -Y
|
||||
faces.push_back(image.copy(QRect(1 * faceWidth, 2 * faceWidth, faceWidth, faceWidth)).mirrored(false, true));
|
||||
// Back = +Z
|
||||
faces.push_back(image.copy(QRect(3 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||
// Front = -Z
|
||||
faces.push_back(image.copy(QRect(1 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||
}
|
||||
|
||||
if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) {
|
||||
_gpuTexture = gpu::TexturePointer(gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)));
|
||||
_gpuTexture->autoGenerateMips(-1);
|
||||
int f = 0;
|
||||
for (auto& face : faces) {
|
||||
_gpuTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f);
|
||||
f++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_gpuTexture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
|
|
43
libraries/render-utils/src/directional_skybox_light.slf
Executable file
43
libraries/render-utils/src/directional_skybox_light.slf
Executable file
|
@ -0,0 +1,43 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// directional_light.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 5/8/2015.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// Everything about deferred buffer
|
||||
<@include DeferredBuffer.slh@>
|
||||
|
||||
<@include DeferredGlobalLight.slh@>
|
||||
|
||||
void main(void) {
|
||||
DeferredFragment frag = unpackDeferredFragment(gl_TexCoord[0].st);
|
||||
|
||||
|
||||
// Light mapped or not ?
|
||||
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
|
||||
vec3 color = evalLightmappedColor(
|
||||
1.0,
|
||||
frag.normal,
|
||||
frag.diffuse,
|
||||
frag.specularVal.xyz);
|
||||
|
||||
gl_FragColor = vec4(color, 1.0);
|
||||
} else {
|
||||
vec3 color = evalSkyboxGlobalColor(1.0,
|
||||
frag.position.xyz,
|
||||
frag.normal,
|
||||
frag.diffuse,
|
||||
frag.specular,
|
||||
frag.gloss);
|
||||
|
||||
gl_FragColor = vec4(color, frag.normalVal.a);
|
||||
}
|
||||
}
|
48
libraries/render-utils/src/directional_skybox_light_cascaded_shadow_map.slf
Executable file
48
libraries/render-utils/src/directional_skybox_light_cascaded_shadow_map.slf
Executable file
|
@ -0,0 +1,48 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// directional_light.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 5/8/2015.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// Everything about deferred buffer
|
||||
<@include DeferredBuffer.slh@>
|
||||
|
||||
<@include DeferredGlobalLight.slh@>
|
||||
|
||||
// Everything about shadow
|
||||
<@include Shadow.slh@>
|
||||
|
||||
void main(void) {
|
||||
DeferredFragment frag = unpackDeferredFragment(gl_TexCoord[0].st);
|
||||
|
||||
// Eval shadow Texcoord and then Attenuation
|
||||
vec4 shadowTexcoord = evalCascadedShadowTexcoord(frag.position);
|
||||
float shadowAttenuation = evalShadowAttenuation(shadowTexcoord);
|
||||
|
||||
// Light mapped or not ?
|
||||
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
|
||||
gl_FragColor = vec4(evalLightmappedColor(
|
||||
shadowAttenuation,
|
||||
frag.normal,
|
||||
frag.diffuse,
|
||||
frag.specularVal.xyz),
|
||||
1.0);
|
||||
} else {
|
||||
vec3 color = evalSkyboxGlobalColor(shadowAttenuation,
|
||||
frag.position.xyz,
|
||||
frag.normal,
|
||||
frag.diffuse,
|
||||
frag.specular,
|
||||
frag.gloss);
|
||||
|
||||
gl_FragColor = vec4(color, frag.normalVal.a);
|
||||
}
|
||||
}
|
49
libraries/render-utils/src/directional_skybox_light_shadow_map.slf
Executable file
49
libraries/render-utils/src/directional_skybox_light_shadow_map.slf
Executable file
|
@ -0,0 +1,49 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// directional_light.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 5/8/2015.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// Everything about deferred buffer
|
||||
<@include DeferredBuffer.slh@>
|
||||
|
||||
<@include DeferredGlobalLight.slh@>
|
||||
|
||||
// Everything about shadow
|
||||
<@include Shadow.slh@>
|
||||
|
||||
|
||||
void main(void) {
|
||||
DeferredFragment frag = unpackDeferredFragment(gl_TexCoord[0].st);
|
||||
|
||||
// Eval shadow Texcoord and then Attenuation
|
||||
vec4 shadowTexcoord = evalShadowTexcoord(frag.position);
|
||||
float shadowAttenuation = evalShadowAttenuation(shadowTexcoord);
|
||||
|
||||
// Light mapped or not ?
|
||||
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
|
||||
gl_FragColor = vec4(evalLightmappedColor(
|
||||
shadowAttenuation,
|
||||
frag.normal,
|
||||
frag.diffuse,
|
||||
frag.specularVal.xyz),
|
||||
1.0);
|
||||
} else {
|
||||
vec3 color = evalSkyboxGlobalColor(shadowAttenuation,
|
||||
frag.position.xyz,
|
||||
frag.normal,
|
||||
frag.diffuse,
|
||||
frag.specular,
|
||||
frag.gloss);
|
||||
|
||||
gl_FragColor = vec4(color, frag.normalVal.a);
|
||||
}
|
||||
}
|
|
@ -23,12 +23,13 @@ MenuItemProperties::MenuItemProperties() :
|
|||
beforeItem(""),
|
||||
afterItem(""),
|
||||
isCheckable(false),
|
||||
isChecked(false)
|
||||
isChecked(false),
|
||||
isSeparator(false)
|
||||
{
|
||||
};
|
||||
|
||||
MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& menuItemName,
|
||||
const QString& shortcutKey, bool checkable, bool checked) :
|
||||
const QString& shortcutKey, bool checkable, bool checked, bool separator) :
|
||||
menuName(menuName),
|
||||
menuItemName(menuItemName),
|
||||
shortcutKey(shortcutKey),
|
||||
|
@ -38,12 +39,13 @@ MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& m
|
|||
beforeItem(""),
|
||||
afterItem(""),
|
||||
isCheckable(checkable),
|
||||
isChecked(checked)
|
||||
isChecked(checked),
|
||||
isSeparator(separator)
|
||||
{
|
||||
}
|
||||
|
||||
MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& menuItemName,
|
||||
const KeyEvent& shortcutKeyEvent, bool checkable, bool checked) :
|
||||
const KeyEvent& shortcutKeyEvent, bool checkable, bool checked, bool separator) :
|
||||
menuName(menuName),
|
||||
menuItemName(menuItemName),
|
||||
shortcutKey(""),
|
||||
|
@ -53,7 +55,8 @@ MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& m
|
|||
beforeItem(""),
|
||||
afterItem(""),
|
||||
isCheckable(checkable),
|
||||
isChecked(checked)
|
||||
isChecked(checked),
|
||||
isSeparator(separator)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@ class MenuItemProperties {
|
|||
public:
|
||||
MenuItemProperties();
|
||||
MenuItemProperties(const QString& menuName, const QString& menuItemName,
|
||||
const QString& shortcutKey = QString(""), bool checkable = false, bool checked = false);
|
||||
const QString& shortcutKey = QString(""), bool checkable = false, bool checked = false, bool separator = false);
|
||||
MenuItemProperties(const QString& menuName, const QString& menuItemName,
|
||||
const KeyEvent& shortcutKeyEvent, bool checkable = false, bool checked = false);
|
||||
const KeyEvent& shortcutKeyEvent, bool checkable = false, bool checked = false, bool separator = false);
|
||||
|
||||
QString menuName;
|
||||
QString menuItemName;
|
||||
|
|
|
@ -493,7 +493,7 @@ void ScriptEngine::run() {
|
|||
/ (1000 * 1000)) + 0.5);
|
||||
const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t);
|
||||
|
||||
QByteArray avatarPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
|
||||
QByteArray avatarPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarData);
|
||||
avatarPacket.append(_avatarData->toByteArray());
|
||||
|
||||
nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer);
|
||||
|
@ -534,9 +534,9 @@ void ScriptEngine::run() {
|
|||
}
|
||||
}
|
||||
|
||||
QByteArray audioPacket = byteArrayWithPopulatedHeader(silentFrame
|
||||
? PacketTypeSilentAudioFrame
|
||||
: PacketTypeMicrophoneAudioNoEcho);
|
||||
QByteArray audioPacket = nodeList->byteArrayWithPopulatedHeader(silentFrame
|
||||
? PacketTypeSilentAudioFrame
|
||||
: PacketTypeMicrophoneAudioNoEcho);
|
||||
|
||||
QDataStream packetStream(&audioPacket, QIODevice::Append);
|
||||
|
||||
|
|
|
@ -32,6 +32,9 @@ void GenericThread::initialize(bool isThreaded) {
|
|||
if (_isThreaded) {
|
||||
_thread = new QThread(this);
|
||||
|
||||
// match the thread name to our object name
|
||||
_thread->setObjectName(objectName());
|
||||
|
||||
// when the worker thread is started, call our engine's run..
|
||||
connect(_thread, SIGNAL(started()), this, SLOT(threadRoutine()));
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace Setting {
|
|||
}
|
||||
|
||||
// Sets up the settings private instance. Should only be run once at startup
|
||||
void setupPrivateInstance() {
|
||||
void init() {
|
||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
QSettings applicationInfo(PathUtils::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
|
||||
|
@ -59,14 +59,11 @@ namespace Setting {
|
|||
QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
||||
privateInstance->moveToThread(thread);
|
||||
thread->start();
|
||||
qCDebug(shared) << "Settings thread started.";
|
||||
|
||||
qCDebug(shared) << "Settings thread started.";
|
||||
|
||||
// Register cleanupPrivateInstance to run inside QCoreApplication's destructor.
|
||||
qAddPostRoutine(cleanupPrivateInstance);
|
||||
}
|
||||
// Register setupPrivateInstance to run after QCoreApplication's constructor.
|
||||
Q_COREAPP_STARTUP_FUNCTION(setupPrivateInstance)
|
||||
|
||||
}
|
||||
|
||||
Interface::~Interface() {
|
||||
if (privateInstance) {
|
||||
|
@ -76,16 +73,20 @@ namespace Setting {
|
|||
|
||||
void Interface::init() {
|
||||
if (!privateInstance) {
|
||||
qWarning() << "Setting::Interface::init(): Manager not yet created, bailing";
|
||||
return;
|
||||
// WARNING: As long as we are using QSettings this should always be triggered for each Setting::Handle
|
||||
// in an assignment-client - the QSettings backing we use for this means persistence of these
|
||||
// settings from an AC (when there can be multiple terminating at same time on one machine)
|
||||
// is currently not supported
|
||||
qWarning() << "Setting::Interface::init() for key" << _key << "- Manager not yet created." <<
|
||||
"Settings persistence disabled.";
|
||||
} else {
|
||||
// Register Handle
|
||||
privateInstance->registerHandle(this);
|
||||
_isInitialized = true;
|
||||
|
||||
// Load value from disk
|
||||
load();
|
||||
}
|
||||
|
||||
// Register Handle
|
||||
privateInstance->registerHandle(this);
|
||||
_isInitialized = true;
|
||||
|
||||
// Load value from disk
|
||||
load();
|
||||
}
|
||||
|
||||
void Interface::maybeInit() {
|
||||
|
|
|
@ -16,11 +16,14 @@
|
|||
#include <QVariant>
|
||||
|
||||
namespace Setting {
|
||||
void init();
|
||||
void cleanupSettings();
|
||||
|
||||
class Interface {
|
||||
public:
|
||||
QString getKey() const { return _key; }
|
||||
bool isSet() const { return _isSet; }
|
||||
|
||||
bool isSet() const { return _isSet; }
|
||||
|
||||
virtual void setVariant(const QVariant& variant) = 0;
|
||||
virtual QVariant getVariant() = 0;
|
||||
|
||||
|
|
|
@ -18,11 +18,12 @@ namespace Setting {
|
|||
Manager::~Manager() {
|
||||
// Cleanup timer
|
||||
stopTimer();
|
||||
disconnect(_saveTimer, 0, 0, 0);
|
||||
|
||||
delete _saveTimer;
|
||||
|
||||
// Save all settings before exit
|
||||
saveAll();
|
||||
sync();
|
||||
|
||||
// sync will be called in the QSettings destructor
|
||||
}
|
||||
|
||||
void Manager::registerHandle(Setting::Interface* handle) {
|
||||
|
|
Loading…
Reference in a new issue