resolve conflicts on merge with upstream/master

This commit is contained in:
Stephen Birarda 2015-03-03 10:01:24 -08:00
commit 5a601f3676
72 changed files with 2088 additions and 842 deletions

View file

@ -4,6 +4,7 @@
* [Qt](http://qt-project.org/downloads) ~> 5.3.2
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1g
* IMPORTANT: OpenSSL 1.0.1g is critical to avoid a security vulnerability.
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
####CMake External Project Dependencies

View file

@ -14,7 +14,7 @@ Or you can start a regular command prompt and then run:
If using Visual Studio 2013 and building as a Visual Studio 2013 project you need the Windows 8 SDK which you should already have as part of installing Visual Studio 2013. You should be able to see it at `C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86`.
####nmake & msbuild
####nmake
Some of the external projects may require nmake to compile and install. If it is not installed at the location listed below, please ensure that it is in your PATH so CMake can find it when required.
@ -75,6 +75,21 @@ To prevent these problems, install OpenSSL yourself. Download the following bina
Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version.
###vhacd
Download it directly from https://github.com/virneo/v-hacd
To build it run the following commands
1. cd src\
2. mkdir build
3. cd build
4. cmake ..
Build using visual studio 2013. Build ALL_BUILD and INSTALL targets both in Release and Debug.
This will create an output folder with include and lib directory inside it.
Either copy the contents of output folder to ENV %HIFI_LIB_DIR%/vhacd or create an environment variable VHACD_ROOT_DIR to this output directory.
###Build High Fidelity using Visual Studio
Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake.

View file

@ -18,10 +18,9 @@
#include <AccountManager.h>
#include <AddressManager.h>
#include <Assignment.h>
#include <AvatarHashMap.h>
#include <HifiConfigVariantMap.h>
#include <LogHandler.h>
#include <LogUtils.h>
#include <LimitedNodeList.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
@ -40,79 +39,43 @@ SharedAssignmentPointer AssignmentClient::_currentAssignment;
int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("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<LimitedNodeList, NodeList>();
auto addressManager = DependencyManager::set<AddressManager>();
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned);
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
// 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>();
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<NodeList>();
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);

View file

@ -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

View file

@ -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 <QCommandLineParser>
#include <LogHandler.h>
#include <SharedUtil.h>
#include <HifiConfigVariantMap.h>
#include <ShutdownEventListener.h>
#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();
}
}

View file

@ -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 <QApplication>
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

View file

@ -0,0 +1,8 @@
#include "AssignmentClientChildData.h"
AssignmentClientChildData::AssignmentClientChildData(QString childType) :
_childType(childType)
{
}

View file

@ -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 <Assignment.h>
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

View file

@ -12,40 +12,51 @@
#include <signal.h>
#include <LogHandler.h>
#include <ShutdownEventListener.h>
#include <AddressManager.h>
#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<LimitedNodeList, NodeList>();
auto addressManager = DependencyManager::set<AddressManager>();
auto nodeList = DependencyManager::set<LimitedNodeList>(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<QPointer<QProcess> >::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>();
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<QProcess>(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<QProcess>(qobject_cast<QProcess*>(sender()));
_childProcesses.removeOne(QPointer<QProcess>(qobject_cast<QProcess*>(sender())));
spawnChildClient();
void AssignmentClientMonitor::checkSpares() {
auto nodeList = DependencyManager::get<NodeList>();
QUuid aSpareId = "";
unsigned int spareCount = 0;
unsigned int totalCount = 0;
nodeList->removeSilentNodes();
nodeList->eachNode([&](const SharedNodePointer& node) {
AssignmentClientChildData *childData = static_cast<AssignmentClientChildData*>(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<NodeList>();
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<LimitedNodeList>()->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<AssignmentClientChildData*>(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);
}
}
}
}

View file

@ -15,25 +15,40 @@
#include <QtCore/QCoreApplication>
#include <QtCore/qpointer.h>
#include <QtCore/QProcess>
#include <QtCore/QDateTime>
#include <Assignment.h>
#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<QPointer<QProcess> > _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

View file

@ -9,34 +9,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <LogHandler.h>
#include <SharedUtil.h>
#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;
}

View file

@ -0,0 +1,59 @@
#
# FindVHACD.cmake
#
# Try to find the V-HACD library that decomposes a 3D surface into a set of "near" convex parts.
#
# Once done this will define
#
# VHACD_FOUND - system found V-HACD
# VHACD_INCLUDE_DIRS - the V-HACD include directory
# VHACD_LIBRARIES - link to this to use V-HACD
#
# Created on 2/20/2015 by Virendra Singh
# 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("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("vhacd")
macro(_FIND_VHACD_LIBRARY _var)
set(_${_var}_NAMES ${ARGN})
find_library(${_var}_LIBRARY_RELEASE
NAMES ${_${_var}_NAMES}
HINTS
${VHACD_SEARCH_DIRS}
$ENV{VHACD_ROOT_DIR}
PATH_SUFFIXES lib lib/Release
)
find_library(${_var}_LIBRARY_DEBUG
NAMES ${_${_var}_NAMES}
HINTS
${VHACD_SEARCH_DIRS}
$ENV{VHACD_ROOT_DIR}
PATH_SUFFIXES lib lib/Debug
)
select_library_configurations(${_var})
mark_as_advanced(${_var}_LIBRARY)
mark_as_advanced(${_var}_LIBRARY)
endmacro()
find_path(VHACD_INCLUDE_DIRS VHACD.h PATH_SUFFIXES include HINTS ${VHACD_SEARCH_DIRS} $ENV{VHACD_ROOT_DIR})
if(NOT WIN32)
_FIND_VHACD_LIBRARY(VHACD libVHACD.a)
else()
_FIND_VHACD_LIBRARY(VHACD VHACD_LIB)
endif()
set(VHACD_LIBRARIES ${VHACD_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(VHACD "Could NOT find VHACD, try to set the path to VHACD root folder in the system variable VHACD_ROOT_DIR or create a directory vhacd in HIFI_LIB_DIR and paste the necessary files there"
VHACD_INCLUDE_DIRS VHACD_LIBRARIES)
mark_as_advanced(VHACD_INCLUDE_DIRS VHACD_LIBRARIES VHACD_SEARCH_DIRS)

View file

@ -32,6 +32,7 @@
#include <SharedUtil.h>
#include <ShutdownEventListener.h>
#include <UUID.h>
#include <LogHandler.h>
#include "DomainServerNodeData.h"
@ -246,19 +247,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
auto nodeList = DependencyManager::set<LimitedNodeList>(domainServerPort, domainServerDTLSPort);
// no matter the local port, save it to shared mem so that local assignment clients can ask what it is
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().addOnlyOnceMessageRegex
("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().addOnlyOnceMessageRegex
("Unable to fulfill assignment request of type [^ ]+ from [^ ]+");
qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType()
<< "from" << senderSockAddr;
noisyMessageTimer.restart();

View file

@ -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") {

View file

@ -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);
*/

View file

@ -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 @@
</tbody>
</table>
</div>
<div id="no-entities">
No entities found within 50 meter radius. Try moving to a different location and refreshing.
</div>
</body>
</html>

View file

@ -128,21 +128,11 @@
var elLightSections = document.querySelectorAll(".light-section");
var elLightSpotLight = document.getElementById("property-light-spot-light");
var elLightDiffuseRed = document.getElementById("property-light-diffuse-red");
var elLightDiffuseGreen = document.getElementById("property-light-diffuse-green");
var elLightDiffuseBlue = document.getElementById("property-light-diffuse-blue");
var elLightColorRed = document.getElementById("property-light-color-red");
var elLightColorGreen = document.getElementById("property-light-color-green");
var elLightColorBlue = document.getElementById("property-light-color-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 elLightIntensity = document.getElementById("property-light-intensity");
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 = "<i>No Selection</i>";
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("<br>");
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;
@ -290,21 +305,11 @@
elLightSections[i].style.display = 'block';
}
elLightDiffuseRed.value = properties.diffuseColor.red;
elLightDiffuseGreen.value = properties.diffuseColor.green;
elLightDiffuseBlue.value = properties.diffuseColor.blue;
elLightColorRed.value = properties.color.red;
elLightColorGreen.value = properties.color.green;
elLightColorBlue.value = properties.color.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;
elLightIntensity.value = properties.intensity;
elLightExponent.value = properties.exponent;
elLightCutoff.value = properties.cutoff;
}
@ -375,27 +380,13 @@
elLightSpotLight.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isSpotlight'));
var lightDiffuseChangeFunction = createEmitColorPropertyUpdateFunction(
'diffuseColor', elLightDiffuseRed, elLightDiffuseGreen, elLightDiffuseBlue);
elLightDiffuseRed.addEventListener('change', lightDiffuseChangeFunction);
elLightDiffuseGreen.addEventListener('change', lightDiffuseChangeFunction);
elLightDiffuseBlue.addEventListener('change', lightDiffuseChangeFunction);
var lightColorChangeFunction = createEmitColorPropertyUpdateFunction(
'color', elLightColorRed, elLightColorGreen, elLightColorBlue);
elLightColorRed.addEventListener('change', lightColorChangeFunction);
elLightColorGreen.addEventListener('change', lightColorChangeFunction);
elLightColorBlue.addEventListener('change', lightColorChangeFunction);
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'));
elLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('intensity'));
elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent'));
elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff'));
@ -448,6 +439,13 @@
percentage: parseInt(elRescaleDimensionsPct.value),
}));
});
window.onblur = function() {
// Fake a change event
var ev = document.createEvent("HTMLEvents");
ev.initEvent("change", true, true);
document.activeElement.dispatchEvent(ev);
}
}
</script>
</head>
@ -715,55 +713,27 @@
</div>
</div>
<div class="light-section property">
<div class="label">Diffuse</div>
<div class="label">Color</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-light-diffuse-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-light-diffuse-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-light-diffuse-blue"></input></div>
<div class="input-area">R <input class="coord" type='number' id="property-light-color-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-light-color-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-light-color-blue"></input></div>
</div>
</div>
<div class="light-section property">
<div class="label">Ambient</div>
<div class="label">Intensity</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-light-ambient-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-light-ambient-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-light-ambient-blue"></input></div>
<input class="coord" type='number' id="property-light-intensity"></input>
</div>
</div>
<div class="light-section property">
<div class="label">Specular</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-light-specular-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-light-specular-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-light-specular-blue"></input></div>
</div>
</div>
<div class="light-section property">
<div class="label">Constant Attenuation</div>
<div class="value">
<input class="coord" type='number' id="property-light-constant-attenuation"></input>
</div>
</div>
<div class="light-section property">
<div class="label">Linear Attenuation</div>
<div class="value">
<input class="coord" type='number' id="property-light-linear-attenuation"></input>
</div>
</div>
<div class="light-section property">
<div class="label">Quadratic Attenuation</div>
<div class="value">
<input class="coord" type='number' id="property-light-quadratic-attenuation"></input>
</div>
</div>
<div class="light-section property">
<div class="label">Exponent</div>
<div class="label">Spot Light Exponent</div>
<div class="value">
<input class="coord" type='number' id="property-light-exponent"></input>
</div>
</div>
<div class="light-section property">
<div class="label">Cutoff (degrees)</div>
<div class="label">Spot Light Cutoff (degrees)</div>
<div class="value">
<input class="coord" type='number' id="property-light-cutoff"></input>
</div>

View file

@ -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;
}

View file

@ -238,12 +238,12 @@ SelectionDisplay = (function () {
// These are multipliers for sizing the rotation degrees display while rotating an entity
var ROTATION_DISPLAY_DISTANCE_MULTIPLIER = 1.2;
var ROTATION_DISPLAY_SIZE_X_MULTIPLIER = 0.5;
var ROTATION_DISPLAY_SIZE_X_MULTIPLIER = 0.6;
var ROTATION_DISPLAY_SIZE_Y_MULTIPLIER = 0.18;
var ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.17;
var ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.14;
var ROTATE_ARROW_WEST_NORTH_URL = HIFI_PUBLIC_BUCKET + "images/rotate-arrow-west-north.png";
var ROTATE_ARROW_WEST_SOUTH_URL = HIFI_PUBLIC_BUCKET + "images/rotate-arrow-west-south.png";
var ROTATE_ARROW_WEST_NORTH_URL = HIFI_PUBLIC_BUCKET + "images/rotate-arrow-west-north.svg";
var ROTATE_ARROW_WEST_SOUTH_URL = HIFI_PUBLIC_BUCKET + "images/rotate-arrow-west-south.svg";
var showExtendedStretchHandles = false;
@ -280,11 +280,11 @@ SelectionDisplay = (function () {
var originalRoll;
var rotateHandleColor = { red: 0, green: 0, blue: 0 };
var rotateHandleAlpha = 0.7;
var handleColor = { red: 255, green: 255, blue: 255 };
var handleAlpha = 0.7;
var highlightedHandleColor = { red: 255, green: 0, blue: 0 };
var highlightedHandleAlpha = 0.7;
var highlightedHandleColor = { red: 183, green: 64, blue: 44 };
var highlightedHandleAlpha = 0.9;
var previousHandle = false;
var previousHandleColor;
@ -385,10 +385,10 @@ SelectionDisplay = (function () {
});
var grabberMoveUp = Overlays.addOverlay("billboard", {
url: HIFI_PUBLIC_BUCKET + "images/up-arrow.png",
url: HIFI_PUBLIC_BUCKET + "images/up-arrow.svg",
position: { x:0, y: 0, z: 0},
color: { red: 0, green: 0, blue: 0 },
alpha: 1.0,
color: handleColor,
alpha: handleAlpha,
visible: false,
size: 0.1,
scale: 0.1,
@ -533,7 +533,7 @@ SelectionDisplay = (function () {
var rotateOverlayTarget = Overlays.addOverlay("circle3d", {
position: { x:0, y: 0, z: 0},
size: rotateOverlayTargetSize,
size: rotateOverlayTargetSize * 2,
color: { red: 0, green: 0, blue: 0 },
alpha: 0.0,
solid: true,
@ -595,8 +595,8 @@ SelectionDisplay = (function () {
var yawHandle = Overlays.addOverlay("billboard", {
url: ROTATE_ARROW_WEST_NORTH_URL,
position: { x:0, y: 0, z: 0},
color: rotateHandleColor,
alpha: rotateHandleAlpha,
color: handleColor,
alpha: handleAlpha,
visible: false,
size: 0.1,
scale: 0.1,
@ -608,8 +608,8 @@ SelectionDisplay = (function () {
var pitchHandle = Overlays.addOverlay("billboard", {
url: ROTATE_ARROW_WEST_NORTH_URL,
position: { x:0, y: 0, z: 0},
color: rotateHandleColor,
alpha: rotateHandleAlpha,
color: handleColor,
alpha: handleAlpha,
visible: false,
size: 0.1,
scale: 0.1,
@ -621,8 +621,8 @@ SelectionDisplay = (function () {
var rollHandle = Overlays.addOverlay("billboard", {
url: ROTATE_ARROW_WEST_NORTH_URL,
position: { x:0, y: 0, z: 0},
color: rotateHandleColor,
alpha: rotateHandleAlpha,
color: handleColor,
alpha: handleAlpha,
visible: false,
size: 0.1,
scale: 0.1,
@ -1692,7 +1692,7 @@ SelectionDisplay = (function () {
y: innerRadius * ROTATION_DISPLAY_SIZE_Y_MULTIPLIER
},
lineHeight: innerRadius * ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER,
text: normalizeDegrees(angleFromZero),
text: normalizeDegrees(angleFromZero) + "°",
});
}
@ -1713,15 +1713,17 @@ SelectionDisplay = (function () {
Overlays.editOverlay(rotateOverlayInner,
{
visible: true,
size: innerRadius,
size: innerRadius * 2,
innerRadius: 0.9,
startAt: 0,
endAt: 360,
alpha: innerAlpha
});
Overlays.editOverlay(rotateOverlayOuter,
{
visible: true,
size: outerRadius,
size: outerRadius * 2,
innerRadius: 0.9,
startAt: 0,
endAt: 360,
@ -1731,7 +1733,7 @@ SelectionDisplay = (function () {
Overlays.editOverlay(rotateOverlayCurrent,
{
visible: true,
size: outerRadius,
size: outerRadius * 2,
startAt: 0,
endAt: 0,
innerRadius: 0.9,
@ -1809,13 +1811,13 @@ SelectionDisplay = (function () {
if (snapToInner) {
Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 });
Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder });
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius,
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius * 2,
majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0,
majorTickMarksLength: -0.25, minorTickMarksLength: 0, });
} else {
Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 });
Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder });
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius,
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius * 2,
majorTickMarksAngle: 45.0, minorTickMarksAngle: 5,
majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, });
}
@ -1840,15 +1842,17 @@ SelectionDisplay = (function () {
Overlays.editOverlay(rotateOverlayInner,
{
visible: true,
size: innerRadius,
size: innerRadius * 2,
innerRadius: 0.9,
startAt: 0,
endAt: 360,
alpha: innerAlpha
});
Overlays.editOverlay(rotateOverlayOuter,
{
visible: true,
size: outerRadius,
size: outerRadius * 2,
innerRadius: 0.9,
startAt: 0,
endAt: 360,
@ -1858,7 +1862,7 @@ SelectionDisplay = (function () {
Overlays.editOverlay(rotateOverlayCurrent,
{
visible: true,
size: outerRadius,
size: outerRadius * 2,
startAt: 0,
endAt: 0,
innerRadius: 0.9,
@ -1929,13 +1933,13 @@ SelectionDisplay = (function () {
if (snapToInner) {
Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 });
Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder });
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius,
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius * 2,
majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0,
majorTickMarksLength: -0.25, minorTickMarksLength: 0, });
} else {
Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 });
Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder });
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius,
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius * 2,
majorTickMarksAngle: 45.0, minorTickMarksAngle: 5,
majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, });
}
@ -1959,15 +1963,17 @@ SelectionDisplay = (function () {
Overlays.editOverlay(rotateOverlayInner,
{
visible: true,
size: innerRadius,
size: innerRadius * 2,
innerRadius: 0.9,
startAt: 0,
endAt: 360,
alpha: innerAlpha
});
Overlays.editOverlay(rotateOverlayOuter,
{
visible: true,
size: outerRadius,
size: outerRadius * 2,
innerRadius: 0.9,
startAt: 0,
endAt: 360,
@ -1977,7 +1983,7 @@ SelectionDisplay = (function () {
Overlays.editOverlay(rotateOverlayCurrent,
{
visible: true,
size: outerRadius,
size: outerRadius * 2,
startAt: 0,
endAt: 0,
innerRadius: 0.9,
@ -2047,13 +2053,13 @@ SelectionDisplay = (function () {
if (snapToInner) {
Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 });
Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder });
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius,
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius * 2,
majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0,
majorTickMarksLength: -0.25, minorTickMarksLength: 0, });
} else {
Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 });
Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder });
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius,
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius * 2,
majorTickMarksAngle: 45.0, minorTickMarksAngle: 5,
majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, });
}
@ -2342,14 +2348,14 @@ SelectionDisplay = (function () {
case yawHandle:
case pitchHandle:
case rollHandle:
pickedColor = rotateHandleColor;
pickedAlpha = rotateHandleAlpha;
pickedColor = handleColor;
pickedAlpha = handleAlpha;
highlightNeeded = true;
break;
case grabberMoveUp:
pickedColor = rotateHandleColor;
pickedAlpha = rotateHandleAlpha;
pickedColor = handleColor;
pickedAlpha = handleAlpha;
highlightNeeded = true;
break;

View file

@ -82,6 +82,8 @@
#include <UserActivityLogger.h>
#include <UUID.h>
#include <SceneScriptingInterface.h>
#include "Application.h"
#include "AudioClient.h"
#include "InterfaceVersion.h"
@ -771,8 +773,8 @@ void Application::paintGL() {
{
PerformanceTimer perfTimer("renderOverlay");
_applicationOverlay.renderOverlay(true);
if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) {
_applicationOverlay.renderOverlay(true);
_applicationOverlay.displayOverlayTexture();
}
}
@ -1858,35 +1860,6 @@ void Application::updateMouseRay() {
}
}
void Application::updateFaceshift() {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateFaceshift()");
auto faceshift = DependencyManager::get<Faceshift>();
// 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<Visage>()->update();
}
void Application::updateDDE() {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateDDE()");
// Update Cara
DependencyManager::get<DdeFaceTracker>()->update();
}
void Application::updateMyAvatarLookAtPosition() {
PerformanceTimer perfTimer("lookAt");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
@ -2066,8 +2039,10 @@ 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();
}
@ -2488,7 +2463,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<SceneScriptingInterface>()->getSkyStage();
return skyStage->getSunLight()->getDirection();
}
void Application::updateShadowMap() {
@ -2498,7 +2474,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);
@ -2867,7 +2843,9 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
{
DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(-getSunDirection(), GLOBAL_LIGHT_COLOR, GLOBAL_LIGHT_INTENSITY);
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity());
PROFILE_RANGE("DeferredLighting");
PerformanceTimer perfTimer("lighting");
DependencyManager::get<DeferredLightingEffect>()->render();

View file

@ -419,9 +419,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);

View file

@ -453,7 +453,8 @@ Menu::Menu() {
QMenu* timingMenu = developerMenu->addMenu("Timing and Stats");
QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer");
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarSimulateTiming, 0, false);

View file

@ -150,7 +150,7 @@ namespace MenuOption {
const QString DisplayModelTriangles = "Display Model Triangles";
const QString DisplayModelElementChildProxies = "Display Model Element Children";
const QString DisplayModelElementProxy = "Display Model Element Bounds";
const QString DisplayTimingDetails = "Display Timing Details";
const QString DisplayDebugTimingDetails = "Display Timing Details";
const QString DontDoPrecisionPicking = "Don't Do Precision Picking";
const QString DontFadeOnOctreeServerChanges = "Don't Fade In/Out on Octree Server Changes";
const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene";
@ -198,6 +198,7 @@ namespace MenuOption {
const QString OctreeStats = "Voxel and Entity Statistics";
const QString OffAxisProjection = "Off-Axis Projection";
const QString OldVoxelCullingMode = "Old Voxel Culling Mode";
const QString OnlyDisplayTopTen = "Only Display Top Ten";
const QString Pair = "Pair";
const QString PipelineWarnings = "Log Render Pipeline Warnings";
const QString Preferences = "Preferences...";

View file

@ -380,8 +380,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode, bool
foreach (const AvatarManager::LocalLight& light, DependencyManager::get<AvatarManager>()->getLocalLights()) {
glm::vec3 direction = orientation * light.direction;
DependencyManager::get<DeferredLightingEffect>()->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);
}
}

View file

@ -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),

View file

@ -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;

View file

@ -880,13 +880,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;
@ -1152,15 +1146,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);

View file

@ -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
@ -64,10 +66,11 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 port) :
_mouthSmileLeftIndex(28),
_mouthSmileRightIndex(29),
_jawOpenIndex(21),
_host(host),
_port(port)
_previousTranslation(glm::vec3()),
_previousRotation(glm::quat())
{
_blendshapeCoefficients.resize(NUM_EXPRESSION);
_previousExpressions.resize(NUM_EXPRESSION);
connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError)));
@ -80,18 +83,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();
@ -151,37 +142,6 @@ float DdeFaceTracker::getBlendshapeCoefficient(int index) const {
return (index >= 0 && index < (int)_blendshapeCoefficients.size()) ? _blendshapeCoefficients[index] : 0.0f;
}
static const float DDE_MIN_RANGE = -0.2f;
static const float DDE_MAX_RANGE = 1.5f;
float rescaleCoef(float ddeCoef) {
return (ddeCoef - DDE_MIN_RANGE) / (DDE_MAX_RANGE - DDE_MIN_RANGE);
}
const int MIN = 0;
const int AVG = 1;
const int MAX = 2;
const float LONG_TERM_AVERAGE = 0.999f;
void resetCoefficient(float * coefficient, float currentValue) {
coefficient[MIN] = coefficient[MAX] = coefficient[AVG] = currentValue;
}
float updateAndGetCoefficient(float * coefficient, float currentValue, bool scaleToRange = false) {
coefficient[MIN] = (currentValue < coefficient[MIN]) ? currentValue : coefficient[MIN];
coefficient[MAX] = (currentValue > coefficient[MAX]) ? currentValue : coefficient[MAX];
coefficient[AVG] = LONG_TERM_AVERAGE * coefficient[AVG] + (1.0f - LONG_TERM_AVERAGE) * currentValue;
if (coefficient[MAX] > coefficient[MIN]) {
if (scaleToRange) {
return glm::clamp((currentValue - coefficient[AVG]) / (coefficient[MAX] - coefficient[MIN]), 0.0f, 1.0f);
} else {
return glm::clamp(currentValue - coefficient[AVG], 0.0f, 1.0f);
}
} else {
return 0.0f;
}
}
void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
if(buffer.size() > MIN_PACKET_SIZE) {
Packet packet;
@ -196,9 +156,6 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
if (_reset || (_lastReceiveTimestamp == 0)) {
memcpy(&_referenceTranslation, &translation, sizeof(glm::vec3));
memcpy(&_referenceRotation, &rotation, sizeof(glm::quat));
resetCoefficient(_rightEye, packet.expressions[0]);
resetCoefficient(_leftEye, packet.expressions[1]);
_reset = false;
}
@ -207,51 +164,75 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
translation -= _referenceTranslation;
translation /= LEAN_DAMPING_FACTOR;
translation.x *= -1;
_headTranslation = (translation + _previousTranslation) / 2.0f;
_previousTranslation = translation;
// Compute relative rotation
rotation = glm::inverse(_referenceRotation) * rotation;
// copy values
_headTranslation = translation;
_headRotation = rotation;
if (_lastReceiveTimestamp == 0) {
// On first packet, reset coefficients
}
// Set blendshapes
float EYE_MAGNIFIER = 4.0f;
float rightEye = glm::clamp((updateAndGetCoefficient(_rightEye, packet.expressions[0])) * EYE_MAGNIFIER, 0.0f, 1.0f);
_blendshapeCoefficients[_rightBlinkIndex] = rightEye;
float leftEye = glm::clamp((updateAndGetCoefficient(_leftEye, packet.expressions[1])) * EYE_MAGNIFIER, 0.0f, 1.0f);
_blendshapeCoefficients[_leftBlinkIndex] = leftEye;
_headRotation = (rotation + _previousRotation) / 2.0f;
_previousRotation = rotation;
float leftBrow = 1.0f - rescaleCoef(packet.expressions[14]);
if (leftBrow < 0.5f) {
_blendshapeCoefficients[_browDownLeftIndex] = 1.0f - 2.0f * leftBrow;
_blendshapeCoefficients[_browUpLeftIndex] = 0.0f;
// The DDE coefficients, overall, range from -0.2 to 1.5 or so. However, individual coefficients typically vary much
// less than this.packet.expressions[1]
// Eye blendshapes
static const float RELAXED_EYE_VALUE = 0.2f;
static const float EYE_OPEN_SCALE = 4.0f;
static const float EYE_BLINK_SCALE = 2.5f;
float leftEye = (packet.expressions[1] + _previousExpressions[1]) / 2.0f - RELAXED_EYE_VALUE;
float rightEye = (packet.expressions[0] + _previousExpressions[0]) / 2.0f - RELAXED_EYE_VALUE;
if (leftEye > 0.0f) {
_blendshapeCoefficients[_leftBlinkIndex] = glm::clamp(EYE_BLINK_SCALE * leftEye, 0.0f, 1.0f);
_blendshapeCoefficients[_leftEyeOpenIndex] = 0.0f;
} else {
_blendshapeCoefficients[_leftBlinkIndex] = 0.0f;
_blendshapeCoefficients[_leftEyeOpenIndex] = glm::clamp(EYE_OPEN_SCALE * -leftEye, 0.0f, 1.0f);
}
if (rightEye > 0.0f) {
_blendshapeCoefficients[_rightBlinkIndex] = glm::clamp(EYE_BLINK_SCALE * rightEye, 0.0f, 1.0f);
_blendshapeCoefficients[_rightEyeOpenIndex] = 0.0f;
} else {
_blendshapeCoefficients[_rightBlinkIndex] = 0.0f;
_blendshapeCoefficients[_rightEyeOpenIndex] = glm::clamp(EYE_OPEN_SCALE * -rightEye, 0.0f, 1.0f);
}
_previousExpressions[1] = packet.expressions[1];
_previousExpressions[0] = packet.expressions[0];
// Eyebrow blendshapes
static const float BROW_UP_SCALE = 3.0f;
static const float BROW_DOWN_SCALE = 3.0f;
float browCenter = (packet.expressions[17] + _previousExpressions[17]) / 2.0f;
if (browCenter > 0) {
float browUp = glm::clamp(BROW_UP_SCALE * browCenter, 0.0f, 1.0f);
_blendshapeCoefficients[_browUpCenterIndex] = browUp;
_blendshapeCoefficients[_browUpLeftIndex] = browUp;
_blendshapeCoefficients[_browDownLeftIndex] = 0.0f;
_blendshapeCoefficients[_browUpLeftIndex] = 2.0f * (leftBrow - 0.5f);
}
float rightBrow = 1.0f - rescaleCoef(packet.expressions[15]);
if (rightBrow < 0.5f) {
_blendshapeCoefficients[_browDownRightIndex] = 1.0f - 2.0f * rightBrow;
_blendshapeCoefficients[_browUpRightIndex] = 0.0f;
} else {
_blendshapeCoefficients[_browUpRightIndex] = browUp;
_blendshapeCoefficients[_browDownRightIndex] = 0.0f;
_blendshapeCoefficients[_browUpRightIndex] = 2.0f * (rightBrow - 0.5f);
} else {
float browDown = glm::clamp(BROW_DOWN_SCALE * -browCenter, 0.0f, 1.0f);
_blendshapeCoefficients[_browUpCenterIndex] = 0.0f;
_blendshapeCoefficients[_browUpLeftIndex] = 0.0f;
_blendshapeCoefficients[_browDownLeftIndex] = browDown;
_blendshapeCoefficients[_browUpRightIndex] = 0.0f;
_blendshapeCoefficients[_browDownRightIndex] = browDown;
}
float JAW_OPEN_MAGNIFIER = 1.4f;
_blendshapeCoefficients[_jawOpenIndex] = rescaleCoef(packet.expressions[21]) * JAW_OPEN_MAGNIFIER;
float SMILE_MULTIPLIER = 2.0f;
_blendshapeCoefficients[_mouthSmileLeftIndex] = glm::clamp(packet.expressions[24] * SMILE_MULTIPLIER, 0.0f, 1.0f);
_blendshapeCoefficients[_mouthSmileRightIndex] = glm::clamp(packet.expressions[23] * SMILE_MULTIPLIER, 0.0f, 1.0f);
_previousExpressions[17] = packet.expressions[17];
// Mouth blendshapes
static const float JAW_OPEN_THRESHOLD = 0.16f;
static const float JAW_OPEN_SCALE = 1.4f;
static const float SMILE_THRESHOLD = 0.18f;
static const float SMILE_SCALE = 1.5f;
float smileLeft = (packet.expressions[24] + _previousExpressions[24]) / 2.0f;
float smileRight = (packet.expressions[23] + _previousExpressions[23]) / 2.0f;
_blendshapeCoefficients[_jawOpenIndex] = glm::clamp(JAW_OPEN_SCALE * (packet.expressions[21] - JAW_OPEN_THRESHOLD),
0.0f, 1.0f);
_blendshapeCoefficients[_mouthSmileLeftIndex] = glm::clamp(SMILE_SCALE * (smileLeft - SMILE_THRESHOLD), 0.0f, 1.0f);
_blendshapeCoefficients[_mouthSmileRightIndex] = glm::clamp(SMILE_SCALE * (smileRight - SMILE_THRESHOLD), 0.0f, 1.0f);
_previousExpressions[24] = packet.expressions[24];
_previousExpressions[23] = packet.expressions[23];
} else {
qDebug() << "[Error] DDE Face Tracker Decode Error";
}

View file

@ -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); }
@ -90,10 +88,11 @@ private:
int _mouthSmileRightIndex;
int _jawOpenIndex;
float _leftEye[3];
float _rightEye[3];
// Previous values for simple smoothing
glm::vec3 _previousTranslation;
glm::quat _previousRotation;
QVector<float> _previousExpressions;
};
#endif // hifi_DdeFaceTracker_h

View file

@ -9,9 +9,60 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <GLMHelpers.h>
#include "FaceTracker.h"
FaceTracker::FaceTracker() :
_estimatedEyePitch(0.0f),
_estimatedEyeYaw(0.0f) {
inline float FaceTracker::getBlendshapeCoefficient(int index) const {
return isValidBlendshapeIndex(index) ? glm::mix(0.0f, _blendshapeCoefficients[index], getFadeCoefficient())
: 0.0f;
}
const QVector<float>& FaceTracker::getBlendshapeCoefficients() const {
static QVector<float> blendshapes;
float fadeCoefficient = getFadeCoefficient();
if (fadeCoefficient == 1.0f) {
return _blendshapeCoefficients;
} else {
blendshapes.resize(_blendshapeCoefficients.size());
for (int i = 0; i < _blendshapeCoefficients.size(); i++) {
blendshapes[i] = glm::mix(0.0f, _blendshapeCoefficients[i], fadeCoefficient);
}
return blendshapes;
}
}
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 safeMix(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);
}
}

View file

@ -18,29 +18,40 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
/// 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; }
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
int getNumBlendshapes() const { return _blendshapeCoefficients.size(); }
bool isValidBlendshapeIndex(int index) const { return index >= 0 && index < getNumBlendshapes(); }
const QVector<float>& getBlendshapeCoefficients() const;
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<float> _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

View file

@ -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());
}

View file

@ -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<float>& 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<float> _eyeDeflection;
Setting::Handle<QString> _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

View file

@ -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<QByteArray, QPair<int, float> >& getActionUnitNameMap()
}
#endif
const float TRANSLATION_SCALE = 20.0f;
#ifdef HAVE_VISAGE
const float TRANSLATION_SCALE = 20.0f;
void Visage::init() {
connect(DependencyManager::get<Faceshift>().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) &&

View file

@ -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;
};

View file

@ -34,7 +34,8 @@
using namespace std;
const int STATS_PELS_PER_LINE = 20;
const int STATS_PELS_PER_LINE = 16;
const int STATS_PELS_INITIALOFFSET = 12;
const int STATS_GENERAL_MIN_WIDTH = 165;
const int STATS_PING_MIN_WIDTH = 190;
@ -170,7 +171,7 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh
}
bool Stats::includeTimingRecord(const QString& name) {
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails)) {
if (name.startsWith("/idle/update/")) {
if (name.startsWith("/idle/update/myAvatar/")) {
if (name.startsWith("/idle/update/myAvatar/simulate/")) {
@ -231,7 +232,7 @@ void Stats::display(
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails)) {
columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide...
// we will also include room for 1 line per timing record and a header of 4 lines
@ -239,15 +240,21 @@ void Stats::display(
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
int statsLines = 0;
while (i.hasNext()) {
i.next();
if (includeTimingRecord(i.key())) {
lines++;
statsLines++;
if (onlyDisplayTopTen && statsLines == 10) {
break;
}
}
}
}
drawBackground(backgroundColor, horizontalOffset, 0, columnOneWidth, lines * STATS_PELS_PER_LINE + 10);
drawBackground(backgroundColor, horizontalOffset, 0, columnOneWidth, (lines + 1) * STATS_PELS_PER_LINE);
horizontalOffset += 5;
int columnOneHorizontalOffset = horizontalOffset;
@ -256,7 +263,7 @@ void Stats::display(
QString avatarNodes = QString("Avatars: %1").arg(totalAvatars);
QString framesPerSecond = QString("Framerate: %1 FPS").arg(fps, 3, 'f', 0);
verticalOffset += STATS_PELS_PER_LINE;
verticalOffset = STATS_PELS_INITIALOFFSET; // first one is offset by less than a line
drawText(horizontalOffset, verticalOffset, scale, rotation, font, serverNodes.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarNodes.toUtf8().constData(), color);
@ -275,9 +282,10 @@ void Stats::display(
// TODO: the display of these timing details should all be moved to JavaScript
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails)) {
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
// Timing details...
verticalOffset += STATS_PELS_PER_LINE * 6; // skip 6 lines to be under the other columns
verticalOffset += STATS_PELS_PER_LINE * 4; // skip 3 lines to be under the other columns
drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font,
"-------------------------------------------------------- Function "
"------------------------------------------------------- --msecs- -calls--", color);
@ -296,6 +304,7 @@ void Stats::display(
}
}
int linesDisplayed = 0;
QMapIterator<float, QString> j(sortedRecords);
j.toBack();
while (j.hasPrevious()) {
@ -311,11 +320,15 @@ void Stats::display(
verticalOffset += STATS_PELS_PER_LINE;
drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine.toUtf8().constData(), color);
linesDisplayed++;
if (onlyDisplayTopTen && linesDisplayed == 10) {
break;
}
}
}
verticalOffset = 0;
verticalOffset = STATS_PELS_INITIALOFFSET;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + 1;
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
@ -351,7 +364,7 @@ void Stats::display(
// only draw our background if column one didn't draw a wide background
if (columnOneWidth == _generalStatsWidth) {
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10);
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, (lines + 1) * STATS_PELS_PER_LINE);
}
horizontalOffset += 5;
@ -377,7 +390,6 @@ void Stats::display(
voxelAvgPing = QString("Entities avg ping: --");
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioPing.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPing.toUtf8().constData(), color);
@ -396,17 +408,17 @@ void Stats::display(
drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing.toUtf8().constData(), color);
}
verticalOffset = 0;
verticalOffset = STATS_PELS_INITIALOFFSET;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2;
}
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();
lines = _expanded ? 8 : 3;
lines = _expanded ? 7 : 3;
if (columnOneWidth == _generalStatsWidth) {
drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, lines * STATS_PELS_PER_LINE + 10);
drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, (lines + 1) * STATS_PELS_PER_LINE);
}
horizontalOffset += 5;
@ -418,7 +430,6 @@ void Stats::display(
QString avatarBodyYaw = QString("Yaw: %1").arg(myAvatar->getBodyYaw(), -1, 'f', 1);
QString avatarMixerStats;
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPosition.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarVelocity.toUtf8().constData(), color);
@ -476,13 +487,13 @@ void Stats::display(
}
}
verticalOffset = 0;
verticalOffset = STATS_PELS_INITIALOFFSET;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3;
lines = _expanded ? 14 : 3;
lines = _expanded ? 10 : 2;
drawBackground(backgroundColor, horizontalOffset, 0, glCanvas->width() - horizontalOffset,
lines * STATS_PELS_PER_LINE + 10);
(lines + 1) * STATS_PELS_PER_LINE);
horizontalOffset += 5;
// Model/Entity render details
@ -491,7 +502,6 @@ void Stats::display(
octreeStats << "Entity Items rendered: " << entities->getItemsRendered()
<< " / Out of view:" << entities->getItemsOutOfView()
<< " / Too small:" << entities->getItemsTooSmall();
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
if (_expanded) {

View file

@ -32,35 +32,22 @@ void RenderableLightEntityItem::render(RenderArgs* args) {
float largestDiameter = glm::max(dimensions.x, dimensions.y, dimensions.z);
const float MAX_COLOR = 255.0f;
float diffuseR = getDiffuseColor()[RED_INDEX] / MAX_COLOR;
float diffuseG = getDiffuseColor()[GREEN_INDEX] / MAX_COLOR;
float diffuseB = getDiffuseColor()[BLUE_INDEX] / MAX_COLOR;
float colorR = getColor()[RED_INDEX] / MAX_COLOR;
float colorG = getColor()[GREEN_INDEX] / MAX_COLOR;
float colorB = getColor()[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(colorR, colorG, colorB);
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 = getIntensity();
float exponent = getExponent();
float cutoff = glm::radians(getCutoff());
if (_isSpotlight) {
DependencyManager::get<DeferredLightingEffect>()->addSpotLight(position, largestDiameter / 2.0f,
ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation,
direction, exponent, cutoff);
DependencyManager::get<DeferredLightingEffect>()->addSpotLight(position, largestDiameter / 2.0f,
color, intensity, rotation, exponent, cutoff);
} else {
DependencyManager::get<DeferredLightingEffect>()->addPointLight(position, largestDiameter / 2.0f,
ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation);
DependencyManager::get<DeferredLightingEffect>()->addPointLight(position, largestDiameter / 2.0f,
color, intensity);
}
#ifdef WANT_DEBUG

View file

@ -49,12 +49,7 @@ EntityItemProperties::EntityItemProperties() :
CONSTRUCT_PROPERTY(ignoreForCollisions, ENTITY_ITEM_DEFAULT_IGNORE_FOR_COLLISIONS),
CONSTRUCT_PROPERTY(collisionsWillMove, ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE),
CONSTRUCT_PROPERTY(isSpotlight, false),
CONSTRUCT_PROPERTY(diffuseColor, ),
CONSTRUCT_PROPERTY(ambientColor, ),
CONSTRUCT_PROPERTY(specularColor, ),
CONSTRUCT_PROPERTY(constantAttenuation, 1.0f),
CONSTRUCT_PROPERTY(linearAttenuation, 0.0f),
CONSTRUCT_PROPERTY(quadraticAttenuation, 0.0f),
CONSTRUCT_PROPERTY(intensity, 1.0f),
CONSTRUCT_PROPERTY(exponent, 0.0f),
CONSTRUCT_PROPERTY(cutoff, PI),
CONSTRUCT_PROPERTY(locked, ENTITY_ITEM_DEFAULT_LOCKED),
@ -222,12 +217,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_IGNORE_FOR_COLLISIONS, ignoreForCollisions);
CHECK_PROPERTY_CHANGE(PROP_COLLISIONS_WILL_MOVE, collisionsWillMove);
CHECK_PROPERTY_CHANGE(PROP_IS_SPOTLIGHT, isSpotlight);
CHECK_PROPERTY_CHANGE(PROP_DIFFUSE_COLOR, diffuseColor);
CHECK_PROPERTY_CHANGE(PROP_AMBIENT_COLOR, ambientColor);
CHECK_PROPERTY_CHANGE(PROP_SPECULAR_COLOR, specularColor);
CHECK_PROPERTY_CHANGE(PROP_CONSTANT_ATTENUATION, constantAttenuation);
CHECK_PROPERTY_CHANGE(PROP_LINEAR_ATTENUATION, linearAttenuation);
CHECK_PROPERTY_CHANGE(PROP_QUADRATIC_ATTENUATION, quadraticAttenuation);
CHECK_PROPERTY_CHANGE(PROP_INTENSITY, intensity);
CHECK_PROPERTY_CHANGE(PROP_EXPONENT, exponent);
CHECK_PROPERTY_CHANGE(PROP_CUTOFF, cutoff);
CHECK_PROPERTY_CHANGE(PROP_LOCKED, locked);
@ -281,12 +271,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
COPY_PROPERTY_TO_QSCRIPTVALUE(ignoreForCollisions);
COPY_PROPERTY_TO_QSCRIPTVALUE(collisionsWillMove);
COPY_PROPERTY_TO_QSCRIPTVALUE(isSpotlight);
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(diffuseColor, getDiffuseColor());
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(ambientColor, getAmbientColor());
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(specularColor, getSpecularColor());
COPY_PROPERTY_TO_QSCRIPTVALUE(constantAttenuation);
COPY_PROPERTY_TO_QSCRIPTVALUE(linearAttenuation);
COPY_PROPERTY_TO_QSCRIPTVALUE(quadraticAttenuation);
COPY_PROPERTY_TO_QSCRIPTVALUE(intensity);
COPY_PROPERTY_TO_QSCRIPTVALUE(exponent);
COPY_PROPERTY_TO_QSCRIPTVALUE(cutoff);
COPY_PROPERTY_TO_QSCRIPTVALUE(locked);
@ -359,12 +344,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(ignoreForCollisions, setIgnoreForCollisions);
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(collisionsWillMove, setCollisionsWillMove);
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(isSpotlight, setIsSpotlight);
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(diffuseColor, setDiffuseColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(ambientColor, setAmbientColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(specularColor, setSpecularColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(constantAttenuation, setConstantAttenuation);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(linearAttenuation, setLinearAttenuation);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(quadraticAttenuation, setQuadraticAttenuation);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(intensity, setIntensity);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(exponent, setExponent);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(cutoff, setCutoff);
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(locked, setLocked);
@ -543,12 +523,8 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
if (properties.getType() == EntityTypes::Light) {
APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, appendValue, properties.getIsSpotlight());
APPEND_ENTITY_PROPERTY(PROP_DIFFUSE_COLOR, appendColor, properties.getDiffuseColor());
APPEND_ENTITY_PROPERTY(PROP_AMBIENT_COLOR, appendColor, properties.getAmbientColor());
APPEND_ENTITY_PROPERTY(PROP_SPECULAR_COLOR, appendColor, properties.getSpecularColor());
APPEND_ENTITY_PROPERTY(PROP_CONSTANT_ATTENUATION, appendValue, properties.getConstantAttenuation());
APPEND_ENTITY_PROPERTY(PROP_LINEAR_ATTENUATION, appendValue, properties.getLinearAttenuation());
APPEND_ENTITY_PROPERTY(PROP_QUADRATIC_ATTENUATION, appendValue, properties.getQuadraticAttenuation());
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, properties.getColor());
APPEND_ENTITY_PROPERTY(PROP_INTENSITY, appendValue, properties.getIntensity());
APPEND_ENTITY_PROPERTY(PROP_EXPONENT, appendValue, properties.getExponent());
APPEND_ENTITY_PROPERTY(PROP_CUTOFF, appendValue, properties.getCutoff());
}
@ -765,12 +741,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
if (properties.getType() == EntityTypes::Light) {
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IS_SPOTLIGHT, bool, setIsSpotlight);
READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_DIFFUSE_COLOR, setDiffuseColor);
READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_AMBIENT_COLOR, setAmbientColor);
READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_SPECULAR_COLOR, setSpecularColor);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CONSTANT_ATTENUATION, float, setConstantAttenuation);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINEAR_ATTENUATION, float, setLinearAttenuation);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_QUADRATIC_ATTENUATION, float, setQuadraticAttenuation);
READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_COLOR, setColor);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INTENSITY, float, setIntensity);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EXPONENT, float, setExponent);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CUTOFF, float, setCutoff);
}
@ -835,12 +807,7 @@ void EntityItemProperties::markAllChanged() {
_ignoreForCollisionsChanged = true;
_collisionsWillMoveChanged = true;
_diffuseColorChanged = true;
_ambientColorChanged = true;
_specularColorChanged = true;
_constantAttenuationChanged = true;
_linearAttenuationChanged = true;
_quadraticAttenuationChanged = true;
_intensityChanged = true;
_exponentChanged = true;
_cutoffChanged = true;
_lockedChanged = true;

View file

@ -66,12 +66,12 @@ enum EntityPropertyList {
// property used by Light entity
PROP_IS_SPOTLIGHT,
PROP_DIFFUSE_COLOR,
PROP_AMBIENT_COLOR,
PROP_SPECULAR_COLOR,
PROP_CONSTANT_ATTENUATION,
PROP_LINEAR_ATTENUATION,
PROP_QUADRATIC_ATTENUATION,
PROP_DIFFUSE_COLOR_UNUSED,
PROP_AMBIENT_COLOR_UNUSED,
PROP_SPECULAR_COLOR_UNUSED,
PROP_INTENSITY, // Previously PROP_CONSTANT_ATTENUATION
PROP_LINEAR_ATTENUATION_UNUSED,
PROP_QUADRATIC_ATTENUATION_UNUSED,
PROP_EXPONENT,
PROP_CUTOFF,
@ -165,12 +165,7 @@ public:
DEFINE_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, IgnoreForCollisions, ignoreForCollisions, bool);
DEFINE_PROPERTY(PROP_COLLISIONS_WILL_MOVE, CollisionsWillMove, collisionsWillMove, bool);
DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool);
DEFINE_PROPERTY_REF(PROP_DIFFUSE_COLOR, DiffuseColor, diffuseColor, xColor);
DEFINE_PROPERTY_REF(PROP_AMBIENT_COLOR, AmbientColor, ambientColor, xColor);
DEFINE_PROPERTY_REF(PROP_SPECULAR_COLOR, SpecularColor, specularColor, xColor);
DEFINE_PROPERTY(PROP_CONSTANT_ATTENUATION, ConstantAttenuation, constantAttenuation, float);
DEFINE_PROPERTY(PROP_LINEAR_ATTENUATION, LinearAttenuation, linearAttenuation, float);
DEFINE_PROPERTY(PROP_QUADRATIC_ATTENUATION, QuadraticAttenuation, quadraticAttenuation, float);
DEFINE_PROPERTY(PROP_INTENSITY, Intensity, intensity, float);
DEFINE_PROPERTY(PROP_EXPONENT, Exponent, exponent, float);
DEFINE_PROPERTY(PROP_CUTOFF, Cutoff, cutoff, float);
DEFINE_PROPERTY(PROP_LOCKED, Locked, locked, bool);
@ -288,12 +283,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, IgnoreForCollisions, ignoreForCollisions, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionsWillMove, collisionsWillMove, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, IsSpotlight, isSpotlight, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, DiffuseColor, diffuseColor, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientColor, ambientColor, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, SpecularColor, specularColor, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ConstantAttenuation, constantAttenuation, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LinearAttenuation, linearAttenuation, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, QuadraticAttenuation, quadraticAttenuation, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Intensity, intensity, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Exponent, exponent, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cutoff, cutoff, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, "");

View file

@ -14,6 +14,7 @@
#include <ByteCountCoding.h>
#include "EntityItemID.h"
#include "EntityTree.h"
#include "EntityTreeElement.h"
#include "LightEntityItem.h"
@ -32,12 +33,8 @@ LightEntityItem::LightEntityItem(const EntityItemID& entityItemID, const EntityI
// default property values
const quint8 MAX_COLOR = 255;
_ambientColor[RED_INDEX] = _ambientColor[GREEN_INDEX] = _ambientColor[BLUE_INDEX] = 0;
_diffuseColor[RED_INDEX] = _diffuseColor[GREEN_INDEX] = _diffuseColor[BLUE_INDEX] = MAX_COLOR;
_specularColor[RED_INDEX] = _specularColor[GREEN_INDEX] = _specularColor[BLUE_INDEX] = MAX_COLOR;
_constantAttenuation = 1.0f;
_linearAttenuation = 0.0f;
_quadraticAttenuation = 0.0f;
_color[RED_INDEX] = _color[GREEN_INDEX] = _color[BLUE_INDEX] = 0;
_intensity = 1.0f;
_exponent = 0.0f;
_cutoff = PI;
@ -54,12 +51,8 @@ EntityItemProperties LightEntityItem::getProperties() const {
EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class
COPY_ENTITY_PROPERTY_TO_PROPERTIES(isSpotlight, getIsSpotlight);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(diffuseColor, getDiffuseXColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientColor, getAmbientXColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(specularColor, getSpecularXColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(constantAttenuation, getConstantAttenuation);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(linearAttenuation, getLinearAttenuation);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(quadraticAttenuation, getQuadraticAttenuation);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(intensity, getIntensity);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(exponent, getExponent);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(cutoff, getCutoff);
@ -70,17 +63,11 @@ bool LightEntityItem::setProperties(const EntityItemProperties& properties) {
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
SET_ENTITY_PROPERTY_FROM_PROPERTIES(isSpotlight, setIsSpotlight);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(diffuseColor, setDiffuseColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientColor, setAmbientColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(specularColor, setSpecularColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(isSpotlight, setIsSpotlight);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(constantAttenuation, setConstantAttenuation);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(linearAttenuation, setLinearAttenuation);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(quadraticAttenuation, setQuadraticAttenuation);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(intensity, setIntensity);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(exponent, setExponent);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cutoff, setCutoff);
if (somethingChanged) {
bool wantDebug = false;
if (wantDebug) {
@ -101,15 +88,37 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
int bytesRead = 0;
const unsigned char* dataAt = data;
READ_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, bool, _isSpotlight);
READ_ENTITY_PROPERTY_COLOR(PROP_DIFFUSE_COLOR, _diffuseColor);
READ_ENTITY_PROPERTY_COLOR(PROP_AMBIENT_COLOR, _ambientColor);
READ_ENTITY_PROPERTY_COLOR(PROP_SPECULAR_COLOR, _specularColor);
READ_ENTITY_PROPERTY(PROP_CONSTANT_ATTENUATION, float, _constantAttenuation);
READ_ENTITY_PROPERTY(PROP_LINEAR_ATTENUATION, float, _linearAttenuation);
READ_ENTITY_PROPERTY(PROP_QUADRATIC_ATTENUATION, float, _quadraticAttenuation);
READ_ENTITY_PROPERTY(PROP_EXPONENT, float, _exponent);
READ_ENTITY_PROPERTY(PROP_CUTOFF, float, _cutoff);
if (args.bitstreamVersion < VERSION_ENTITIES_LIGHT_HAS_INTENSITY_AND_COLOR_PROPERTIES) {
rgbColor ignoredColor;
float ignoredAttenuation;
READ_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, bool, _isSpotlight);
// _diffuseColor has been renamed to _color
READ_ENTITY_PROPERTY_COLOR(PROP_DIFFUSE_COLOR_UNUSED, _color);
// Ambient and specular color are from an older format and are no longer supported.
// Their values will be ignored.
READ_ENTITY_PROPERTY_COLOR(PROP_AMBIENT_COLOR_UNUSED, ignoredColor);
READ_ENTITY_PROPERTY_COLOR(PROP_SPECULAR_COLOR_UNUSED, ignoredColor);
// _constantAttenuation has been renamed to _intensity
READ_ENTITY_PROPERTY(PROP_INTENSITY, float, _intensity);
// Linear and quadratic attenuation are from an older format and are no longer supported.
// Their values will be ignored.
READ_ENTITY_PROPERTY(PROP_LINEAR_ATTENUATION_UNUSED, float, ignoredAttenuation);
READ_ENTITY_PROPERTY(PROP_QUADRATIC_ATTENUATION_UNUSED, float, ignoredAttenuation);
READ_ENTITY_PROPERTY(PROP_EXPONENT, float, _exponent);
READ_ENTITY_PROPERTY(PROP_CUTOFF, float, _cutoff);
} else {
READ_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, bool, _isSpotlight);
READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color);
READ_ENTITY_PROPERTY(PROP_INTENSITY, float, _intensity);
READ_ENTITY_PROPERTY(PROP_EXPONENT, float, _exponent);
READ_ENTITY_PROPERTY(PROP_CUTOFF, float, _cutoff);
}
return bytesRead;
}
@ -119,12 +128,8 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
EntityPropertyFlags LightEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_IS_SPOTLIGHT;
requestedProperties += PROP_DIFFUSE_COLOR;
requestedProperties += PROP_AMBIENT_COLOR;
requestedProperties += PROP_SPECULAR_COLOR;
requestedProperties += PROP_CONSTANT_ATTENUATION;
requestedProperties += PROP_LINEAR_ATTENUATION;
requestedProperties += PROP_QUADRATIC_ATTENUATION;
requestedProperties += PROP_COLOR;
requestedProperties += PROP_INTENSITY;
requestedProperties += PROP_EXPONENT;
requestedProperties += PROP_CUTOFF;
return requestedProperties;
@ -140,12 +145,8 @@ void LightEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, appendValue, getIsSpotlight());
APPEND_ENTITY_PROPERTY(PROP_DIFFUSE_COLOR, appendColor, getDiffuseColor());
APPEND_ENTITY_PROPERTY(PROP_AMBIENT_COLOR, appendColor, getAmbientColor());
APPEND_ENTITY_PROPERTY(PROP_SPECULAR_COLOR, appendColor, getSpecularColor());
APPEND_ENTITY_PROPERTY(PROP_CONSTANT_ATTENUATION, appendValue, getConstantAttenuation());
APPEND_ENTITY_PROPERTY(PROP_LINEAR_ATTENUATION, appendValue, getLinearAttenuation());
APPEND_ENTITY_PROPERTY(PROP_QUADRATIC_ATTENUATION, appendValue, getQuadraticAttenuation());
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
APPEND_ENTITY_PROPERTY(PROP_INTENSITY, appendValue, getIntensity());
APPEND_ENTITY_PROPERTY(PROP_EXPONENT, appendValue, getExponent());
APPEND_ENTITY_PROPERTY(PROP_CUTOFF, appendValue, getCutoff());
}

View file

@ -43,53 +43,23 @@ public:
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
const rgbColor& getAmbientColor() const { return _ambientColor; }
xColor getAmbientXColor() const {
xColor color = { _ambientColor[RED_INDEX], _ambientColor[GREEN_INDEX], _ambientColor[BLUE_INDEX] }; return color;
const rgbColor& getColor() const { return _color; }
xColor getXColor() const {
xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color;
}
void setAmbientColor(const rgbColor& value) { memcpy(_ambientColor, value, sizeof(_ambientColor)); }
void setAmbientColor(const xColor& value) {
_ambientColor[RED_INDEX] = value.red;
_ambientColor[GREEN_INDEX] = value.green;
_ambientColor[BLUE_INDEX] = value.blue;
}
const rgbColor& getDiffuseColor() const { return _diffuseColor; }
xColor getDiffuseXColor() const {
xColor color = { _diffuseColor[RED_INDEX], _diffuseColor[GREEN_INDEX], _diffuseColor[BLUE_INDEX] }; return color;
}
void setDiffuseColor(const rgbColor& value) { memcpy(_diffuseColor, value, sizeof(_diffuseColor)); }
void setDiffuseColor(const xColor& value) {
_diffuseColor[RED_INDEX] = value.red;
_diffuseColor[GREEN_INDEX] = value.green;
_diffuseColor[BLUE_INDEX] = value.blue;
}
const rgbColor& getSpecularColor() const { return _specularColor; }
xColor getSpecularXColor() const {
xColor color = { _specularColor[RED_INDEX], _specularColor[GREEN_INDEX], _specularColor[BLUE_INDEX] }; return color;
}
void setSpecularColor(const rgbColor& value) { memcpy(_specularColor, value, sizeof(_specularColor)); }
void setSpecularColor(const xColor& value) {
_specularColor[RED_INDEX] = value.red;
_specularColor[GREEN_INDEX] = value.green;
_specularColor[BLUE_INDEX] = value.blue;
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
void setColor(const xColor& value) {
_color[RED_INDEX] = value.red;
_color[GREEN_INDEX] = value.green;
_color[BLUE_INDEX] = value.blue;
}
bool getIsSpotlight() const { return _isSpotlight; }
void setIsSpotlight(bool value) { _isSpotlight = value; }
float getConstantAttenuation() const { return _constantAttenuation; }
void setConstantAttenuation(float value) { _constantAttenuation = value; }
float getLinearAttenuation() const { return _linearAttenuation; }
void setLinearAttenuation(float value) { _linearAttenuation = value; }
float getQuadraticAttenuation() const { return _quadraticAttenuation; }
void setQuadraticAttenuation(float value) { _quadraticAttenuation = value; }
float getIntensity() const { return _intensity; }
void setIntensity(float value) { _intensity = value; }
float getExponent() const { return _exponent; }
void setExponent(float value) { _exponent = value; }
@ -103,13 +73,9 @@ public:
protected:
// properties of a light
rgbColor _ambientColor;
rgbColor _diffuseColor;
rgbColor _specularColor;
rgbColor _color;
bool _isSpotlight;
float _constantAttenuation;
float _linearAttenuation;
float _quadraticAttenuation;
float _intensity;
float _exponent;
float _cutoff;

View file

@ -44,7 +44,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;

View file

@ -648,7 +648,7 @@ void HeightfieldHeightEditor::select() {
const float CONVERSION_SCALE = 65534.0f / numeric_limits<quint16>::max();
for (int i = 0; i < rawSize; i++, dest += size) {
for (quint16* lineDest = dest, *end = dest + rawSize; lineDest != end; lineDest++, src++) {
*lineDest = (quint16)(*src * CONVERSION_SCALE) + CONVERSION_OFFSET;
*lineDest = (*src == 0) ? 0 : (quint16)(*src * CONVERSION_SCALE) + CONVERSION_OFFSET;
}
}
emit heightChanged(_height = new HeightfieldHeight(size, contents));

View file

@ -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 <glm/gtx/transform.hpp>
#include <math.h>
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));
}

View file

@ -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

View file

@ -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();
}
}

View file

@ -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;
}

View file

@ -26,6 +26,7 @@
#include <qsharedpointer.h>
#include <QtNetwork/qudpsocket.h>
#include <QtNetwork/qhostaddress.h>
#include <QSharedMemory>
#include <tbb/concurrent_unordered_map.h>
@ -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();

View file

@ -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();

View file

@ -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) {

View file

@ -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; }

View file

@ -70,9 +70,11 @@ 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;
return VERSION_ENTITIES_LIGHT_HAS_INTENSITY_AND_COLOR_PROPERTIES;
case PacketTypeEntityErase:
return 2;
case PacketTypeAudioStreamStats:
@ -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);

View file

@ -67,7 +67,7 @@ enum PacketType {
PacketTypeEntityErase,
PacketTypeEntityAddResponse,
PacketTypeOctreeDataNack, // 45
UNUSED_10,
PacketTypeStopNode,
PacketTypeAudioEnvironment,
PacketTypeEntityEditNack,
PacketTypeSignedTransactionPayment,
@ -86,7 +86,7 @@ const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
<< 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;
@ -128,6 +128,7 @@ const PacketVersion VERSION_ENTITIES_MODELS_HAVE_ANIMATION_SETTINGS = 5;
const PacketVersion VERSION_ENTITIES_HAVE_USER_DATA = 6;
const PacketVersion VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME = 7;
const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SHAPE_TYPE = 8;
const PacketVersion VERSION_ENTITIES_LIGHT_HAS_INTENSITY_AND_COLOR_PROPERTIES = 9;
const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1;
#endif // hifi_PacketHeaders_h

View file

@ -63,6 +63,10 @@ void EntityMotionState::stepKinematicSimulation(quint64 now) {
_entity->simulate(now);
}
bool EntityMotionState::isMoving() const {
return _entity->isMoving();
}
// This callback is invoked by the physics simulation in two cases:
// (1) when the RigidBody is first added to the world
// (irregardless of MotionType: STATIC, DYNAMIC, or KINEMATIC)

View file

@ -36,28 +36,30 @@ public:
virtual ~EntityMotionState();
/// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem
MotionType computeMotionType() const;
virtual MotionType computeMotionType() const;
void updateKinematicState(uint32_t substep);
void stepKinematicSimulation(quint64 now);
virtual void updateKinematicState(uint32_t substep);
virtual void stepKinematicSimulation(quint64 now);
virtual bool isMoving() const;
// this relays incoming position/rotation to the RigidBody
void getWorldTransform(btTransform& worldTrans) const;
virtual void getWorldTransform(btTransform& worldTrans) const;
// this relays outgoing position/rotation to the EntityItem
void setWorldTransform(const btTransform& worldTrans);
virtual void setWorldTransform(const btTransform& worldTrans);
// these relay incoming values to the RigidBody
void updateObjectEasy(uint32_t flags, uint32_t frame);
void updateObjectVelocities();
virtual void updateObjectEasy(uint32_t flags, uint32_t frame);
virtual void updateObjectVelocities();
void computeShapeInfo(ShapeInfo& shapeInfo);
float computeMass(const ShapeInfo& shapeInfo) const;
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
virtual float computeMass(const ShapeInfo& shapeInfo) const;
void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame);
virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame);
uint32_t getIncomingDirtyFlags() const;
void clearIncomingDirtyFlags(uint32_t flags) { _entity->clearDirtyFlags(flags); }
virtual uint32_t getIncomingDirtyFlags() const;
virtual void clearIncomingDirtyFlags(uint32_t flags) { _entity->clearDirtyFlags(flags); }
EntityItem* getEntity() const { return _entity; }

View file

@ -101,6 +101,8 @@ public:
void setKinematic(bool kinematic, uint32_t substep);
virtual void stepKinematicSimulation(quint64 now) = 0;
virtual bool isMoving() const = 0;
friend class PhysicsEngine;
protected:
void setRigidBody(btRigidBody* body);

View file

@ -439,6 +439,10 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap
const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec
const float DYNAMIC_ANGULAR_VELOCITY_THRESHOLD = 0.087266f; // ~5 deg/sec
body->setSleepingThresholds(DYNAMIC_LINEAR_VELOCITY_THRESHOLD, DYNAMIC_ANGULAR_VELOCITY_THRESHOLD);
if (!motionState->isMoving()) {
// try to initialize this object as inactive
body->forceActivationState(ISLAND_SLEEPING);
}
break;
}
case MOTION_TYPE_STATIC:

View file

@ -1,15 +1,15 @@
set(TARGET_NAME render-utils)
AUTOSCRIBE_SHADER_LIB()
# pull in the resources.qrc file
qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library(Widgets OpenGL Network Script)
add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
set(TARGET_NAME render-utils)
AUTOSCRIBE_SHADER_LIB()
# pull in the resources.qrc file
qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library(Widgets OpenGL Network Script)
add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(animation fbx shared gpu)

View file

@ -95,7 +95,8 @@ vec3 evalAmbienSphereGlobalColor(float shadowAttenuation, vec3 position, vec3 no
vec3 evalLightmappedColor(float shadowAttenuation, vec3 normal, vec3 diffuse, vec3 lightmap) {
Light light = getLight();
float diffuseDot = dot(normal, getLightDirection(light));
vec3 fragNormal = vec3(invViewMat * vec4(normal, 0.0));
float diffuseDot = dot(fragNormal, -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

View file

@ -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);

View file

@ -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); }

View file

@ -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 <AddressManager.h>
#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;
}

View file

@ -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 <qscriptengine.h>
#include <DependencyManager.h>
#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

View file

@ -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<SceneScriptingInterface>();
_entityScriptingInterface.init();
// register various meta-types
@ -353,6 +357,7 @@ void ScriptEngine::init() {
registerGlobalObject("Vec3", &_vec3Library);
registerGlobalObject("Uuid", &_uuidLibrary);
registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
registerGlobalObject("Scene", DependencyManager::get<SceneScriptingInterface>().data());
// constants
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));

View file

@ -101,6 +101,22 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont
}
}
}
// see if this message is one we should only print once
foreach(const QString& regexString, getInstance()._onlyOnceMessageRegexes) {
QRegExp onlyOnceRegex(regexString);
if (onlyOnceRegex.indexIn(message) != -1) {
if (!_onlyOnceMessageCountHash.contains(message)) {
// we have a match and haven't yet printed this message.
_onlyOnceMessageCountHash[message] = 1;
// break the foreach so we output the first match
break;
} else {
// We've already printed this message, don't print it again.
return QString();
}
}
}
}
// log prefix is in the following format

View file

@ -48,6 +48,7 @@ public:
static void verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message);
const QString& addRepeatedMessageRegex(const QString& regexString) { return *_repeatedMessageRegexes.insert(regexString); }
const QString& addOnlyOnceMessageRegex(const QString& regexString) { return *_onlyOnceMessageRegexes.insert(regexString); }
private:
LogHandler();
@ -58,6 +59,9 @@ private:
QSet<QString> _repeatedMessageRegexes;
QHash<QString, int> _repeatMessageCountHash;
QHash<QString, QString> _lastRepeatedMessage;
QSet<QString> _onlyOnceMessageRegexes;
QHash<QString, int> _onlyOnceMessageCountHash;
};
#endif // hifi_LogHandler_h
#endif // hifi_LogHandler_h

View file

@ -35,6 +35,7 @@ ShutdownEventListener::ShutdownEventListener(QObject* parent) :
#ifndef Q_OS_WIN
// be a signal handler for SIGTERM so we can stop our children when we get it
signal(SIGTERM, signalHandler);
signal(SIGINT, signalHandler);
#endif
}

View file

@ -3,3 +3,8 @@ add_subdirectory(bitstream2json)
add_subdirectory(json2bitstream)
add_subdirectory(mtc)
add_subdirectory(scribe)
find_package(VHACD)
if(VHACD_FOUND)
add_subdirectory(vhacd)
endif()

View file

@ -0,0 +1,23 @@
set(TARGET_NAME vhacd)
setup_hifi_project()
link_hifi_libraries(shared model fbx gpu networking octree)
#find_package(VHACD REQUIRED) done in CMakeList.txt in parent directory
target_include_directories(${TARGET_NAME} PUBLIC ${VHACD_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${VHACD_LIBRARIES})
if(NOT WIN32)
find_package( Threads)
target_link_libraries(${TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT})
include(FindOpenMP)
if(OPENMP_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()
endif()
add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})

View file

@ -0,0 +1,129 @@
//
// VHACDUtil.cpp
// tools/vhacd/src
//
// Created by Virendra Singh on 2/20/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 <QVector>
#include "VHACDUtil.h"
//Read all the meshes from provided FBX file
bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *results){
// open the fbx file
QFile fbx(filename);
if (!fbx.open(QIODevice::ReadOnly)) {
return false;
}
std::cout << "Reading FBX.....\n";
QByteArray fbxContents = fbx.readAll();
FBXGeometry geometry = readFBX(fbxContents, QVariantHash());
//results->meshCount = geometry.meshes.count();
int count = 0;
foreach(FBXMesh mesh, geometry.meshes){
//get vertices for each mesh
QVector<glm::vec3> vertices = mesh.vertices;
//get the triangle indices for each mesh
QVector<int> triangles;
foreach(FBXMeshPart part, mesh.parts){
QVector<int> indices = part.triangleIndices;
triangles += indices;
}
//only read meshes with triangles
if (triangles.count() <= 0){
continue;
}
results->perMeshVertices.append(vertices);
results->perMeshTriangleIndices.append(triangles);
count++;
}
results->meshCount = count;
return true;
}
bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD::Parameters params, vhacd::ComputeResults *results)const{
VHACD::IVHACD * interfaceVHACD = VHACD::CreateVHACD();
int meshCount = meshes->meshCount;
int count = 0;
std::cout << "Performing V-HACD computation on " << meshCount << " meshes ..... " << std::endl;
for (int i = 0; i < meshCount; i++){
std::vector<glm::vec3> vertices = meshes->perMeshVertices.at(i).toStdVector();
std::vector<int> triangles = meshes->perMeshTriangleIndices.at(i).toStdVector();
int nPoints = (unsigned int)vertices.size();
int nTriangles = (unsigned int)triangles.size() / 3;
std::cout << "Mesh " << i + 1 << " : ";
// compute approximate convex decomposition
bool res = interfaceVHACD->Compute(&vertices[0].x, 3, nPoints, &triangles[0], 3, nTriangles, params);
if (!res){
std::cout << "V-HACD computation failed for Mesh : " << i + 1 << std::endl;
continue;
}
count++; //For counting number of successfull computations
//Number of hulls for the mesh
unsigned int nConvexHulls = interfaceVHACD->GetNConvexHulls();
results->convexHullsCountList.append(nConvexHulls);
//get all the convex hulls for this mesh
QVector<VHACD::IVHACD::ConvexHull> convexHulls;
for (unsigned int j = 0; j < nConvexHulls; j++){
VHACD::IVHACD::ConvexHull hull;
interfaceVHACD->GetConvexHull(j, hull);
convexHulls.append(hull);
}
results->convexHullList.append(convexHulls);
} //end of for loop
results->meshCount = count;
//release memory
interfaceVHACD->Clean();
interfaceVHACD->Release();
if (count > 0){
return true;
}
else{
return false;
}
}
vhacd::VHACDUtil:: ~VHACDUtil(){
//nothing to be cleaned
}
//ProgressClaback implementation
void vhacd::ProgressCallback::Update(const double overallProgress, const double stageProgress, const double operationProgress,
const char * const stage, const char * const operation){
int progress = (int)(overallProgress + 0.5);
if (progress < 10){
std::cout << "\b\b";
}
else{
std::cout << "\b\b\b";
}
std::cout << progress << "%";
if (progress >= 100){
std::cout << std::endl;
}
}
vhacd::ProgressCallback::ProgressCallback(void){}
vhacd::ProgressCallback::~ProgressCallback(){}

View file

@ -0,0 +1,55 @@
//
// VHACDUtil.h
// tools/vhacd/src
//
// Created by Virendra Singh on 2/20/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_VHACDUtil_h
#define hifi_VHACDUtil_h
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <chrono> //c++11 feature
#include <QFile>
#include <FBXReader.h>
#include <VHACD.h>
namespace vhacd{
typedef struct{
int meshCount;
QVector<int> convexHullsCountList;
QVector<QVector<VHACD::IVHACD::ConvexHull>> convexHullList;
}ComputeResults;
typedef struct{
int meshCount;
QVector<QVector<glm::vec3>> perMeshVertices;
QVector<QVector<int>> perMeshTriangleIndices;
}LoadFBXResults;
class VHACDUtil{
public:
bool loadFBX(const QString filename, vhacd::LoadFBXResults *results);
bool computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD::Parameters params, vhacd::ComputeResults *results)const;
~VHACDUtil();
};
class ProgressCallback : public VHACD::IVHACD::IUserCallback{
public:
ProgressCallback(void);
~ProgressCallback();
//Couldn't follow coding guideline here due to virtual function declared in IUserCallback
void Update(const double overallProgress, const double stageProgress, const double operationProgress,
const char * const stage, const char * const operation);
};
}
#endif //hifi_VHACDUtil_h

111
tools/vhacd/src/main.cpp Normal file
View file

@ -0,0 +1,111 @@
//
// main.cpp
// tools/vhacd/src
//
// Created by Virendra Singh on 2/20/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 <stdio.h>
#include <iostream>
#include <iomanip>
#include <VHACD.h>
#include <string>
#include <vector>
#include "VHACDUtil.h"
using namespace std;
using namespace VHACD;
int main(int argc, char * argv[]){
vector<int> triangles; // array of indexes
vector<float> points; // array of coordinates
vhacd::VHACDUtil vUtil;
vhacd::LoadFBXResults fbx; //mesh data from loaded fbx file
vhacd::ComputeResults results; // results after computing vhacd
VHACD::IVHACD::Parameters params;
vhacd::ProgressCallback pCallBack;
if (argc < 2){
cout << "please provide a FBX file as argument\n ";
return 1;
}
string filename(argv[1]);
if (filename.empty()){
cout << "please provide a FBX file as argument\n ";
return 1;
}
QString fname = QString::fromStdString(filename);
//set parameters for V-HACD
params.m_callback = &pCallBack; //progress callback
params.m_resolution = 50000;
params.m_depth = 10;
params.m_concavity = 0.003;
params.m_alpha = 0.05; // controls the bias toward clipping along symmetry planes
params.m_pca = 1; // enable/disable normalizing the mesh before applying the convex decomposition
params.m_mode = 1; // 0: voxel - based approximate convex decomposition, 1 : tetrahedron - based approximate convex decomposition
params.m_maxNumVerticesPerCH = 128;
params.m_minVolumePerCH = 0.0001; // controls the adaptive sampling of the generated convex - hulls
// load the mesh
auto begin = std::chrono::high_resolution_clock::now();
if (!vUtil.loadFBX(fname, &fbx)){
cout << "Error in opening FBX file....";
return 1;
}
auto end = std::chrono::high_resolution_clock::now();
auto loadDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
//perform vhacd computation
begin = std::chrono::high_resolution_clock::now();
if (!vUtil.computeVHACD(&fbx, params, &results)){
cout << "Compute Failed...";
return 1;
}
end = std::chrono::high_resolution_clock::now();
auto computeDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
int totalVertices = 0;
for (int i = 0; i < fbx.meshCount; i++){
totalVertices += fbx.perMeshVertices.at(i).count();
}
int totalTriangles = 0;
for (int i = 0; i < fbx.meshCount; i++){
totalTriangles += fbx.perMeshTriangleIndices.at(i).count();
}
int totalHulls = 0;
QVector<int> hullCounts = results.convexHullsCountList;
for (int i = 0; i < results.meshCount; i++){
totalHulls += hullCounts.at(i);
}
cout << endl << "Summary of V-HACD Computation..................." << endl;
cout << "File Path : " << fname.toStdString() << endl;
cout << "Number Of Meshes : " << fbx.meshCount << endl;
cout << "Processed Meshes : " << results.meshCount << endl;
cout << "Total vertices : " << totalVertices << endl;
cout << "Total Triangles : " << totalTriangles << endl;
cout << "Total Convex Hulls : " << totalHulls << endl;
cout << "Total FBX load time: " << (double)loadDuration / 1000000000.00 << " seconds" << endl;
cout << "V-HACD Compute time: " << (double)computeDuration / 1000000000.00 << " seconds" << endl;
cout << endl << "Summary per convex hull ........................" << endl <<endl;
for (int i = 0; i < results.meshCount; i++){
cout << "Mesh : " << i + 1 << endl;
QVector<VHACD::IVHACD::ConvexHull> chList = results.convexHullList.at(i);
cout << "\t" << "Number Of Hulls : " << chList.count() << endl;
for (int j = 0; j < results.convexHullList.at(i).count(); j++){
cout << "\tHUll : " << j + 1 << endl;
cout << "\t\tNumber Of Points : " << chList.at(j).m_nPoints << endl;
cout << "\t\tNumber Of Triangles : " << chList.at(j).m_nTriangles << endl;
}
}
getchar();
return 0;
}