diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index bf67d4d597..86680d2f03 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -18,10 +18,9 @@ #include #include #include -#include -#include #include #include +#include #include #include #include @@ -40,79 +39,43 @@ SharedAssignmentPointer AssignmentClient::_currentAssignment; int hifiSockAddrMeta = qRegisterMetaType("HifiSockAddr"); -AssignmentClient::AssignmentClient(int &argc, char **argv) : - QCoreApplication(argc, argv), +AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool, QUuid walletUUID, + QString assignmentServerHostname, quint16 assignmentServerPort) : _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME), - _localASPortSharedMem(NULL) + _localASPortSharedMem(NULL), + _localACMPortSharedMem(NULL) { LogUtils::init(); - setOrganizationName("High Fidelity"); - setOrganizationDomain("highfidelity.io"); - setApplicationName("assignment-client"); QSettings::setDefaultFormat(QSettings::IniFormat); // create a NodeList as an unassigned client DependencyManager::registerInheritance(); auto addressManager = DependencyManager::set(); auto nodeList = DependencyManager::set(NodeType::Unassigned); - auto avatarHashMap = DependencyManager::set(); - - // setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us -#ifdef _WIN32 - installNativeEventFilter(&ShutdownEventListener::getInstance()); -#else - ShutdownEventListener::getInstance(); -#endif + + // make up a uuid for this child so the parent can tell us apart. This id will be changed + // when the domain server hands over an assignment. + QUuid nodeUUID = QUuid::createUuid(); + nodeList->setSessionUUID(nodeUUID); // set the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); - const QVariantMap argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); - - const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "t"; - const QString ASSIGNMENT_POOL_OPTION = "pool"; - const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet"; - const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "a"; - const QString CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION = "p"; - - Assignment::Type requestAssignmentType = Assignment::AllTypes; - - // check for an assignment type passed on the command line or in the config - if (argumentVariantMap.contains(ASSIGNMENT_TYPE_OVERRIDE_OPTION)) { - requestAssignmentType = (Assignment::Type) argumentVariantMap.value(ASSIGNMENT_TYPE_OVERRIDE_OPTION).toInt(); - } - - QString assignmentPool; - - // check for an assignment pool passed on the command line or in the config - if (argumentVariantMap.contains(ASSIGNMENT_POOL_OPTION)) { - assignmentPool = argumentVariantMap.value(ASSIGNMENT_POOL_OPTION).toString(); - } - // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); // check for a wallet UUID on the command line or in the config // this would represent where the user running AC wants funds sent to - if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) { - QUuid walletUUID = argumentVariantMap.value(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION).toString(); + if (!walletUUID.isNull()) { qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); _requestAssignment.setWalletUUID(walletUUID); } - quint16 assignmentServerPort = DEFAULT_DOMAIN_SERVER_PORT; - // check for an overriden assignment server hostname - if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION)) { + if (assignmentServerHostname != "") { // change the hostname for our assignment server - _assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString(); - } - - // check for an overriden assignment server port - if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION)) { - assignmentServerPort = - argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toString().toUInt(); + _assignmentServerHostname = assignmentServerHostname; } _assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerPort, true); @@ -123,9 +86,12 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : // call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required qDebug() << "Waiting for assignment -" << _requestAssignment; - QTimer* timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), SLOT(sendAssignmentRequest())); - timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS); + if (_assignmentServerHostname != "localhost") { + qDebug () << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort(); + } + + connect(&_requestTimer, SIGNAL(timeout()), SLOT(sendAssignmentRequest())); + _requestTimer.start(ASSIGNMENT_REQUEST_INTERVAL_MSECS); // connect our readPendingDatagrams method to the readyRead() signal of the socket connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams); @@ -136,6 +102,45 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : // 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(); +} + + +void AssignmentClient::stopAssignmentClient() { + qDebug() << "Exiting."; + _requestTimer.stop(); + _statsTimerACM.stop(); + QCoreApplication::quit(); +} + + +void AssignmentClient::setUpStatsToMonitor() { + // Figure out the address to send out stats to + quint16 localMonitorServerPort = DEFAULT_ASSIGNMENT_CLIENT_MONITOR_PORT; + auto nodeList = DependencyManager::get(); + + nodeList->getLocalServerPortFromSharedMemory(ASSIGNMENT_CLIENT_MONITOR_LOCAL_PORT_SMEM_KEY, + _localACMPortSharedMem, localMonitorServerPort); + _assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, localMonitorServerPort, true); + + // send a stats packet every 1 seconds + connect(&_statsTimerACM, &QTimer::timeout, this, &AssignmentClient::sendStatsPacketToACM); + _statsTimerACM.start(1000); +} + +void AssignmentClient::sendStatsPacketToACM() { + // tell the assignment client monitor what this assignment client is doing (if anything) + QJsonObject statsObject; + auto nodeList = DependencyManager::get(); + + if (_currentAssignment) { + statsObject["assignment_type"] = _currentAssignment->getTypeName(); + } else { + statsObject["assignment_type"] = "none"; + } + nodeList->sendStats(statsObject, _assignmentClientMonitorSocket); } void AssignmentClient::sendAssignmentRequest() { @@ -145,23 +150,9 @@ void AssignmentClient::sendAssignmentRequest() { if (_assignmentServerHostname == "localhost") { // we want to check again for the local domain-server port in case the DS has restarted - if (!_localASPortSharedMem) { - _localASPortSharedMem = new QSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this); - - if (!_localASPortSharedMem->attach(QSharedMemory::ReadOnly)) { - qWarning() << "Could not attach to shared memory at key" << DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY - << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort(); - } - } - - if (_localASPortSharedMem->isAttached()) { - _localASPortSharedMem->lock(); - - quint16 localAssignmentServerPort; - memcpy(&localAssignmentServerPort, _localASPortSharedMem->data(), sizeof(localAssignmentServerPort)); - - _localASPortSharedMem->unlock(); - + quint16 localAssignmentServerPort; + if (nodeList->getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, _localASPortSharedMem, + localAssignmentServerPort)) { if (localAssignmentServerPort != _assignmentServerSocket.getPort()) { qDebug() << "Port for local assignment server read from shared memory is" << localAssignmentServerPort; @@ -170,7 +161,6 @@ void AssignmentClient::sendAssignmentRequest() { nodeList->setAssignmentServerSocket(_assignmentServerSocket); } } - } nodeList->sendAssignment(_requestAssignment); @@ -227,6 +217,14 @@ void AssignmentClient::readPendingDatagrams() { } else { qDebug() << "Received an assignment that could not be unpacked. Re-requesting."; } + } else if (packetTypeForPacket(receivedPacket) == PacketTypeStopNode) { + if (senderSockAddr.getAddress() == QHostAddress::LocalHost || + senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) { + qDebug() << "Network told me to exit."; + emit stopAssignmentClient(); + } else { + qDebug() << "Got a stop packet from other than localhost."; + } } else { // have the NodeList attempt to handle it nodeList->processNodeData(senderSockAddr, receivedPacket); diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 053458f136..d1d93c78dc 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -18,10 +18,12 @@ class QSharedMemory; -class AssignmentClient : public QCoreApplication { +class AssignmentClient : public QObject { Q_OBJECT public: - AssignmentClient(int &argc, char **argv); + + AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool, + QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort); static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; } private slots: @@ -29,13 +31,22 @@ private slots: void readPendingDatagrams(); void assignmentCompleted(); void handleAuthenticationRequest(); + void sendStatsPacketToACM(); + void stopAssignmentClient(); private: + void setUpStatsToMonitor(); Assignment _requestAssignment; static SharedAssignmentPointer _currentAssignment; QString _assignmentServerHostname; HifiSockAddr _assignmentServerSocket; - QSharedMemory* _localASPortSharedMem; + 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 + + protected: + HifiSockAddr _assignmentClientMonitorSocket; }; #endif // hifi_AssignmentClient_h diff --git a/assignment-client/src/AssignmentClientApp.cpp b/assignment-client/src/AssignmentClientApp.cpp new file mode 100644 index 0000000000..cfb77c8542 --- /dev/null +++ b/assignment-client/src/AssignmentClientApp.cpp @@ -0,0 +1,185 @@ +// +// AssignmentClientapp.cpp +// assignment-client/src +// +// Created by Seth Alves on 2/19/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 +// + +#include + +#include +#include +#include +#include + +#include "Assignment.h" +#include "AssignmentClient.h" +#include "AssignmentClientMonitor.h" +#include "AssignmentClientApp.h" + + +AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : + QCoreApplication(argc, argv) +{ +# ifndef WIN32 + setvbuf(stdout, NULL, _IOLBF, 0); +# endif + + // setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us +# ifdef _WIN32 + installNativeEventFilter(&ShutdownEventListener::getInstance()); +# else + ShutdownEventListener::getInstance(); +# endif + + setOrganizationName("High Fidelity"); + setOrganizationDomain("highfidelity.io"); + setApplicationName("assignment-client"); + + // use the verbose message handler in Logging + qInstallMessageHandler(LogHandler::verboseMessageHandler); + + // parse command-line + QCommandLineParser parser; + parser.setApplicationDescription("High Fidelity Assignment Client"); + parser.addHelpOption(); + + const QCommandLineOption helpOption = parser.addHelpOption(); + + const QCommandLineOption clientTypeOption(ASSIGNMENT_TYPE_OVERRIDE_OPTION, + "run single assignment client of given type", "type"); + parser.addOption(clientTypeOption); + + const QCommandLineOption poolOption(ASSIGNMENT_POOL_OPTION, "set assignment pool", "pool-name"); + parser.addOption(poolOption); + + const QCommandLineOption walletDestinationOption(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION, + "set wallet destination", "wallet-uuid"); + parser.addOption(walletDestinationOption); + + const QCommandLineOption assignmentServerHostnameOption(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION, + "set assignment-server hostname", "hostname"); + parser.addOption(assignmentServerHostnameOption); + + const QCommandLineOption assignmentServerPortOption(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION, + "set assignment-server port", "port"); + parser.addOption(assignmentServerPortOption); + + const QCommandLineOption numChildsOption(ASSIGNMENT_NUM_FORKS_OPTION, "number of children to fork", "child-count"); + parser.addOption(numChildsOption); + + const QCommandLineOption minChildsOption(ASSIGNMENT_MIN_FORKS_OPTION, "minimum number of children", "child-count"); + parser.addOption(minChildsOption); + + const QCommandLineOption maxChildsOption(ASSIGNMENT_MAX_FORKS_OPTION, "maximum number of children", "child-count"); + parser.addOption(maxChildsOption); + + + if (!parser.parse(QCoreApplication::arguments())) { + qCritical() << parser.errorText() << endl; + parser.showHelp(); + Q_UNREACHABLE(); + } + + if (parser.isSet(helpOption)) { + parser.showHelp(); + Q_UNREACHABLE(); + } + + + const QVariantMap argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); + + + unsigned int numForks = 0; + if (parser.isSet(numChildsOption)) { + numForks = parser.value(numChildsOption).toInt(); + } + + unsigned int minForks = 0; + if (parser.isSet(minChildsOption)) { + minForks = parser.value(minChildsOption).toInt(); + } + + unsigned int maxForks = 0; + if (parser.isSet(maxChildsOption)) { + maxForks = parser.value(maxChildsOption).toInt(); + } + + if (!numForks && minForks) { + // if the user specified --min but not -n, set -n to --min + numForks = minForks; + } + + Assignment::Type requestAssignmentType = Assignment::AllTypes; + if (argumentVariantMap.contains(ASSIGNMENT_TYPE_OVERRIDE_OPTION)) { + requestAssignmentType = (Assignment::Type) argumentVariantMap.value(ASSIGNMENT_TYPE_OVERRIDE_OPTION).toInt(); + } + if (parser.isSet(clientTypeOption)) { + requestAssignmentType = (Assignment::Type) parser.value(clientTypeOption).toInt(); + } + + QString assignmentPool; + // check for an assignment pool passed on the command line or in the config + if (argumentVariantMap.contains(ASSIGNMENT_POOL_OPTION)) { + assignmentPool = argumentVariantMap.value(ASSIGNMENT_POOL_OPTION).toString(); + } + if (parser.isSet(poolOption)) { + assignmentPool = parser.value(poolOption); + } + + + QUuid walletUUID; + if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) { + walletUUID = argumentVariantMap.value(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION).toString(); + } + if (parser.isSet(walletDestinationOption)) { + walletUUID = parser.value(walletDestinationOption); + } + + QString assignmentServerHostname; + if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) { + assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString(); + } + if (parser.isSet(assignmentServerHostnameOption)) { + assignmentServerHostname = parser.value(assignmentServerHostnameOption); + } + + // check for an overriden assignment server port + quint16 assignmentServerPort = DEFAULT_DOMAIN_SERVER_PORT; + if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) { + assignmentServerPort = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toString().toUInt(); + } + 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"; + parser.showHelp(); + Q_UNREACHABLE(); + } + if (maxForks && maxForks < numForks) { + qCritical() << "--max can't be less than -n"; + parser.showHelp(); + Q_UNREACHABLE(); + } + } + + + if (numForks || minForks || maxForks) { + AssignmentClientMonitor monitor(numForks, minForks, maxForks, assignmentPool, + walletUUID, assignmentServerHostname, assignmentServerPort); + exec(); + } else { + AssignmentClient client(requestAssignmentType, assignmentPool, + walletUUID, assignmentServerHostname, assignmentServerPort); + exec(); + } +} diff --git a/assignment-client/src/AssignmentClientApp.h b/assignment-client/src/AssignmentClientApp.h new file mode 100644 index 0000000000..531035ef0e --- /dev/null +++ b/assignment-client/src/AssignmentClientApp.h @@ -0,0 +1,34 @@ +// +// AssignmentClientapp.h +// assignment-client/src +// +// Created by Seth Alves on 2/19/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 +// + +#ifndef hifi_AssignmentClientApp_h +#define hifi_AssignmentClientApp_h + + +#include + +const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "t"; +const QString ASSIGNMENT_POOL_OPTION = "pool"; +const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet"; +const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "a"; +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"; + + +class AssignmentClientApp : public QCoreApplication { + Q_OBJECT +public: + AssignmentClientApp(int argc, char* argv[]); +}; + +#endif // hifi_AssignmentClientApp_h diff --git a/assignment-client/src/AssignmentClientChildData.cpp b/assignment-client/src/AssignmentClientChildData.cpp new file mode 100644 index 0000000000..de34613ec9 --- /dev/null +++ b/assignment-client/src/AssignmentClientChildData.cpp @@ -0,0 +1,8 @@ + +#include "AssignmentClientChildData.h" + + +AssignmentClientChildData::AssignmentClientChildData(QString childType) : + _childType(childType) +{ +} diff --git a/assignment-client/src/AssignmentClientChildData.h b/assignment-client/src/AssignmentClientChildData.h new file mode 100644 index 0000000000..6fd5f72f7f --- /dev/null +++ b/assignment-client/src/AssignmentClientChildData.h @@ -0,0 +1,32 @@ +// +// AssignmentClientChildData.h +// assignment-client/src +// +// Created by Seth Alves on 2/23/2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AssignmentClientChildData_h +#define hifi_AssignmentClientChildData_h + +#include + + +class AssignmentClientChildData : public NodeData { + public: + AssignmentClientChildData(QString childType); + + QString getChildType() { return _childType; } + void setChildType(QString childType) { _childType = childType; } + + // implement parseData to return 0 so we can be a subclass of NodeData + int parseData(const QByteArray& packet) { return 0; } + + private: + QString _childType; +}; + +#endif // hifi_AssignmentClientChildData_h diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 45e1f56d53..7517669d0b 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -12,40 +12,51 @@ #include #include -#include +#include #include "AssignmentClientMonitor.h" +#include "AssignmentClientApp.h" +#include "AssignmentClientChildData.h" +#include "PacketHeaders.h" +#include "SharedUtil.h" const char* NUM_FORKS_PARAMETER = "-n"; const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor"; -AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, int numAssignmentClientForks) : - QCoreApplication(argc, argv) +AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmentClientForks, + const unsigned int minAssignmentClientForks, + const unsigned int maxAssignmentClientForks, + QString assignmentPool, QUuid walletUUID, QString assignmentServerHostname, + quint16 assignmentServerPort) : + _numAssignmentClientForks(numAssignmentClientForks), + _minAssignmentClientForks(minAssignmentClientForks), + _maxAssignmentClientForks(maxAssignmentClientForks), + _assignmentPool(assignmentPool), + _walletUUID(walletUUID), + _assignmentServerHostname(assignmentServerHostname), + _assignmentServerPort(assignmentServerPort) { // start the Logging class with the parent's target name LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME); - - // setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us -#ifdef _WIN32 - installNativeEventFilter(&ShutdownEventListener::getInstance()); -#else - ShutdownEventListener::getInstance(); -#endif - - _childArguments = arguments(); - - // remove the parameter for the number of forks so it isn't passed to the child forked processes - int forksParameterIndex = _childArguments.indexOf(NUM_FORKS_PARAMETER); - - // this removes both the "-n" parameter and the number of forks passed - _childArguments.removeAt(forksParameterIndex); - _childArguments.removeAt(forksParameterIndex); + + // create a NodeList so we can receive stats from children + DependencyManager::registerInheritance(); + auto addressManager = DependencyManager::set(); + auto nodeList = DependencyManager::set(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_PORT); + + connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClientMonitor::readPendingDatagrams); + + nodeList->putLocalPortIntoSharedMemory(ASSIGNMENT_CLIENT_MONITOR_LOCAL_PORT_SMEM_KEY, this); // use QProcess to fork off a process for each of the child assignment clients - for (int i = 0; i < numAssignmentClientForks; i++) { + for (unsigned int i = 0; i < _numAssignmentClientForks; i++) { spawnChildClient(); } + + connect(&_checkSparesTimer, &QTimer::timeout, this, &AssignmentClientMonitor::checkSpares); + + _checkSparesTimer.start(NODE_SILENCE_THRESHOLD_MSECS * 3); } AssignmentClientMonitor::~AssignmentClientMonitor() { @@ -53,46 +64,145 @@ AssignmentClientMonitor::~AssignmentClientMonitor() { } void AssignmentClientMonitor::stopChildProcesses() { - - QList >::Iterator it = _childProcesses.begin(); - while (it != _childProcesses.end()) { - if (!it->isNull()) { - qDebug() << "Monitor is terminating child process" << it->data(); - - // don't re-spawn this child when it goes down - disconnect(it->data(), 0, this, 0); - - it->data()->terminate(); - it->data()->waitForFinished(); - } - - it = _childProcesses.erase(it); - } + auto nodeList = DependencyManager::get(); + + nodeList->eachNode([&](const SharedNodePointer& node) { + qDebug() << "asking child" << node->getUUID() << "to exit."; + node->activateLocalSocket(); + QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode); + nodeList->writeUnverifiedDatagram(diePacket, *node->getActiveSocket()); + }); } void AssignmentClientMonitor::spawnChildClient() { QProcess *assignmentClient = new QProcess(this); - - _childProcesses.append(QPointer(assignmentClient)); - + + // unparse the parts of the command-line that the child cares about + QStringList _childArguments; + if (_assignmentPool != "") { + _childArguments.append("--" + ASSIGNMENT_POOL_OPTION); + _childArguments.append(_assignmentPool); + } + if (!_walletUUID.isNull()) { + _childArguments.append("--" + ASSIGNMENT_WALLET_DESTINATION_ID_OPTION); + _childArguments.append(_walletUUID.toString()); + } + if (_assignmentServerHostname != "") { + _childArguments.append("--" + CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION); + _childArguments.append(_assignmentServerHostname); + } + if (_assignmentServerPort != DEFAULT_DOMAIN_SERVER_PORT) { + _childArguments.append("--" + CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION); + _childArguments.append(QString::number(_assignmentServerPort)); + } + // make sure that the output from the child process appears in our output assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels); - assignmentClient->start(applicationFilePath(), _childArguments); - - // link the child processes' finished slot to our childProcessFinished slot - connect(assignmentClient, SIGNAL(finished(int, QProcess::ExitStatus)), this, - SLOT(childProcessFinished(int, QProcess::ExitStatus))); - + assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments); + qDebug() << "Spawned a child client with PID" << assignmentClient->pid(); } -void AssignmentClientMonitor::childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { - qDebug("Replacing dead child assignment client with a new one"); - - // remove the old process from our list of child processes - qDebug() << "need to remove" << QPointer(qobject_cast(sender())); - _childProcesses.removeOne(QPointer(qobject_cast(sender()))); - - spawnChildClient(); + + +void AssignmentClientMonitor::checkSpares() { + auto nodeList = DependencyManager::get(); + QUuid aSpareId = ""; + unsigned int spareCount = 0; + unsigned int totalCount = 0; + + nodeList->removeSilentNodes(); + + nodeList->eachNode([&](const SharedNodePointer& node) { + AssignmentClientChildData *childData = static_cast(node->getLinkedData()); + totalCount ++; + if (childData->getChildType() == "none") { + spareCount ++; + aSpareId = node->getUUID(); + } + }); + + // Spawn or kill children, as needed. If --min or --max weren't specified, allow the child count + // to drift up or down as far as needed. + if (spareCount < 1 || totalCount < _minAssignmentClientForks) { + if (!_maxAssignmentClientForks || totalCount < _maxAssignmentClientForks) { + spawnChildClient(); + } + } + + if (spareCount > 1) { + if (!_minAssignmentClientForks || totalCount > _minAssignmentClientForks) { + // kill aSpareId + qDebug() << "asking child" << aSpareId << "to exit."; + SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId); + childNode->activateLocalSocket(); + QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode); + nodeList->writeUnverifiedDatagram(diePacket, childNode); + } + } } + + +void AssignmentClientMonitor::readPendingDatagrams() { + auto nodeList = DependencyManager::get(); + + QByteArray receivedPacket; + HifiSockAddr senderSockAddr; + + while (nodeList->getNodeSocket().hasPendingDatagrams()) { + receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); + nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), + senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); + + if (nodeList->packetVersionAndHashMatch(receivedPacket)) { + if (packetTypeForPacket(receivedPacket) == PacketTypeNodeJsonStats) { + QUuid packetUUID = uuidFromPacketHeader(receivedPacket); + SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket); + if (!matchingNode) { + // The parent only expects to be talking with prorams running on this same machine. + if (senderSockAddr.getAddress() == QHostAddress::LocalHost || + senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) { + if (!packetUUID.isNull()) { + matchingNode = DependencyManager::get()->addOrUpdateNode + (packetUUID, NodeType::Unassigned, senderSockAddr, senderSockAddr, false); + AssignmentClientChildData *childData = new AssignmentClientChildData("unknown"); + matchingNode->setLinkedData(childData); + } else { + // tell unknown assignment-client child to exit. + qDebug() << "asking unknown child to exit."; + QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode); + nodeList->writeUnverifiedDatagram(diePacket, senderSockAddr); + } + } + } + + if (matchingNode) { + // update our records about how to reach this child + matchingNode->setLocalSocket(senderSockAddr); + + // push past the packet header + QDataStream packetStream(receivedPacket); + packetStream.skipRawData(numBytesForPacketHeader(receivedPacket)); + // decode json + QVariantMap unpackedVariantMap; + packetStream >> unpackedVariantMap; + QJsonObject unpackedStatsJSON = QJsonObject::fromVariantMap(unpackedVariantMap); + + // get child's assignment type out of the decoded json + QString childType = unpackedStatsJSON["assignment_type"].toString(); + AssignmentClientChildData *childData = + static_cast(matchingNode->getLinkedData()); + childData->setChildType(childType); + // note when this child talked + matchingNode->setLastHeardMicrostamp(usecTimestampNow()); + } + } else { + // have the NodeList attempt to handle it + nodeList->processNodeData(senderSockAddr, receivedPacket); + } + } + } +} + + diff --git a/assignment-client/src/AssignmentClientMonitor.h b/assignment-client/src/AssignmentClientMonitor.h index 9a7bca9cb3..dc88bfcd95 100644 --- a/assignment-client/src/AssignmentClientMonitor.h +++ b/assignment-client/src/AssignmentClientMonitor.h @@ -15,25 +15,40 @@ #include #include #include +#include #include +#include "AssignmentClientChildData.h" + extern const char* NUM_FORKS_PARAMETER; -class AssignmentClientMonitor : public QCoreApplication { + +class AssignmentClientMonitor : public QObject { Q_OBJECT public: - AssignmentClientMonitor(int &argc, char **argv, int numAssignmentClientForks); + AssignmentClientMonitor(const unsigned int numAssignmentClientForks, const unsigned int minAssignmentClientForks, + const unsigned int maxAssignmentClientForks, QString assignmentPool, QUuid walletUUID, + QString assignmentServerHostname, quint16 assignmentServerPort); ~AssignmentClientMonitor(); void stopChildProcesses(); private slots: - void childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); + void readPendingDatagrams(); + void checkSpares(); + private: void spawnChildClient(); - QList > _childProcesses; - - QStringList _childArguments; + QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children + + const unsigned int _numAssignmentClientForks; + const unsigned int _minAssignmentClientForks; + const unsigned int _maxAssignmentClientForks; + + QString _assignmentPool; + QUuid _walletUUID; + QString _assignmentServerHostname; + quint16 _assignmentServerPort; }; #endif // hifi_AssignmentClientMonitor_h diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 3bf6990a74..81a12526bf 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -9,34 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include "Assignment.h" -#include "AssignmentClient.h" -#include "AssignmentClientMonitor.h" +#include "AssignmentClientApp.h" int main(int argc, char* argv[]) { -#ifndef WIN32 - setvbuf(stdout, NULL, _IOLBF, 0); -#endif - - // use the verbose message handler in Logging - qInstallMessageHandler(LogHandler::verboseMessageHandler); - - const char* numForksString = getCmdOption(argc, (const char**)argv, NUM_FORKS_PARAMETER); - - int numForks = 0; - - if (numForksString) { - numForks = atoi(numForksString); - } - - if (numForks) { - AssignmentClientMonitor monitor(argc, argv, numForks); - return monitor.exec(); - } else { - AssignmentClient client(argc, argv); - return client.exec(); - } + AssignmentClientApp app(argc, argv); + return 0; } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f84722a438..9aec456587 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include "DomainServerNodeData.h" @@ -246,19 +247,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { auto nodeList = DependencyManager::set(domainServerPort, domainServerDTLSPort); // no matter the local port, save it to shared mem so that local assignment clients can ask what it is - QSharedMemory* sharedPortMem = new QSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this); - quint16 localPort = nodeList->getNodeSocket().localPort(); - - // attempt to create the shared memory segment - if (sharedPortMem->create(sizeof(localPort)) || sharedPortMem->attach()) { - sharedPortMem->lock(); - memcpy(sharedPortMem->data(), &localPort, sizeof(localPort)); - sharedPortMem->unlock(); - - qDebug() << "Wrote local listening port" << localPort << "to shared memory at key" << DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY; - } else { - qWarning() << "Failed to create and attach to shared memory to share local port with assignment-client children."; - } + nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this); // set our LimitedNodeList UUID to match the UUID from our config // nodes will currently use this to add resources to data-web that relate to our domain @@ -955,8 +944,10 @@ void DomainServer::readAvailableDatagrams() { if (requestAssignment.getType() != Assignment::AgentType || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { + static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex + ("Received a request for assignment type [^ ]+ from [^ ]+"); qDebug() << "Received a request for assignment type" << requestAssignment.getType() - << "from" << senderSockAddr; + << "from" << senderSockAddr; noisyMessageTimer.restart(); } @@ -986,6 +977,8 @@ void DomainServer::readAvailableDatagrams() { } else { if (requestAssignment.getType() != Assignment::AgentType || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { + static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex + ("Unable to fulfill assignment request of type [^ ]+ from [^ ]+"); qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() << "from" << senderSockAddr; noisyMessageTimer.restart(); diff --git a/examples/editEntities.js b/examples/editEntities.js index 9b13b167e7..ef6472e4b8 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -934,24 +934,37 @@ PropertiesTool = function(opts) { data = { type: 'update', }; - if (selectionManager.hasSelection()) { - data.id = selectionManager.selections[0].id; - data.properties = Entities.getEntityProperties(selectionManager.selections[0]); - data.properties.rotation = Quat.safeEulerAngles(data.properties.rotation); + var selections = []; + for (var i = 0; i < selectionManager.selections.length; i++) { + var entity = {}; + entity.id = selectionManager.selections[i].id; + entity.properties = Entities.getEntityProperties(selectionManager.selections[i]); + entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation); + selections.push(entity); } + data.selections = selections; webView.eventBridge.emitScriptEvent(JSON.stringify(data)); }); webView.eventBridge.webEventReceived.connect(function(data) { - print(data); data = JSON.parse(data); if (data.type == "update") { selectionManager.saveProperties(); - if (data.properties.rotation !== undefined) { - var rotation = data.properties.rotation; - data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z); + if (selectionManager.selections.length > 1) { + properties = { + locked: data.properties.locked, + visible: data.properties.visible, + }; + for (var i = 0; i < selectionManager.selections.length; i++) { + Entities.editEntity(selectionManager.selections[i], properties); + } + } else { + if (data.properties.rotation !== undefined) { + var rotation = data.properties.rotation; + data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z); + } + Entities.editEntity(selectionManager.selections[0], data.properties); } - Entities.editEntity(selectionManager.selections[0], data.properties); pushCommandForSelections(); selectionManager._update(); } else if (data.type == "action") { diff --git a/examples/example/misc/sunLightExample.js b/examples/example/misc/sunLightExample.js new file mode 100644 index 0000000000..42837c6836 --- /dev/null +++ b/examples/example/misc/sunLightExample.js @@ -0,0 +1,34 @@ +// +// SunLightExample.js +// examples +// Sam Gateau +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var intensity = 1.0; +var day = 0.0; +var hour = 12.0; +var longitude = 115.0; +var latitude = 31.0; +var stageOrientation = Quat.fromPitchYawRollDegrees(0.0, 180.0, 0.0); + +Scene.setStageDayTime(hour); +Scene.setStageOrientation(stageOrientation); +Scene.setStageLocation(longitude, latitude, 0.0); +/* +function ticktack() { + hour += 0.1; + //Scene.setSunIntensity(Math.cos(time)); + if (hour > 24.0) { + hour = 0.0; + day++; + Scene.setStageYearTime(day); + } + Scene.setStageDayTime(hour); +} + +Script.setInterval(ticktack, 41); +*/ \ No newline at end of file diff --git a/examples/html/entityList.html b/examples/html/entityList.html index bbfa4d81b8..bcc1c117ea 100644 --- a/examples/html/entityList.html +++ b/examples/html/entityList.html @@ -20,6 +20,7 @@ elRefresh = document.getElementById("refresh"); elDelete = document.getElementById("delete"); elTeleport = document.getElementById("teleport"); + elNoEntitiesMessage = document.getElementById("no-entities"); document.getElementById("entity-type").onclick = function() { setSortColumn('type'); @@ -155,11 +156,18 @@ } } else if (data.type == "update") { var newEntities = data.entities; - for (var i = 0; i < newEntities.length; i++) { - var id = newEntities[i].id; - addEntity(id, newEntities[i].type, newEntities[i].url); + if (newEntities.length == 0) { + elEntityTable.style.display = "none"; + elNoEntitiesMessage.style.display = "block"; + } else { + elEntityTable.style.display = "table"; + elNoEntitiesMessage.style.display = "none"; + for (var i = 0; i < newEntities.length; i++) { + var id = newEntities[i].id; + addEntity(id, newEntities[i].type, newEntities[i].url); + } + updateSelectedEntities(data.selectedIDs); } - updateSelectedEntities(data.selectedIDs); } }); setTimeout(refreshEntities, 1000); @@ -194,6 +202,8 @@ - +
+ No entities found within 50 meter radius. Try moving to a different location and refreshing. +
diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index ad2b359e79..f54c2c9ce3 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -132,17 +132,7 @@ var elLightDiffuseGreen = document.getElementById("property-light-diffuse-green"); var elLightDiffuseBlue = document.getElementById("property-light-diffuse-blue"); - var elLightAmbientRed = document.getElementById("property-light-ambient-red"); - var elLightAmbientGreen = document.getElementById("property-light-ambient-green"); - var elLightAmbientBlue = document.getElementById("property-light-ambient-blue"); - - var elLightSpecularRed = document.getElementById("property-light-specular-red"); - var elLightSpecularGreen = document.getElementById("property-light-specular-green"); - var elLightSpecularBlue = document.getElementById("property-light-specular-blue"); - var elLightConstantAttenuation = document.getElementById("property-light-constant-attenuation"); - var elLightLinearAttenuation = document.getElementById("property-light-linear-attenuation"); - var elLightQuadraticAttenuation = document.getElementById("property-light-quadratic-attenuation"); var elLightExponent = document.getElementById("property-light-exponent"); var elLightCutoff = document.getElementById("property-light-cutoff"); @@ -171,12 +161,37 @@ EventBridge.scriptEventReceived.connect(function(data) { data = JSON.parse(data); if (data.type == "update") { - if (data.properties === undefined) { - disableChildren(document.getElementById("properties"), 'input'); - } else { - var properties = data.properties; + if (data.selections.length == 0) { + elType.innerHTML = "No Selection"; + elID.innerHTML = ""; + disableChildren(document.getElementById("properties-list"), 'input'); + } else if (data.selections.length > 1) { + var selections = data.selections; - elID.innerHTML = data.id; + var ids = []; + var types = {}; + + for (var i = 0; i < selections.length; i++) { + ids.push(selections[i].id); + var type = selections[i].properties.type; + if (types[type] === undefined) { + types[type] = 0; + } + types[type]++; + } + elID.innerHTML = ids.join("
"); + + var typeStrs = []; + for (type in types) { + typeStrs.push(type + " (" + types[type] + ")"); + } + elType.innerHTML = typeStrs.join(", "); + + disableChildren(document.getElementById("properties-list"), 'input'); + } else { + var properties = data.selections[0].properties; + + elID.innerHTML = properties.id; elType.innerHTML = properties.type; @@ -294,17 +309,7 @@ elLightDiffuseGreen.value = properties.diffuseColor.green; elLightDiffuseBlue.value = properties.diffuseColor.blue; - elLightAmbientRed.value = properties.ambientColor.red; - elLightAmbientGreen.value = properties.ambientColor.green; - elLightAmbientBlue.value = properties.ambientColor.blue; - - elLightSpecularRed.value = properties.specularColor.red; - elLightSpecularGreen.value = properties.specularColor.green; - elLightSpecularBlue.value = properties.specularColor.blue; - elLightConstantAttenuation.value = properties.constantAttenuation; - elLightLinearAttenuation.value = properties.linearAttenuation; - elLightQuadraticAttenuation.value = properties.quadraticAttenuation; elLightExponent.value = properties.exponent; elLightCutoff.value = properties.cutoff; } @@ -381,21 +386,7 @@ elLightDiffuseGreen.addEventListener('change', lightDiffuseChangeFunction); elLightDiffuseBlue.addEventListener('change', lightDiffuseChangeFunction); - var lightAmbientChangeFunction = createEmitColorPropertyUpdateFunction( - 'ambientColor', elLightAmbientRed, elLightAmbientGreen, elLightAmbientBlue); - elLightAmbientRed.addEventListener('change', lightAmbientChangeFunction); - elLightAmbientGreen.addEventListener('change', lightAmbientChangeFunction); - elLightAmbientBlue.addEventListener('change', lightAmbientChangeFunction); - - var lightSpecularChangeFunction = createEmitColorPropertyUpdateFunction( - 'specularColor', elLightSpecularRed, elLightSpecularGreen, elLightSpecularBlue); - elLightSpecularRed.addEventListener('change', lightSpecularChangeFunction); - elLightSpecularGreen.addEventListener('change', lightSpecularChangeFunction); - elLightSpecularBlue.addEventListener('change', lightSpecularChangeFunction); - elLightConstantAttenuation.addEventListener('change', createEmitNumberPropertyUpdateFunction('constantAttenuation')); - elLightLinearAttenuation.addEventListener('change', createEmitNumberPropertyUpdateFunction('linearAttenuation')); - elLightQuadraticAttenuation.addEventListener('change', createEmitNumberPropertyUpdateFunction('quadraticAttenuation')); elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent')); elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff')); @@ -715,7 +706,7 @@
-
Diffuse
+
Color
R
G
@@ -723,47 +714,19 @@
-
Ambient
-
-
R
-
G
-
B
-
-
-
-
Specular
-
-
R
-
G
-
B
-
-
-
-
Constant Attenuation
+
Intensity
-
Linear Attenuation
-
- -
-
-
-
Quadratic Attenuation
-
- -
-
-
-
Exponent
+
Spot Light Exponent
-
Cutoff (degrees)
+
Spot Light Cutoff (degrees)
diff --git a/examples/html/style.css b/examples/html/style.css index 7177b8c8ba..baa159df74 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -189,6 +189,11 @@ input, textarea { font-size: 7.5pt; } +input:disabled, textarea:disabled { + background-color: rgb(102, 102, 102); + color: rgb(160, 160, 160); +} + #properties-list input[type=button] { cursor: pointer; background-color: rgb(51, 102, 102); @@ -199,6 +204,11 @@ input, textarea { color: rgb(204, 204, 204); } +#properties-list input[type=button]:disabled { + background-color: rgb(41, 82, 82); + color: rgb(160, 160, 160); +} + #properties-list .property { padding: 6pt 6pt; border-top: 0.75pt solid rgb(63, 63, 63); @@ -257,3 +267,11 @@ td { vertical-align: top; } + +#no-entities { + display: none; + font-size: 120%; + padding: 10pt; + font-weight: bold; + font-style: italic; +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5fe9e6f6b8..d824ca4505 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -82,6 +82,8 @@ #include #include +#include + #include "Application.h" #include "AudioClient.h" #include "InterfaceVersion.h" @@ -1859,35 +1861,6 @@ void Application::updateMouseRay() { } } -void Application::updateFaceshift() { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateFaceshift()"); - auto faceshift = DependencyManager::get(); - // Update faceshift - faceshift->update(); - - // Copy angular velocity if measured by faceshift, to the head - if (faceshift->isActive()) { - _myAvatar->getHead()->setAngularVelocity(faceshift->getHeadAngularVelocity()); - } -} - -void Application::updateVisage() { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateVisage()"); - - // Update Visage - DependencyManager::get()->update(); -} - -void Application::updateDDE() { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateDDE()"); - - // Update Cara - DependencyManager::get()->update(); -} - void Application::updateMyAvatarLookAtPosition() { PerformanceTimer perfTimer("lookAt"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); @@ -2067,12 +2040,13 @@ void Application::update(float deltaTime) { { PerformanceTimer perfTimer("devices"); DeviceTracker::updateAll(); - updateFaceshift(); - updateVisage(); + FaceTracker* tracker = getActiveFaceTracker(); + if (tracker) { + tracker->update(deltaTime); + } SixenseManager::getInstance().update(deltaTime); JoystickScriptingInterface::getInstance().update(); _prioVR.update(deltaTime); - } // Dispatch input events @@ -2491,7 +2465,8 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { glm::vec3 Application::getSunDirection() { // Sun direction is in fact just the location of the sun relative to the origin - return glm::normalize(_environment.getClosestData(_myCamera.getPosition()).getSunLocation(_myCamera.getPosition())); + auto skyStage = DependencyManager::get()->getSkyStage(); + return skyStage->getSunLight()->getDirection(); } void Application::updateShadowMap() { @@ -2501,7 +2476,7 @@ void Application::updateShadowMap() { glEnable(GL_DEPTH_TEST); glClear(GL_DEPTH_BUFFER_BIT); - glm::vec3 lightDirection = -getSunDirection(); + glm::vec3 lightDirection = getSunDirection(); glm::quat rotation = rotationBetween(IDENTITY_FRONT, lightDirection); glm::quat inverseRotation = glm::inverse(rotation); @@ -2874,7 +2849,9 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs { DependencyManager::get()->setAmbientLightMode(getRenderAmbientLight()); - DependencyManager::get()->setGlobalLight(-getSunDirection(), GLOBAL_LIGHT_COLOR, GLOBAL_LIGHT_INTENSITY); + auto skyStage = DependencyManager::get()->getSkyStage(); + DependencyManager::get()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity()); + PROFILE_RANGE("DeferredLighting"); PerformanceTimer perfTimer("lighting"); DependencyManager::get()->render(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 9cd19c8259..4a8e712d30 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -421,9 +421,6 @@ private: // Various helper functions called during update() void updateLOD(); void updateMouseRay(); - void updateFaceshift(); - void updateVisage(); - void updateDDE(); void updateMyAvatarLookAtPosition(); void updateThreads(float deltaTime); void updateMetavoxels(float deltaTime); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index af02d5b54f..3b209d8e2d 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -380,8 +380,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode, bool foreach (const AvatarManager::LocalLight& light, DependencyManager::get()->getLocalLights()) { glm::vec3 direction = orientation * light.direction; DependencyManager::get()->addSpotLight(position - direction * distance, - distance * 2.0f, glm::vec3(), light.color, light.color, 1.0f, 0.5f, 0.0f, direction, - LIGHT_EXPONENT, LIGHT_CUTOFF); + distance * 2.0f, light.color, 0.5f, orientation, LIGHT_EXPONENT, LIGHT_CUTOFF); } } diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 084ddb4fc7..311356daf3 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -42,7 +42,6 @@ Head::Head(Avatar* owningAvatar) : _mouth2(0.0f), _mouth3(0.0f), _mouth4(0.0f), - _angularVelocity(0,0,0), _renderLookatVectors(false), _saccade(0.0f, 0.0f, 0.0f), _saccadeTarget(0.0f, 0.0f, 0.0f), diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 4454f063b4..5465d74172 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -55,9 +55,6 @@ public: /// \return orientationBody * orientationBasePitch glm::quat getCameraOrientation () const; - - const glm::vec3& getAngularVelocity() const { return _angularVelocity; } - void setAngularVelocity(glm::vec3 angularVelocity) { _angularVelocity = angularVelocity; } void setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition); glm::vec3 getCorrectedLookAtPosition(); @@ -130,7 +127,6 @@ private: float _mouth2; float _mouth3; float _mouth4; - glm::vec3 _angularVelocity; bool _renderLookatVectors; glm::vec3 _saccade; glm::vec3 _saccadeTarget; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c3ecd23a3f..2c6600c457 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -892,13 +892,7 @@ void MyAvatar::updateLookAtTargetAvatar() { _lookAtTargetAvatar.clear(); _targetAvatarPosition = glm::vec3(0.0f); - glm::quat faceRotation = Application::getInstance()->getViewFrustum()->getOrientation(); - FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); - if (tracker) { - // If faceshift or other face tracker in use, add on the actual angle of the head - faceRotation *= tracker->getHeadRotation(); - } - glm::vec3 lookForward = faceRotation * IDENTITY_FRONT; + glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT; glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); float smallestAngleTo = glm::radians(Application::getInstance()->getCamera()->getFieldOfView()) / 2.0f; @@ -1166,15 +1160,8 @@ void MyAvatar::updateOrientation(float deltaTime) { pitch *= DEGREES_PER_RADIAN; roll *= DEGREES_PER_RADIAN; - // Record the angular velocity - Head* head = getHead(); - if (deltaTime > 0.0f) { - glm::vec3 angularVelocity(pitch - head->getBasePitch(), yaw - head->getBaseYaw(), roll - head->getBaseRoll()); - angularVelocity *= 1.0f / deltaTime; - head->setAngularVelocity(angularVelocity); - } - //Invert yaw and roll when in mirror mode + Head* head = getHead(); if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) { head->setBaseYaw(-yaw); head->setBasePitch(pitch); diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index 87a180bd1e..ed0e4057ef 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -50,6 +50,8 @@ DdeFaceTracker::DdeFaceTracker() : } DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 port) : + _host(host), + _port(port), _lastReceiveTimestamp(0), _reset(false), _leftBlinkIndex(0), // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes @@ -63,9 +65,7 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 port) : _browUpRightIndex(18), _mouthSmileLeftIndex(28), _mouthSmileRightIndex(29), - _jawOpenIndex(21), - _host(host), - _port(port) + _jawOpenIndex(21) { _blendshapeCoefficients.resize(NUM_EXPRESSION); @@ -80,18 +80,6 @@ DdeFaceTracker::~DdeFaceTracker() { } } -void DdeFaceTracker::init() { - -} - -void DdeFaceTracker::reset() { - _reset = true; -} - -void DdeFaceTracker::update() { - -} - void DdeFaceTracker::setEnabled(bool enabled) { // isOpen() does not work as one might expect on QUdpSocket; don't test isOpen() before closing socket. _udpSocket.close(); diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index bd5d066732..88528370a2 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -23,12 +23,10 @@ class DdeFaceTracker : public FaceTracker, public Dependency { SINGLETON_DEPENDENCY public: - //initialization - void init(); - void reset(); - void update(); - - bool isActive() const; + virtual void reset() { _reset = true; } + + virtual bool isActive() const; + virtual bool isTracking() const { return isActive(); } float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); } float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); } diff --git a/interface/src/devices/FaceTracker.cpp b/interface/src/devices/FaceTracker.cpp index 52fe04de77..d3e0600057 100644 --- a/interface/src/devices/FaceTracker.cpp +++ b/interface/src/devices/FaceTracker.cpp @@ -11,7 +11,41 @@ #include "FaceTracker.h" -FaceTracker::FaceTracker() : - _estimatedEyePitch(0.0f), - _estimatedEyeYaw(0.0f) { +inline float FaceTracker::getBlendshapeCoefficient(int index) const { + return isValidBlendshapeIndex(index) ? _blendshapeCoefficients[index] : 0.0f; } + +float FaceTracker::getFadeCoefficient() const { + return _fadeCoefficient; +} + +const glm::vec3 FaceTracker::getHeadTranslation() const { + return glm::mix(glm::vec3(0.0f), _headTranslation, getFadeCoefficient()); +} + +const glm::quat FaceTracker::getHeadRotation() const { + return glm::mix(glm::quat(), _headRotation, getFadeCoefficient()); +} + +void FaceTracker::update(float deltaTime) { + // Based on exponential distributions: http://en.wikipedia.org/wiki/Exponential_distribution + static const float EPSILON = 0.02f; // MUST BE < 1.0f + static const float INVERSE_AT_EPSILON = -std::log(EPSILON); // So that f(1.0f) = EPSILON ~ 0.0f + static const float RELAXATION_TIME = 0.8f; // sec + + if (isTracking()) { + if (_relaxationStatus == 1.0f) { + _fadeCoefficient = 1.0f; + return; + } + _relaxationStatus = glm::clamp(_relaxationStatus + deltaTime / RELAXATION_TIME, 0.0f, 1.0f); + _fadeCoefficient = 1.0f - std::exp(-_relaxationStatus * INVERSE_AT_EPSILON); + } else { + if (_relaxationStatus == 0.0f) { + _fadeCoefficient = 0.0f; + return; + } + _relaxationStatus = glm::clamp(_relaxationStatus - deltaTime / RELAXATION_TIME, 0.0f, 1.0f); + _fadeCoefficient = std::exp(-(1.0f - _relaxationStatus) * INVERSE_AT_EPSILON); + } +} \ No newline at end of file diff --git a/interface/src/devices/FaceTracker.h b/interface/src/devices/FaceTracker.h index 7c367b7899..1ec0468dec 100644 --- a/interface/src/devices/FaceTracker.h +++ b/interface/src/devices/FaceTracker.h @@ -18,29 +18,40 @@ #include #include -/// Base class for face trackers (Faceshift, Visage). +/// Base class for face trackers (Faceshift, Visage, DDE). class FaceTracker : public QObject { Q_OBJECT public: - FaceTracker(); - virtual ~FaceTracker() {} + virtual bool isActive() const { return false; } + virtual bool isTracking() const { return false; } - const glm::vec3& getHeadTranslation() const { return _headTranslation; } - const glm::quat& getHeadRotation() const { return _headRotation; } + virtual void init() {} + virtual void update(float deltaTime); + virtual void reset() {} + + float getFadeCoefficient() const; + + const glm::vec3 getHeadTranslation() const; + const glm::quat getHeadRotation() const; float getEstimatedEyePitch() const { return _estimatedEyePitch; } float getEstimatedEyeYaw() const { return _estimatedEyeYaw; } + int getNumBlendshapes() const { return _blendshapeCoefficients.size(); } + bool isValidBlendshapeIndex(int index) const { return index >= 0 && index < getNumBlendshapes(); } const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } + float getBlendshapeCoefficient(int index) const; protected: - - glm::vec3 _headTranslation; - glm::quat _headRotation; - float _estimatedEyePitch; - float _estimatedEyeYaw; + glm::vec3 _headTranslation = glm::vec3(0.0f); + glm::quat _headRotation = glm::quat(); + float _estimatedEyePitch = 0.0f; + float _estimatedEyeYaw = 0.0f; QVector _blendshapeCoefficients; + + float _relaxationStatus = 0.0f; // Between 0.0f and 1.0f + float _fadeCoefficient = 0.0f; // Between 0.0f and 1.0f }; #endif // hifi_FaceTracker_h diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp index 2f541c1c4e..a2bb4e74a9 100644 --- a/interface/src/devices/Faceshift.cpp +++ b/interface/src/devices/Faceshift.cpp @@ -25,37 +25,11 @@ using namespace fs; using namespace std; +const QString DEFAULT_FACESHIFT_HOSTNAME = "localhost"; const quint16 FACESHIFT_PORT = 33433; -float STARTING_FACESHIFT_FRAME_TIME = 0.033f; +const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f; Faceshift::Faceshift() : - _tcpEnabled(true), - _tcpRetryCount(0), - _lastTrackingStateReceived(0), - _averageFrameTime(STARTING_FACESHIFT_FRAME_TIME), - _headAngularVelocity(0), - _headLinearVelocity(0), - _lastHeadTranslation(0), - _filteredHeadTranslation(0), - _eyeGazeLeftPitch(0.0f), - _eyeGazeLeftYaw(0.0f), - _eyeGazeRightPitch(0.0f), - _eyeGazeRightYaw(0.0f), - _leftBlinkIndex(0), // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes - _rightBlinkIndex(1), - _leftEyeOpenIndex(8), - _rightEyeOpenIndex(9), - _browDownLeftIndex(14), - _browDownRightIndex(15), - _browUpCenterIndex(16), - _browUpLeftIndex(17), - _browUpRightIndex(18), - _mouthSmileLeftIndex(28), - _mouthSmileRightIndex(29), - _jawOpenIndex(21), - _longTermAverageEyePitch(0.0f), - _longTermAverageEyeYaw(0.0f), - _longTermAverageInitialized(false), _eyeDeflection("faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION), _hostname("faceshiftHostname", DEFAULT_FACESHIFT_HOSTNAME) { @@ -71,31 +45,17 @@ Faceshift::Faceshift() : #endif } +#ifdef HAVE_FACESHIFT void Faceshift::init() { -#ifdef HAVE_FACESHIFT setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift)); -#endif } -bool Faceshift::isConnectedOrConnecting() const { - return _tcpSocket.state() == QAbstractSocket::ConnectedState || - (_tcpRetryCount == 0 && _tcpSocket.state() != QAbstractSocket::UnconnectedState); -} - -bool Faceshift::isActive() const { -#ifdef HAVE_FACESHIFT - const quint64 ACTIVE_TIMEOUT_USECS = 1000000; - return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS; -#else - return false; -#endif -} - -void Faceshift::update() { +void Faceshift::update(float deltaTime) { if (!isActive()) { return; } - PerformanceTimer perfTimer("faceshift"); + FaceTracker::update(deltaTime); + // get the euler angles relative to the window glm::vec3 eulers = glm::degrees(safeEulerAngles(_headRotation * glm::quat(glm::radians(glm::vec3( (_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f))))); @@ -116,14 +76,28 @@ void Faceshift::update() { } void Faceshift::reset() { -#ifdef HAVE_FACESHIFT if (_tcpSocket.state() == QAbstractSocket::ConnectedState) { string message; fsBinaryStream::encode_message(message, fsMsgCalibrateNeutral()); send(message); } _longTermAverageInitialized = false; +} + +bool Faceshift::isActive() const { + const quint64 ACTIVE_TIMEOUT_USECS = 1000000; + return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS; +} + +bool Faceshift::isTracking() const { + return isActive() && _tracking; +} #endif + + +bool Faceshift::isConnectedOrConnecting() const { + return _tcpSocket.state() == QAbstractSocket::ConnectedState || + (_tcpRetryCount == 0 && _tcpSocket.state() != QAbstractSocket::UnconnectedState); } void Faceshift::updateFakeCoefficients(float leftBlink, float rightBlink, float browUp, @@ -148,12 +122,13 @@ void Faceshift::updateFakeCoefficients(float leftBlink, float rightBlink, float } void Faceshift::setTCPEnabled(bool enabled) { +#ifdef HAVE_FACESHIFT if ((_tcpEnabled = enabled)) { connectSocket(); - } else { _tcpSocket.disconnectFromHost(); } +#endif } void Faceshift::connectSocket() { @@ -202,10 +177,6 @@ void Faceshift::readFromSocket() { receive(_tcpSocket.readAll()); } -float Faceshift::getBlendshapeCoefficient(int index) const { - return (index >= 0 && index < (int)_blendshapeCoefficients.size()) ? _blendshapeCoefficients[index] : 0.0f; -} - void Faceshift::send(const std::string& message) { _tcpSocket.write(message.data(), message.size()); } diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h index 0cef3d3e13..f224448b8e 100644 --- a/interface/src/devices/Faceshift.h +++ b/interface/src/devices/Faceshift.h @@ -24,8 +24,7 @@ #include "FaceTracker.h" -const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f; -const QString DEFAULT_FACESHIFT_HOSTNAME = "localhost"; +const float STARTING_FACESHIFT_FRAME_TIME = 0.033f; /// Handles interaction with the Faceshift software, which provides head position/orientation and facial features. class Faceshift : public FaceTracker, public Dependency { @@ -33,11 +32,17 @@ class Faceshift : public FaceTracker, public Dependency { SINGLETON_DEPENDENCY public: - void init(); +#ifdef HAVE_FACESHIFT + // If we don't have faceshift, use the base class' methods + virtual void init(); + virtual void update(float deltaTime); + virtual void reset(); - bool isConnectedOrConnecting() const; + virtual bool isActive() const; + virtual bool isTracking() const; +#endif - bool isActive() const; + bool isConnectedOrConnecting() const; const glm::vec3& getHeadAngularVelocity() const { return _headAngularVelocity; } @@ -68,9 +73,6 @@ public: QString getHostname() { return _hostname.get(); } void setHostname(const QString& hostname); - - void update(); - void reset(); void updateFakeCoefficients(float leftBlink, float rightBlink, @@ -82,15 +84,12 @@ public: QVector& coefficients) const; signals: - void connectionStateChanged(); public slots: - void setTCPEnabled(bool enabled); private slots: - void connectSocket(); void noteConnected(); void noteError(QAbstractSocket::SocketError error); @@ -101,8 +100,6 @@ private: Faceshift(); virtual ~Faceshift() {} - float getBlendshapeCoefficient(int index) const; - void send(const std::string& message); void receive(const QByteArray& buffer); @@ -113,48 +110,48 @@ private: fs::fsBinaryStream _stream; #endif - bool _tcpEnabled; - int _tcpRetryCount; - bool _tracking; - quint64 _lastTrackingStateReceived; - float _averageFrameTime; + bool _tcpEnabled = true; + int _tcpRetryCount = 0; + bool _tracking = false; + quint64 _lastTrackingStateReceived = 0; + float _averageFrameTime = STARTING_FACESHIFT_FRAME_TIME; - glm::vec3 _headAngularVelocity; - glm::vec3 _headLinearVelocity; - glm::vec3 _lastHeadTranslation; - glm::vec3 _filteredHeadTranslation; + glm::vec3 _headAngularVelocity = glm::vec3(0.0f); + glm::vec3 _headLinearVelocity = glm::vec3(0.0f); + glm::vec3 _lastHeadTranslation = glm::vec3(0.0f); + glm::vec3 _filteredHeadTranslation = glm::vec3(0.0f); // degrees - float _eyeGazeLeftPitch; - float _eyeGazeLeftYaw; - float _eyeGazeRightPitch; - float _eyeGazeRightYaw; - - int _leftBlinkIndex; - int _rightBlinkIndex; - int _leftEyeOpenIndex; - int _rightEyeOpenIndex; - - // Brows - int _browDownLeftIndex; - int _browDownRightIndex; - int _browUpCenterIndex; - int _browUpLeftIndex; - int _browUpRightIndex; - - int _mouthSmileLeftIndex; - int _mouthSmileRightIndex; - - int _jawOpenIndex; + float _eyeGazeLeftPitch = 0.0f; + float _eyeGazeLeftYaw = 0.0f; + float _eyeGazeRightPitch = 0.0f; + float _eyeGazeRightYaw = 0.0f; // degrees - float _longTermAverageEyePitch; - float _longTermAverageEyeYaw; - bool _longTermAverageInitialized; + float _longTermAverageEyePitch = 0.0f; + float _longTermAverageEyeYaw = 0.0f; + bool _longTermAverageInitialized = false; Setting::Handle _eyeDeflection; Setting::Handle _hostname; + // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes + int _leftBlinkIndex = 0; + int _rightBlinkIndex = 1; + int _leftEyeOpenIndex = 8; + int _rightEyeOpenIndex = 9; + + // Brows + int _browDownLeftIndex = 14; + int _browDownRightIndex = 15; + int _browUpCenterIndex = 16; + int _browUpLeftIndex = 17; + int _browUpRightIndex = 18; + + int _mouthSmileLeftIndex = 28; + int _mouthSmileRightIndex = 29; + + int _jawOpenIndex = 21; }; #endif // hifi_Faceshift_h diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index 39bda83e61..010b872bc6 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -41,7 +41,6 @@ const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.7f); Visage::Visage() : _enabled(false), - _active(false), _headOrigin(DEFAULT_HEAD_ORIGIN) { #ifdef HAVE_VISAGE @@ -119,20 +118,20 @@ static const QMultiHash >& getActionUnitNameMap() } #endif -const float TRANSLATION_SCALE = 20.0f; +#ifdef HAVE_VISAGE +const float TRANSLATION_SCALE = 20.0f; void Visage::init() { connect(DependencyManager::get().data(), SIGNAL(connectionStateChanged()), SLOT(updateEnabled())); updateEnabled(); } -void Visage::update() { -#ifdef HAVE_VISAGE - _active = (_tracker->getTrackingData(_data) == TRACK_STAT_OK); - if (!_active) { +void Visage::update(float deltaTime) { + if (!isActive()) { return; } - PerformanceTimer perfTimer("visage"); + FaceTracker::update(deltaTime); + _headRotation = glm::quat(glm::vec3(-_data->faceRotation[0], -_data->faceRotation[1], _data->faceRotation[2])); _headTranslation = (glm::vec3(_data->faceTranslation[0], _data->faceTranslation[1], _data->faceTranslation[2]) - _headOrigin) * TRANSLATION_SCALE; @@ -164,12 +163,12 @@ void Visage::update() { } _blendshapeCoefficients[leftEyeBlinkIndex] = 1.0f - _data->eyeClosure[1]; _blendshapeCoefficients[rightEyeBlinkIndex] = 1.0f - _data->eyeClosure[0]; -#endif } void Visage::reset() { _headOrigin += _headTranslation / TRANSLATION_SCALE; } +#endif void Visage::updateEnabled() { setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Visage) && diff --git a/interface/src/devices/Visage.h b/interface/src/devices/Visage.h index 690a9f3066..3ff1ea8c27 100644 --- a/interface/src/devices/Visage.h +++ b/interface/src/devices/Visage.h @@ -31,15 +31,16 @@ class Visage : public FaceTracker, public Dependency { SINGLETON_DEPENDENCY public: - void init(); +#ifdef HAVE_VISAGE + virtual void init(); + virtual void update(float deltaTime); + virtual void reset(); - bool isActive() const { return _active; } - - void update(); - void reset(); + virtual bool isActive() const { return _tracker->getTrackingData(_data) == TRACK_STAT_OK; } + virtual bool isTracking() const { return isActive(); } +#endif public slots: - void updateEnabled(); private: @@ -55,7 +56,6 @@ private: void setEnabled(bool enabled); bool _enabled; - bool _active; glm::vec3 _headOrigin; }; diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index 1e0a9b6fc7..7f630f1c8c 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -36,31 +36,18 @@ void RenderableLightEntityItem::render(RenderArgs* args) { float diffuseG = getDiffuseColor()[GREEN_INDEX] / MAX_COLOR; float diffuseB = getDiffuseColor()[BLUE_INDEX] / MAX_COLOR; - float ambientR = getAmbientColor()[RED_INDEX] / MAX_COLOR; - float ambientG = getAmbientColor()[GREEN_INDEX] / MAX_COLOR; - float ambientB = getAmbientColor()[BLUE_INDEX] / MAX_COLOR; + glm::vec3 color = glm::vec3(diffuseR, diffuseG, diffuseB); - float specularR = getSpecularColor()[RED_INDEX] / MAX_COLOR; - float specularG = getSpecularColor()[GREEN_INDEX] / MAX_COLOR; - float specularB = getSpecularColor()[BLUE_INDEX] / MAX_COLOR; - - glm::vec3 ambient = glm::vec3(ambientR, ambientG, ambientB); - glm::vec3 diffuse = glm::vec3(diffuseR, diffuseG, diffuseB); - glm::vec3 specular = glm::vec3(specularR, specularG, specularB); - glm::vec3 direction = IDENTITY_FRONT * rotation; - float constantAttenuation = getConstantAttenuation(); - float linearAttenuation = getLinearAttenuation(); - float quadraticAttenuation = getQuadraticAttenuation(); + float intensity = getConstantAttenuation(); float exponent = getExponent(); float cutoff = glm::radians(getCutoff()); if (_isSpotlight) { - DependencyManager::get()->addSpotLight(position, largestDiameter / 2.0f, - ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation, - direction, exponent, cutoff); + DependencyManager::get()->addSpotLight(position, largestDiameter / 2.0f, + color, intensity, rotation, exponent, cutoff); } else { - DependencyManager::get()->addPointLight(position, largestDiameter / 2.0f, - ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation); + DependencyManager::get()->addPointLight(position, largestDiameter / 2.0f, + color, intensity); } #ifdef WANT_DEBUG diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index f16a6287f8..49d139d727 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -47,7 +47,7 @@ public: static void syncGPUObject(const Buffer& buffer); static GLuint getBufferID(const Buffer& buffer); - class GLTexture { + class GLTexture : public GPUObject { public: Stamp _storageStamp; Stamp _contentStamp; diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp new file mode 100644 index 0000000000..d867056e8a --- /dev/null +++ b/libraries/model/src/model/Stage.cpp @@ -0,0 +1,209 @@ +// +// Stage.cpp +// libraries/model/src/model +// +// Created by Sam Gateau on 2/24/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 +// +#include "Stage.h" + +#include +#include + +using namespace model; + + +void EarthSunModel::updateAll() const { + updateWorldToSurface(); + updateSurfaceToEye(); + updateSun(); +} + +Mat4d EarthSunModel::evalWorldToGeoLocationMat(double longitude, double latitude, double absAltitude, double scale) { + // Longitude is along Z axis but - from east to west + Mat4d rotLon = glm::rotate(glm::radians(longitude), Vec3d(0.0, 0.0, 1.0)); + + // latitude is along X axis + from south to north + Mat4d rotLat = glm::rotate(-glm::radians(latitude), Vec3d(1.0, 0.0, 0.0)); + + // translation is movin to the earth surface + altiture at the radius along Y axis + Mat4d surfaceT = glm::translate(Vec3d(0.0, -absAltitude, 0.0)); + + // Mat4d worldScale = glm::scale(Vec3d(scale)); + + Mat4d worldToGeoLocMat = surfaceT * rotLat * rotLon; + + return worldToGeoLocMat; +} + +void EarthSunModel::updateWorldToSurface() const { + // Check if the final position is too close to the earth center ? + double absAltitude = _earthRadius + _altitude; + if ( absAltitude < 0.01) { + absAltitude = 0.01; + } + + // Final world to local Frame + _worldToSurfaceMat = evalWorldToGeoLocationMat(_longitude, _latitude, absAltitude, _scale); + // and the inverse + _surfaceToWorldMat = glm::inverse(_worldToSurfaceMat); + + _surfacePos = Vec3d(_surfaceToWorldMat * Vec4d(0.0, 0.0, 0.0, 1.0)); +} + +void EarthSunModel::updateSurfaceToEye() const { + _surfaceToEyeMat = glm::inverse(_eyeToSurfaceMat); + _worldToEyeMat = _surfaceToEyeMat * _worldToSurfaceMat; + _eyeToWorldMat = _surfaceToWorldMat * _eyeToSurfaceMat; + _eyePos = Vec3d(_eyeToWorldMat * Vec4d(0.0, 0.0, 0.0, 1.0) ); + _eyeDir = Vec3d(_eyeToWorldMat * Vec4d(0.0, 0.0, -1.0, 0.0) ); +} + +void EarthSunModel::updateSun() const { + // Longitude is along Y axis but - from east to west + Mat4d rotSunLon; + + Mat4d rotSun = evalWorldToGeoLocationMat(_sunLongitude, _sunLatitude, _earthRadius, _scale); + rotSun = glm::inverse(rotSun); + + _sunDir = Vec3d(rotSun * Vec4d(0.0, 1.0, 0.0, 0.0)); + + // sun direction is looking up toward Y axis at the specified sun lat, long + Vec3d lssd = Vec3d(_worldToSurfaceMat * Vec4d(_sunDir.x, _sunDir.y, _sunDir.z, 0.0)); + + // apply surface rotation offset + glm::dquat dSurfOrient(_surfaceOrientation); + lssd = glm::rotate(dSurfOrient, lssd); + + _surfaceSunDir = glm::normalize(Vec3(lssd.x, lssd.y, lssd.z)); +} + +void EarthSunModel::setSurfaceOrientation(const Quat& orientation) { + _surfaceOrientation = orientation; + invalidate(); +} + +double moduloRange(double val, double minVal, double maxVal) { + double range = maxVal - minVal; + double rval = (val - minVal) / range; + double intval; + return modf(rval, &intval) * range + minVal; +} + +const float MAX_LONGITUDE = 180.0f; +const float MAX_LATITUDE = 90.0f; + +float validateLongitude(float lon) { + return moduloRange(lon, -MAX_LONGITUDE, MAX_LONGITUDE); +} + +float validateLatitude(float lat) { + return moduloRange(lat, -MAX_LATITUDE, MAX_LATITUDE); +} + +float validateAltitude(float altitude) { + const float MIN_ALTITUDE = -1000.0f; + const float MAX_ALTITUDE = 100000.0f; + return std::min(std::max(altitude, MIN_ALTITUDE), MAX_ALTITUDE); +} + +void EarthSunModel::setLatitude(float lat) { + _latitude = validateLatitude(lat); + invalidate(); +} +void EarthSunModel::setLongitude(float lon) { + _longitude = validateLongitude(lon); + invalidate(); +} +void EarthSunModel::setAltitude(float altitude) { + _altitude = validateAltitude(altitude); + invalidate(); +} + +void EarthSunModel::setSunLatitude(float lat) { + _sunLatitude = validateLatitude(lat); + invalidate(); +} +void EarthSunModel::setSunLongitude(float lon) { + _sunLongitude = validateLongitude(lon); + invalidate(); +} + +const int NUM_DAYS_PER_YEAR = 365; +const float NUM_HOURS_PER_DAY = 24.0f; +const float NUM_HOURS_PER_HALF_DAY = NUM_HOURS_PER_DAY * 0.5f; + +SunSkyStage::SunSkyStage() : + _sunLight(new Light()) +{ + _sunLight->setType(Light::SUN); + + setSunIntensity(1.0f); + setSunColor(Vec3(1.0f, 1.0f, 1.0f)); + + // Default origin location is a special place in the world... + setOriginLocation(122.407f, 37.777f, 0.03f); + // Noun + setDayTime(12.0f); + // Begining of march + setYearTime(60.0f); +} + +SunSkyStage::~SunSkyStage() { +} + +void SunSkyStage::setDayTime(float hour) { + _dayTime = moduloRange(hour, 0.f, NUM_HOURS_PER_DAY); + invalidate(); +} + +void SunSkyStage::setYearTime(unsigned int day) { + _yearTime = day % NUM_DAYS_PER_YEAR; + invalidate(); +} + +void SunSkyStage::setOriginOrientation(const Quat& orientation) { + _earthSunModel.setSurfaceOrientation(orientation); + invalidate(); +} + +void SunSkyStage::setOriginLocation(float longitude, float latitude, float altitude) { + _earthSunModel.setLongitude(longitude); + _earthSunModel.setLatitude(latitude); + _earthSunModel.setAltitude(altitude); + invalidate(); +} + +void SunSkyStage::setSunColor(const Vec3& color) { + _sunLight->setColor(color); +} +void SunSkyStage::setSunIntensity(float intensity) { + _sunLight->setIntensity(intensity); +} + +// THe sun declinaison calculus is taken from https://en.wikipedia.org/wiki/Position_of_the_Sun +double evalSunDeclinaison(double dayNumber) { + return -(23.0 + 44.0/60.0)*cos(glm::radians((360.0/365.0)*(dayNumber + 10.0))); +} + +void SunSkyStage::updateGraphicsObject() const { + // Always update the sunLongitude based on the current dayTime and the current origin + // The day time is supposed to be local at the origin + double signedNormalizedDayTime = (_dayTime - NUM_HOURS_PER_HALF_DAY) / NUM_HOURS_PER_HALF_DAY; + double sunLongitude = _earthSunModel.getLongitude() + (MAX_LONGITUDE * signedNormalizedDayTime); + _earthSunModel.setSunLongitude(sunLongitude); + + // And update the sunLAtitude as the declinaison depending of the time of the year + _earthSunModel.setSunLatitude(evalSunDeclinaison(_yearTime)); + + Vec3d sunLightDir = -_earthSunModel.getSurfaceSunDir(); + _sunLight->setDirection(Vec3(sunLightDir.x, sunLightDir.y, sunLightDir.z)); + + double originAlt = _earthSunModel.getAltitude(); + _sunLight->setPosition(Vec3(0.0f, originAlt, 0.0f)); + +} + diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h new file mode 100644 index 0000000000..762e2d9717 --- /dev/null +++ b/libraries/model/src/model/Stage.h @@ -0,0 +1,161 @@ +// +// Stage.h +// libraries/model/src/model +// +// Created by Sam Gateau on 2/24/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 +// +#ifndef hifi_model_Stage_h +#define hifi_model_Stage_h + +#include "Light.h" + +namespace model { + +typedef glm::dvec3 Vec3d; +typedef glm::dvec4 Vec4d; +typedef glm::dmat4 Mat4d; +typedef glm::mat4 Mat4; + +class EarthSunModel { +public: + + void setScale(float scale); + float getScale() const { return _scale; } + + void setLatitude(float lat); + float getLatitude() const { return _latitude; } + void setLongitude(float lon); + float getLongitude() const { return _longitude; } + void setAltitude(float altitude); + float getAltitude() const { return _altitude; } + + + void setSurfaceOrientation(const Quat& orientation); + const Quat& getSurfaceOrientation() const { valid(); return _surfaceOrientation; } + + const Vec3d& getSurfacePos() const { valid(); return _surfacePos; } + + const Mat4d& getSurfaceToWorldMat() const { valid(); return _surfaceToWorldMat; } + const Mat4d& getWoldToSurfaceMat() const { valid(); return _worldToSurfaceMat; } + + const Mat4d& getEyeToSurfaceMat() const { valid(); return _eyeToSurfaceMat; } + const Mat4d& getSurfaceToEyeMat() const { valid(); return _surfaceToEyeMat; } + + const Mat4d& getEyeToWorldMat() const { valid(); return _eyeToWorldMat; } + const Mat4d& getWorldToEyeMat() const { valid(); return _worldToEyeMat; } + + + //or set the surfaceToEye mat directly + void setEyeToSurfaceMat( const Mat4d& e2s); + + const Vec3d& getEyePos() const { valid(); return _eyePos; } + const Vec3d& getEyeDir() const { valid(); return _eyeDir; } + + void setSunLongitude(float lon); + float getSunLongitude() const { return _sunLongitude; } + + void setSunLatitude(float lat); + float getSunLatitude() const { return _sunLatitude; } + + const Vec3d& getWorldSunDir() const { valid(); return _sunDir; } + const Vec3d& getSurfaceSunDir() const { valid(); return _surfaceSunDir; } + + + EarthSunModel() { valid(); } + +protected: + double _scale = 1000.0; //Km + double _earthRadius = 6360.0; + + Quat _surfaceOrientation; + + double _longitude = 0.0; + double _latitude = 0.0; + double _altitude = 0.01; + mutable Vec3d _surfacePos; + mutable Mat4d _worldToSurfaceMat; + mutable Mat4d _surfaceToWorldMat; + void updateWorldToSurface() const; + + mutable Mat4d _surfaceToEyeMat; + mutable Mat4d _eyeToSurfaceMat; + mutable Vec3d _eyeDir; + mutable Vec3d _eyePos; + void updateSurfaceToEye() const; + + mutable Mat4d _worldToEyeMat; + mutable Mat4d _eyeToWorldMat; + + double _sunLongitude = 0.0; + double _sunLatitude = 0.0; + mutable Vec3d _sunDir; + mutable Vec3d _surfaceSunDir; + void updateSun() const; + + mutable bool _invalid = true; + void invalidate() const { _invalid = true; } + void valid() const { if (_invalid) { updateAll(); _invalid = false; } } + void updateAll() const; + + static Mat4d evalWorldToGeoLocationMat(double longitude, double latitude, double altitude, double scale); +}; + +// Sun sky stage generates the rendering primitives to display a scene realistically +// at the specified location and time around earth +class SunSkyStage { +public: + + SunSkyStage(); + ~SunSkyStage(); + + // time of the day (local to the position) expressed in decimal hour in the range [0.0, 24.0] + void setDayTime(float hour); + float getDayTime() const { return _dayTime; } + + // time of the year expressed in day in the range [0, 365] + void setYearTime(unsigned int day); + unsigned int getYearTime() const { return _yearTime; } + + // Origin orientation used to modify the cardinal axis alignement used. + // THe default is north along +Z axis and west along +X axis. this orientation gets added + // to the transform stack producing the sun light direction. + void setOriginOrientation(const Quat& orientation); + const Quat& getOriginOrientation() const { return _earthSunModel.getSurfaceOrientation(); } + + // Location used to define the sun & sky is a longitude and latitude [rad] and a earth surface altitude [km] + void setOriginLocation(float longitude, float latitude, float surfaceAltitude); + float getOriginLatitude() const { return _earthSunModel.getLatitude(); } + float getOriginLongitude() const { return _earthSunModel.getLongitude(); } + float getOriginSurfaceAltitude() const { return _earthSunModel.getAltitude(); } + + // Sun properties + void setSunColor(const Vec3& color); + const Vec3& getSunColor() const { return getSunLight()->getColor(); } + void setSunIntensity(float intensity); + float getSunIntensity() const { return getSunLight()->getIntensity(); } + + LightPointer getSunLight() const { valid(); return _sunLight; } + +protected: + LightPointer _sunLight; + + float _dayTime; + int _yearTime; + + mutable EarthSunModel _earthSunModel; + + mutable bool _invalid = true; + void invalidate() const { _invalid = true; } + void valid() const { if (_invalid) { updateGraphicsObject(); _invalid = false; } } + void updateGraphicsObject() const; +}; + +typedef QSharedPointer< SunSkyStage > SunSkyStagePointer; + +}; + +#endif diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 9bcdcbe9a4..f67abdca2b 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -76,7 +76,7 @@ const QString AddressManager::currentPath(bool withOrientation) const { pathString += "/" + orientationString; } else { qDebug() << "Cannot add orientation to path without a getter for position." - << "Call AdressManager::setOrientationGetter to pass a function that will return a glm::quat"; + << "Call AddressManager::setOrientationGetter to pass a function that will return a glm::quat"; } } @@ -84,7 +84,7 @@ const QString AddressManager::currentPath(bool withOrientation) const { return pathString; } else { qDebug() << "Cannot create address path without a getter for position." - << "Call AdressManager::setPositionGetter to pass a function that will return a const glm::vec3&"; + << "Call AddressManager::setPositionGetter to pass a function that will return a const glm::vec3&"; return QString(); } } diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index c8c454ff14..b240f0df9e 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -669,3 +669,41 @@ void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSock writeUnverifiedDatagram(iceRequestByteArray, iceServerSockAddr); } + +void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* parent) { + // save our local port to shared memory so that assignment client children know how to talk to this parent + QSharedMemory* sharedPortMem = new QSharedMemory(key, parent); + quint16 localPort = getNodeSocket().localPort(); + + // attempt to create the shared memory segment + if (sharedPortMem->create(sizeof(localPort)) || sharedPortMem->attach()) { + sharedPortMem->lock(); + memcpy(sharedPortMem->data(), &localPort, sizeof(localPort)); + sharedPortMem->unlock(); + + qDebug() << "Wrote local listening port" << localPort << "to shared memory at key" << key; + } else { + qWarning() << "Failed to create and attach to shared memory to share local port with assignment-client children."; + } +} + + +bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, QSharedMemory*& sharedMem, + quint16& localPort) { + if (!sharedMem) { + sharedMem = new QSharedMemory(key, this); + + if (!sharedMem->attach(QSharedMemory::ReadOnly)) { + qWarning() << "Could not attach to shared memory at key" << key; + } + } + + if (sharedMem->isAttached()) { + sharedMem->lock(); + memcpy(&localPort, sharedMem->data(), sizeof(localPort)); + sharedMem->unlock(); + return true; + } + + return false; +} diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 72aefdb2b3..afc98de169 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -49,6 +50,10 @@ const char STUN_SERVER_HOSTNAME[] = "stun.highfidelity.io"; const unsigned short STUN_SERVER_PORT = 3478; const QString DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY = "domain-server.local-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; class HifiSockAddr; @@ -168,6 +173,9 @@ public: return SharedNodePointer(); } + + void putLocalPortIntoSharedMemory(const QString key, QObject* parent); + bool getLocalServerPortFromSharedMemory(const QString key, QSharedMemory*& sharedMem, quint16& localPort); public slots: void reset(); diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 2bf792c6ee..2a38799707 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -149,7 +149,12 @@ QDataStream& operator>>(QDataStream& in, Node& node) { } QDebug operator<<(QDebug debug, const Node &node) { - debug.nospace() << NodeType::getNodeTypeName(node.getType()) << " (" << node.getType() << ")"; + debug.nospace() << NodeType::getNodeTypeName(node.getType()); + if (node.getType() == NodeType::Unassigned) { + debug.nospace() << " (1)"; + } else { + debug.nospace() << " (" << node.getType() << ")"; + } debug << " " << node.getUUID().toString().toLocal8Bit().constData() << " "; debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket(); return debug.nospace(); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index f70c2d9b3c..e63f230f6e 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -62,13 +62,17 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset); } -qint64 NodeList::sendStatsToDomainServer(const QJsonObject& statsObject) { +qint64 NodeList::sendStats(const QJsonObject& statsObject, HifiSockAddr destination) { QByteArray statsPacket = byteArrayWithPopulatedHeader(PacketTypeNodeJsonStats); QDataStream statsPacketStream(&statsPacket, QIODevice::Append); statsPacketStream << statsObject.toVariantMap(); - return writeUnverifiedDatagram(statsPacket, _domainHandler.getSockAddr()); + return writeUnverifiedDatagram(statsPacket, destination); +} + +qint64 NodeList::sendStatsToDomainServer(const QJsonObject& statsObject) { + return sendStats(statsObject, _domainHandler.getSockAddr()); } void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode) { diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 907fccfcac..1c6de4bb6c 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -47,6 +47,7 @@ public: NodeType_t getOwnerType() const { return _ownerType; } void setOwnerType(NodeType_t ownerType) { _ownerType = ownerType; } + qint64 sendStats(const QJsonObject& statsObject, HifiSockAddr destination); qint64 sendStatsToDomainServer(const QJsonObject& statsObject); int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; } diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 2eee540fff..db97e216ee 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -70,6 +70,8 @@ PacketVersion versionForPacketType(PacketType type) { return 2; case PacketTypeOctreeStats: return 1; + case PacketTypeStopNode: + return 1; case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: return VERSION_MODEL_ENTITIES_SUPPORT_SHAPE_TYPE; @@ -124,6 +126,7 @@ QString nameForPacketType(PacketType type) { PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityErase); PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityAddResponse); PACKET_TYPE_NAME_LOOKUP(PacketTypeOctreeDataNack); + PACKET_TYPE_NAME_LOOKUP(PacketTypeStopNode); PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioEnvironment); PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityEditNack); PACKET_TYPE_NAME_LOOKUP(PacketTypeSignedTransactionPayment); diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index e593955b51..32f33c01d9 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -67,7 +67,7 @@ enum PacketType { PacketTypeEntityErase, PacketTypeEntityAddResponse, PacketTypeOctreeDataNack, // 45 - UNUSED_10, + PacketTypeStopNode, PacketTypeAudioEnvironment, PacketTypeEntityEditNack, PacketTypeSignedTransactionPayment, @@ -86,7 +86,7 @@ const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeNodeJsonStats << PacketTypeEntityQuery << PacketTypeOctreeDataNack << PacketTypeEntityEditNack << PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse - << PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply; + << PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode; const int NUM_BYTES_MD5_HASH = 16; const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index 966e69f7ff..3039c92d67 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -97,9 +97,7 @@ vec3 evalLightmappedColor(float shadowAttenuation, vec3 normal, vec3 diffuse, ve Light light = getLight(); vec3 fragNormal = vec3(invViewMat * vec4(normal, 0.0)); - float diffuseDot = dot(fragNormal, -getLightDirection(light)); - // float diffuseDot = dot(normal, getLightDirection(light)); // need to catch normals perpendicular to the projection plane hence the magic number for the threshold // it should be just 0, but we have innacurracy so we need to overshoot diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 1f5d0ce4c3..c8ad3711d3 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -126,15 +126,13 @@ void DeferredLightingEffect::renderSolidCone(float base, float height, int slice releaseSimpleProgram(); } -void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& ambient, - const glm::vec3& diffuse, const glm::vec3& specular, float constantAttenuation, - float linearAttenuation, float quadraticAttenuation) { - addSpotLight(position, radius, ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation); +void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& color, + float intensity) { + addSpotLight(position, radius, color, intensity); } -void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& ambient, - const glm::vec3& diffuse, const glm::vec3& specular, float constantAttenuation, float linearAttenuation, - float quadraticAttenuation, const glm::vec3& direction, float exponent, float cutoff) { +void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color, + float intensity, const glm::quat& orientation, float exponent, float cutoff) { int lightID = _pointLights.size() + _spotLights.size() + _globalLights.size(); if (lightID >= _allocatedLights.size()) { @@ -144,8 +142,8 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu lp->setPosition(position); lp->setMaximumRadius(radius); - lp->setColor(diffuse); - lp->setIntensity(1.0f); + lp->setColor(color); + lp->setIntensity(intensity); //lp->setShowContour(quadraticAttenuation); if (exponent == 0.0f && cutoff == PI) { @@ -153,7 +151,7 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu _pointLights.push_back(lightID); } else { - lp->setDirection(direction); + lp->setOrientation(orientation); lp->setSpotAngle(cutoff); lp->setSpotExponent(exponent); lp->setType(model::Light::SPOT); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index eeb92f19c7..c375f3eb52 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -57,15 +57,12 @@ public: void renderSolidCone(float base, float height, int slices, int stacks); /// Adds a point light to render for the current frame. - void addPointLight(const glm::vec3& position, float radius, const glm::vec3& ambient = glm::vec3(0.0f, 0.0f, 0.0f), - const glm::vec3& diffuse = glm::vec3(1.0f, 1.0f, 1.0f), const glm::vec3& specular = glm::vec3(1.0f, 1.0f, 1.0f), - float constantAttenuation = 1.0f, float linearAttenuation = 0.0f, float quadraticAttenuation = 0.0f); + void addPointLight(const glm::vec3& position, float radius, const glm::vec3& color = glm::vec3(0.0f, 0.0f, 0.0f), + float intensity = 0.5f); /// Adds a spot light to render for the current frame. - void addSpotLight(const glm::vec3& position, float radius, const glm::vec3& ambient = glm::vec3(0.0f, 0.0f, 0.0f), - const glm::vec3& diffuse = glm::vec3(1.0f, 1.0f, 1.0f), const glm::vec3& specular = glm::vec3(1.0f, 1.0f, 1.0f), - float constantAttenuation = 1.0f, float linearAttenuation = 0.0f, float quadraticAttenuation = 0.0f, - const glm::vec3& direction = glm::vec3(0.0f, 0.0f, -1.0f), float exponent = 0.0f, float cutoff = PI); + void addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f), + float intensity = 0.5f, const glm::quat& orientation = glm::quat(), float exponent = 0.0f, float cutoff = PI); /// Adds an object to render after performing the deferred lighting for the current frame (e.g., a translucent object). void addPostLightingRenderable(PostLightingRenderable* renderable) { _postLightingRenderables.append(renderable); } diff --git a/libraries/script-engine/src/SceneScriptingInterface.cpp b/libraries/script-engine/src/SceneScriptingInterface.cpp new file mode 100644 index 0000000000..9cac521225 --- /dev/null +++ b/libraries/script-engine/src/SceneScriptingInterface.cpp @@ -0,0 +1,40 @@ +// +// SceneScriptingInterface.cpp +// interface/src/scripting +// +// Created by Sam Gateau on 2/24/15. +// 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 + +#include "SceneScriptingInterface.h" + +void SceneScriptingInterface::setStageOrientation(const glm::quat& orientation) { + _skyStage->setOriginOrientation(orientation); +} +void SceneScriptingInterface::setStageLocation(float longitude, float latitude, float altitude) { + _skyStage->setOriginLocation(longitude, latitude, altitude); +} + +void SceneScriptingInterface::setStageDayTime(float hour) { + _skyStage->setDayTime(hour); +} +void SceneScriptingInterface::setStageYearTime(int day) { + _skyStage->setYearTime(day); +} + +void SceneScriptingInterface::setSunColor(const glm::vec3& color) { + _skyStage->setSunColor(color); +} +void SceneScriptingInterface::setSunIntensity(float intensity) { + _skyStage->setSunIntensity(intensity); +} + + +model::SunSkyStagePointer SceneScriptingInterface::getSkyStage() const { + return _skyStage; +} \ No newline at end of file diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h new file mode 100644 index 0000000000..8ae9424c95 --- /dev/null +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -0,0 +1,43 @@ +// +// SceneScriptingInterface.h +// interface/src/scripting +// +// Created by Sam Gateau on 2/24/15. +// 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_SceneScriptingInterface_h +#define hifi_SceneScriptingInterface_h + +#include + +#include + +#include "model/Stage.h" + +class SceneScriptingInterface : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public: + Q_INVOKABLE void setStageOrientation(const glm::quat& orientation); + Q_INVOKABLE void setStageLocation(float longitude, float latitude, float altitude); + Q_INVOKABLE void setStageDayTime(float hour); + Q_INVOKABLE void setStageYearTime(int day); + + Q_INVOKABLE void setSunColor(const glm::vec3& color); + Q_INVOKABLE void setSunIntensity(float intensity); + + model::SunSkyStagePointer getSkyStage() const; + +protected: + SceneScriptingInterface() {}; + ~SceneScriptingInterface() {}; + + model::SunSkyStagePointer _skyStage = model::SunSkyStagePointer(new model::SunSkyStage()); +}; + +#endif // hifi_SceneScriptingInterface_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index d201c1568c..3bdf9a9fa9 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -39,6 +39,8 @@ #include "TypedArrays.h" #include "XMLHttpRequestClass.h" +#include "SceneScriptingInterface.h" + #include "MIDIEvent.h" @@ -310,6 +312,8 @@ void ScriptEngine::init() { _isInitialized = true; + auto sceneScriptingInterface = DependencyManager::set(); + _entityScriptingInterface.init(); // register various meta-types @@ -353,6 +357,7 @@ void ScriptEngine::init() { registerGlobalObject("Vec3", &_vec3Library); registerGlobalObject("Uuid", &_uuidLibrary); registerGlobalObject("AnimationCache", DependencyManager::get().data()); + registerGlobalObject("Scene", DependencyManager::get().data()); // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));