mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 11:48:09 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into flock-bots
This commit is contained in:
commit
206096465b
22 changed files with 736 additions and 287 deletions
|
@ -50,11 +50,19 @@ OctreeSendThread::~OctreeSendThread() {
|
||||||
void OctreeSendThread::setIsShuttingDown() {
|
void OctreeSendThread::setIsShuttingDown() {
|
||||||
QMutexLocker locker(&_processLock); // this will cause us to wait till the process loop is complete
|
QMutexLocker locker(&_processLock); // this will cause us to wait till the process loop is complete
|
||||||
_isShuttingDown = true;
|
_isShuttingDown = true;
|
||||||
|
OctreeServer::stopTrackingThread(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool OctreeSendThread::process() {
|
bool OctreeSendThread::process() {
|
||||||
|
OctreeServer::didProcess(this);
|
||||||
|
|
||||||
|
float lockWaitElapsedUsec = OctreeServer::SKIP_TIME;
|
||||||
|
quint64 lockWaitStart = usecTimestampNow();
|
||||||
QMutexLocker locker(&_processLock);
|
QMutexLocker locker(&_processLock);
|
||||||
|
quint64 lockWaitEnd = usecTimestampNow();
|
||||||
|
lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart);
|
||||||
|
OctreeServer::trackProcessWaitTime(lockWaitElapsedUsec);
|
||||||
|
|
||||||
if (_isShuttingDown) {
|
if (_isShuttingDown) {
|
||||||
return false; // exit early if we're shutting down
|
return false; // exit early if we're shutting down
|
||||||
|
@ -119,6 +127,8 @@ quint64 OctreeSendThread::_totalPackets = 0;
|
||||||
|
|
||||||
int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
|
int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
|
||||||
OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
|
OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
|
||||||
|
|
||||||
|
OctreeServer::didHandlePacketSend(this);
|
||||||
|
|
||||||
// if we're shutting down, then exit early
|
// if we're shutting down, then exit early
|
||||||
if (nodeData->isShuttingDown()) {
|
if (nodeData->isShuttingDown()) {
|
||||||
|
@ -175,10 +185,12 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
|
||||||
}
|
}
|
||||||
|
|
||||||
// actually send it
|
// actually send it
|
||||||
|
OctreeServer::didCallWriteDatagram(this);
|
||||||
NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, SharedNodePointer(node));
|
NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, SharedNodePointer(node));
|
||||||
packetSent = true;
|
packetSent = true;
|
||||||
} else {
|
} else {
|
||||||
// not enough room in the packet, send two packets
|
// not enough room in the packet, send two packets
|
||||||
|
OctreeServer::didCallWriteDatagram(this);
|
||||||
NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, SharedNodePointer(node));
|
NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, SharedNodePointer(node));
|
||||||
|
|
||||||
// since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
|
// since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
|
||||||
|
@ -197,6 +209,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
|
||||||
truePacketsSent++;
|
truePacketsSent++;
|
||||||
packetsSent++;
|
packetsSent++;
|
||||||
|
|
||||||
|
OctreeServer::didCallWriteDatagram(this);
|
||||||
NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(),
|
NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(),
|
||||||
SharedNodePointer(node));
|
SharedNodePointer(node));
|
||||||
|
|
||||||
|
@ -217,6 +230,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
|
||||||
// If there's actually a packet waiting, then send it.
|
// If there's actually a packet waiting, then send it.
|
||||||
if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) {
|
if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) {
|
||||||
// just send the voxel packet
|
// just send the voxel packet
|
||||||
|
OctreeServer::didCallWriteDatagram(this);
|
||||||
NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(),
|
NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(),
|
||||||
SharedNodePointer(node));
|
SharedNodePointer(node));
|
||||||
packetSent = true;
|
packetSent = true;
|
||||||
|
@ -246,6 +260,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node,
|
||||||
|
|
||||||
/// Version of voxel distributor that sends the deepest LOD level at once
|
/// Version of voxel distributor that sends the deepest LOD level at once
|
||||||
int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged) {
|
int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged) {
|
||||||
|
OctreeServer::didPacketDistributor(this);
|
||||||
|
|
||||||
// if shutting down, exit early
|
// if shutting down, exit early
|
||||||
if (nodeData->isShuttingDown()) {
|
if (nodeData->isShuttingDown()) {
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <QtCore/QTimer>
|
#include <QJsonObject>
|
||||||
#include <QtCore/QUuid>
|
#include <QNetworkAccessManager>
|
||||||
#include <QtNetwork/QNetworkAccessManager>
|
#include <QTimer>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <HTTPConnection.h>
|
#include <HTTPConnection.h>
|
||||||
|
@ -59,6 +60,15 @@ int OctreeServer::_noCompress = 0;
|
||||||
SimpleMovingAverage OctreeServer::_averagePacketSendingTime(MOVING_AVERAGE_SAMPLE_COUNTS);
|
SimpleMovingAverage OctreeServer::_averagePacketSendingTime(MOVING_AVERAGE_SAMPLE_COUNTS);
|
||||||
int OctreeServer::_noSend = 0;
|
int OctreeServer::_noSend = 0;
|
||||||
|
|
||||||
|
SimpleMovingAverage OctreeServer::_averageProcessWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS);
|
||||||
|
SimpleMovingAverage OctreeServer::_averageProcessShortWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS);
|
||||||
|
SimpleMovingAverage OctreeServer::_averageProcessLongWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS);
|
||||||
|
SimpleMovingAverage OctreeServer::_averageProcessExtraLongWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS);
|
||||||
|
int OctreeServer::_extraLongProcessWait = 0;
|
||||||
|
int OctreeServer::_longProcessWait = 0;
|
||||||
|
int OctreeServer::_shortProcessWait = 0;
|
||||||
|
int OctreeServer::_noProcessWait = 0;
|
||||||
|
|
||||||
|
|
||||||
void OctreeServer::resetSendingStats() {
|
void OctreeServer::resetSendingStats() {
|
||||||
_averageLoopTime.reset();
|
_averageLoopTime.reset();
|
||||||
|
@ -95,6 +105,15 @@ void OctreeServer::resetSendingStats() {
|
||||||
|
|
||||||
_averagePacketSendingTime.reset();
|
_averagePacketSendingTime.reset();
|
||||||
_noSend = 0;
|
_noSend = 0;
|
||||||
|
|
||||||
|
_averageProcessWaitTime.reset();
|
||||||
|
_averageProcessShortWaitTime.reset();
|
||||||
|
_averageProcessLongWaitTime.reset();
|
||||||
|
_averageProcessExtraLongWaitTime.reset();
|
||||||
|
_extraLongProcessWait = 0;
|
||||||
|
_longProcessWait = 0;
|
||||||
|
_shortProcessWait = 0;
|
||||||
|
_noProcessWait = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreeServer::trackEncodeTime(float time) {
|
void OctreeServer::trackEncodeTime(float time) {
|
||||||
|
@ -164,6 +183,24 @@ void OctreeServer::trackPacketSendingTime(float time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OctreeServer::trackProcessWaitTime(float time) {
|
||||||
|
const float MAX_SHORT_TIME = 10.0f;
|
||||||
|
const float MAX_LONG_TIME = 100.0f;
|
||||||
|
if (time == SKIP_TIME) {
|
||||||
|
_noProcessWait++;
|
||||||
|
time = 0.0f;
|
||||||
|
} else if (time <= MAX_SHORT_TIME) {
|
||||||
|
_shortProcessWait++;
|
||||||
|
_averageProcessShortWaitTime.updateAverage(time);
|
||||||
|
} else if (time <= MAX_LONG_TIME) {
|
||||||
|
_longProcessWait++;
|
||||||
|
_averageProcessLongWaitTime.updateAverage(time);
|
||||||
|
} else {
|
||||||
|
_extraLongProcessWait++;
|
||||||
|
_averageProcessExtraLongWaitTime.updateAverage(time);
|
||||||
|
}
|
||||||
|
_averageProcessWaitTime.updateAverage(time);
|
||||||
|
}
|
||||||
|
|
||||||
void OctreeServer::attachQueryNodeToNode(Node* newNode) {
|
void OctreeServer::attachQueryNodeToNode(Node* newNode) {
|
||||||
if (!newNode->getLinkedData()) {
|
if (!newNode->getLinkedData()) {
|
||||||
|
@ -179,6 +216,7 @@ OctreeServer::OctreeServer(const QByteArray& packet) :
|
||||||
_argv(NULL),
|
_argv(NULL),
|
||||||
_parsedArgV(NULL),
|
_parsedArgV(NULL),
|
||||||
_httpManager(NULL),
|
_httpManager(NULL),
|
||||||
|
_statusPort(0),
|
||||||
_packetsPerClientPerInterval(10),
|
_packetsPerClientPerInterval(10),
|
||||||
_packetsTotalPerInterval(DEFAULT_PACKETS_PER_INTERVAL),
|
_packetsTotalPerInterval(DEFAULT_PACKETS_PER_INTERVAL),
|
||||||
_tree(NULL),
|
_tree(NULL),
|
||||||
|
@ -289,41 +327,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
statsString += "\r\n";
|
statsString += "\r\n";
|
||||||
|
|
||||||
quint64 now = usecTimestampNow();
|
statsString += "Uptime: " + getUptime();
|
||||||
const int USECS_PER_MSEC = 1000;
|
|
||||||
quint64 msecsElapsed = (now - _startedUSecs) / USECS_PER_MSEC;
|
|
||||||
const int MSECS_PER_SEC = 1000;
|
|
||||||
const int SECS_PER_MIN = 60;
|
|
||||||
const int MIN_PER_HOUR = 60;
|
|
||||||
const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
|
|
||||||
|
|
||||||
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
|
|
||||||
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
|
||||||
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
|
|
||||||
|
|
||||||
statsString += "Uptime: ";
|
|
||||||
|
|
||||||
if (hours > 0) {
|
|
||||||
statsString += QString("%1 hour").arg(hours);
|
|
||||||
if (hours > 1) {
|
|
||||||
statsString += QString("s");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (minutes > 0) {
|
|
||||||
if (hours > 0) {
|
|
||||||
statsString += QString(" ");
|
|
||||||
}
|
|
||||||
statsString += QString("%1 minute").arg(minutes);
|
|
||||||
if (minutes > 1) {
|
|
||||||
statsString += QString("s");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (seconds > 0) {
|
|
||||||
if (hours > 0 || minutes > 0) {
|
|
||||||
statsString += QString(" ");
|
|
||||||
}
|
|
||||||
statsString += QString().sprintf("%.3f seconds", seconds);
|
|
||||||
}
|
|
||||||
statsString += "\r\n\r\n";
|
statsString += "\r\n\r\n";
|
||||||
|
|
||||||
// display voxel file load time
|
// display voxel file load time
|
||||||
|
@ -336,33 +340,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
statsString += "\r\n";
|
statsString += "\r\n";
|
||||||
|
|
||||||
quint64 msecsElapsed = getLoadElapsedTime() / USECS_PER_MSEC;;
|
|
||||||
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
|
|
||||||
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
|
||||||
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
|
|
||||||
|
|
||||||
statsString += QString("%1 File Load Took ").arg(getMyServerName());
|
statsString += QString("%1 File Load Took ").arg(getMyServerName());
|
||||||
if (hours > 0) {
|
statsString += getFileLoadTime();
|
||||||
statsString += QString("%1 hour").arg(hours);
|
|
||||||
if (hours > 1) {
|
|
||||||
statsString += QString("s");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (minutes > 0) {
|
|
||||||
if (hours > 0) {
|
|
||||||
statsString += QString(" ");
|
|
||||||
}
|
|
||||||
statsString += QString("%1 minute").arg(minutes);
|
|
||||||
if (minutes > 1) {
|
|
||||||
statsString += QString("s");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (seconds >= 0) {
|
|
||||||
if (hours > 0 || minutes > 0) {
|
|
||||||
statsString += QString(" ");
|
|
||||||
}
|
|
||||||
statsString += QString().sprintf("%.3f seconds", seconds);
|
|
||||||
}
|
|
||||||
statsString += "\r\n";
|
statsString += "\r\n";
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -371,27 +350,32 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
statsString += "\r\n\r\n";
|
statsString += "\r\n\r\n";
|
||||||
statsString += "<b>Configuration:</b>\r\n";
|
statsString += "<b>Configuration:</b>\r\n";
|
||||||
|
statsString += getConfiguration() + "\r\n"; //one to end the config line
|
||||||
|
statsString += "\r\n";
|
||||||
|
|
||||||
|
const int COLUMN_WIDTH = 19;
|
||||||
|
QLocale locale(QLocale::English);
|
||||||
|
const float AS_PERCENT = 100.0;
|
||||||
|
|
||||||
|
statsString += QString(" Configured Max PPS/Client: %1 pps/client\r\n")
|
||||||
|
.arg(locale.toString((uint)getPacketsPerClientPerSecond()).rightJustified(COLUMN_WIDTH, ' '));
|
||||||
|
statsString += QString(" Configured Max PPS/Server: %1 pps/server\r\n\r\n")
|
||||||
|
.arg(locale.toString((uint)getPacketsTotalPerSecond()).rightJustified(COLUMN_WIDTH, ' '));
|
||||||
|
|
||||||
for (int i = 1; i < _argc; i++) {
|
|
||||||
statsString += _argv[i];
|
|
||||||
}
|
|
||||||
statsString += "\r\n"; //one to end the config line
|
|
||||||
statsString += "\r\n\r\n"; // two more for spacing
|
|
||||||
|
|
||||||
// display scene stats
|
// display scene stats
|
||||||
unsigned long nodeCount = OctreeElement::getNodeCount();
|
unsigned long nodeCount = OctreeElement::getNodeCount();
|
||||||
unsigned long internalNodeCount = OctreeElement::getInternalNodeCount();
|
unsigned long internalNodeCount = OctreeElement::getInternalNodeCount();
|
||||||
unsigned long leafNodeCount = OctreeElement::getLeafNodeCount();
|
unsigned long leafNodeCount = OctreeElement::getLeafNodeCount();
|
||||||
|
|
||||||
QLocale locale(QLocale::English);
|
statsString += "<b>Current Elements in scene:</b>\r\n";
|
||||||
const float AS_PERCENT = 100.0;
|
statsString += QString(" Total Elements: %1 nodes\r\n")
|
||||||
statsString += "<b>Current Nodes in scene:</b>\r\n";
|
.arg(locale.toString((uint)nodeCount).rightJustified(16, ' '));
|
||||||
statsString += QString(" Total Nodes: %1 nodes\r\n").arg(locale.toString((uint)nodeCount).rightJustified(16, ' '));
|
statsString += QString().sprintf(" Internal Elements: %s nodes (%5.2f%%)\r\n",
|
||||||
statsString += QString().sprintf(" Internal Nodes: %s nodes (%5.2f%%)\r\n",
|
|
||||||
locale.toString((uint)internalNodeCount).rightJustified(16,
|
locale.toString((uint)internalNodeCount).rightJustified(16,
|
||||||
' ').toLocal8Bit().constData(),
|
' ').toLocal8Bit().constData(),
|
||||||
((float)internalNodeCount / (float)nodeCount) * AS_PERCENT);
|
((float)internalNodeCount / (float)nodeCount) * AS_PERCENT);
|
||||||
statsString += QString().sprintf(" Leaf Nodes: %s nodes (%5.2f%%)\r\n",
|
statsString += QString().sprintf(" Leaf Elements: %s nodes (%5.2f%%)\r\n",
|
||||||
locale.toString((uint)leafNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
|
locale.toString((uint)leafNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
|
||||||
((float)leafNodeCount / (float)nodeCount) * AS_PERCENT);
|
((float)leafNodeCount / (float)nodeCount) * AS_PERCENT);
|
||||||
statsString += "\r\n";
|
statsString += "\r\n";
|
||||||
|
@ -408,20 +392,65 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
quint64 totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks();
|
quint64 totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks();
|
||||||
quint64 totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
|
quint64 totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
|
||||||
|
|
||||||
const int COLUMN_WIDTH = 19;
|
statsString += QString(" Total Clients Connected: %1 clients\r\n")
|
||||||
statsString += QString(" Configured Max PPS/Client: %1 pps/client\r\n")
|
|
||||||
.arg(locale.toString((uint)getPacketsPerClientPerSecond()).rightJustified(COLUMN_WIDTH, ' '));
|
|
||||||
statsString += QString(" Configured Max PPS/Server: %1 pps/server\r\n\r\n")
|
|
||||||
.arg(locale.toString((uint)getPacketsTotalPerSecond()).rightJustified(COLUMN_WIDTH, ' '));
|
|
||||||
statsString += QString(" Total Clients Connected: %1 clients\r\n\r\n")
|
|
||||||
.arg(locale.toString((uint)getCurrentClientCount()).rightJustified(COLUMN_WIDTH, ' '));
|
.arg(locale.toString((uint)getCurrentClientCount()).rightJustified(COLUMN_WIDTH, ' '));
|
||||||
|
|
||||||
|
quint64 oneSecondAgo = usecTimestampNow() - USECS_PER_SECOND;
|
||||||
|
|
||||||
|
statsString += QString(" process() last second: %1 clients\r\n")
|
||||||
|
.arg(locale.toString((uint)howManyThreadsDidProcess(oneSecondAgo)).rightJustified(COLUMN_WIDTH, ' '));
|
||||||
|
statsString += QString(" packetDistributor() last second: %1 clients\r\n")
|
||||||
|
.arg(locale.toString((uint)howManyThreadsDidPacketDistributor(oneSecondAgo)).rightJustified(COLUMN_WIDTH, ' '));
|
||||||
|
statsString += QString(" handlePacketSend() last second: %1 clients\r\n")
|
||||||
|
.arg(locale.toString((uint)howManyThreadsDidHandlePacketSend(oneSecondAgo)).rightJustified(COLUMN_WIDTH, ' '));
|
||||||
|
statsString += QString(" writeDatagram() last second: %1 clients\r\n\r\n")
|
||||||
|
.arg(locale.toString((uint)howManyThreadsDidCallWriteDatagram(oneSecondAgo)).rightJustified(COLUMN_WIDTH, ' '));
|
||||||
|
|
||||||
float averageLoopTime = getAverageLoopTime();
|
float averageLoopTime = getAverageLoopTime();
|
||||||
statsString += QString().sprintf(" Average packetLoop() time: %7.2f msecs\r\n", averageLoopTime);
|
statsString += QString().sprintf(" Average packetLoop() time: %7.2f msecs"
|
||||||
|
" samples: %12d \r\n",
|
||||||
|
averageLoopTime, _averageLoopTime.getSampleCount());
|
||||||
|
|
||||||
float averageInsideTime = getAverageInsideTime();
|
float averageInsideTime = getAverageInsideTime();
|
||||||
statsString += QString().sprintf(" Average 'inside' time: %9.2f usecs\r\n\r\n", averageInsideTime);
|
statsString += QString().sprintf(" Average 'inside' time: %9.2f usecs"
|
||||||
|
" samples: %12d \r\n\r\n",
|
||||||
|
averageInsideTime, _averageInsideTime.getSampleCount());
|
||||||
|
|
||||||
|
|
||||||
|
// Process Wait
|
||||||
|
{
|
||||||
|
int allWaitTimes = _extraLongProcessWait +_longProcessWait + _shortProcessWait + _noProcessWait;
|
||||||
|
|
||||||
|
float averageProcessWaitTime = getAverageProcessWaitTime();
|
||||||
|
statsString += QString().sprintf(" Average process lock wait time:"
|
||||||
|
" %9.2f usecs samples: %12d \r\n",
|
||||||
|
averageProcessWaitTime, allWaitTimes);
|
||||||
|
|
||||||
|
float zeroVsTotal = (allWaitTimes > 0) ? ((float)_noProcessWait / (float)allWaitTimes) : 0.0f;
|
||||||
|
statsString += QString().sprintf(" No Lock Wait:"
|
||||||
|
" (%6.2f%%) samples: %12d \r\n",
|
||||||
|
zeroVsTotal * AS_PERCENT, _noProcessWait);
|
||||||
|
|
||||||
|
float shortVsTotal = (allWaitTimes > 0) ? ((float)_shortProcessWait / (float)allWaitTimes) : 0.0f;
|
||||||
|
statsString += QString().sprintf(" Avg process lock short wait time:"
|
||||||
|
" %9.2f usecs (%6.2f%%) samples: %12d \r\n",
|
||||||
|
_averageProcessShortWaitTime.getAverage(),
|
||||||
|
shortVsTotal * AS_PERCENT, _shortProcessWait);
|
||||||
|
|
||||||
|
float longVsTotal = (allWaitTimes > 0) ? ((float)_longProcessWait / (float)allWaitTimes) : 0.0f;
|
||||||
|
statsString += QString().sprintf(" Avg process lock long wait time:"
|
||||||
|
" %9.2f usecs (%6.2f%%) samples: %12d \r\n",
|
||||||
|
_averageProcessLongWaitTime.getAverage(),
|
||||||
|
longVsTotal * AS_PERCENT, _longProcessWait);
|
||||||
|
|
||||||
|
float extraLongVsTotal = (allWaitTimes > 0) ? ((float)_extraLongProcessWait / (float)allWaitTimes) : 0.0f;
|
||||||
|
statsString += QString().sprintf("Avg process lock extralong wait time:"
|
||||||
|
" %9.2f usecs (%6.2f%%) samples: %12d \r\n\r\n",
|
||||||
|
_averageProcessExtraLongWaitTime.getAverage(),
|
||||||
|
extraLongVsTotal * AS_PERCENT, _extraLongProcessWait);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tree Wait
|
||||||
int allWaitTimes = _extraLongTreeWait +_longTreeWait + _shortTreeWait + _noTreeWait;
|
int allWaitTimes = _extraLongTreeWait +_longTreeWait + _shortTreeWait + _noTreeWait;
|
||||||
|
|
||||||
float averageTreeWaitTime = getAverageTreeWaitTime();
|
float averageTreeWaitTime = getAverageTreeWaitTime();
|
||||||
|
@ -452,6 +481,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
_averageTreeExtraLongWaitTime.getAverage(),
|
_averageTreeExtraLongWaitTime.getAverage(),
|
||||||
extraLongVsTotal * AS_PERCENT, _extraLongTreeWait);
|
extraLongVsTotal * AS_PERCENT, _extraLongTreeWait);
|
||||||
|
|
||||||
|
// encode
|
||||||
float averageEncodeTime = getAverageEncodeTime();
|
float averageEncodeTime = getAverageEncodeTime();
|
||||||
statsString += QString().sprintf(" Average encode time: %9.2f usecs\r\n", averageEncodeTime);
|
statsString += QString().sprintf(" Average encode time: %9.2f usecs\r\n", averageEncodeTime);
|
||||||
|
|
||||||
|
@ -482,7 +512,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
|
|
||||||
float averageCompressAndWriteTime = getAverageCompressAndWriteTime();
|
float averageCompressAndWriteTime = getAverageCompressAndWriteTime();
|
||||||
statsString += QString().sprintf(" Average compress and write time: %9.2f usecs\r\n", averageCompressAndWriteTime);
|
statsString += QString().sprintf(" Average compress and write time: %9.2f usecs\r\n",
|
||||||
|
averageCompressAndWriteTime);
|
||||||
|
|
||||||
int allCompressTimes = _noCompress + _shortCompress + _longCompress + _extraLongCompress;
|
int allCompressTimes = _noCompress + _shortCompress + _longCompress + _extraLongCompress;
|
||||||
|
|
||||||
|
@ -668,8 +699,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
for (int i=0; i <= NUMBER_OF_CHILDREN; i++) {
|
for (int i=0; i <= NUMBER_OF_CHILDREN; i++) {
|
||||||
checkSum += OctreeElement::getChildrenCount(i);
|
checkSum += OctreeElement::getChildrenCount(i);
|
||||||
statsString += QString().sprintf(" Nodes with %d children: %s nodes (%5.2f%%)\r\n", i,
|
statsString += QString().sprintf(" Nodes with %d children: %s nodes (%5.2f%%)\r\n", i,
|
||||||
locale.toString((uint)OctreeElement::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(),
|
locale.toString((uint)OctreeElement::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(),
|
||||||
((float)OctreeElement::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT);
|
((float)OctreeElement::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT);
|
||||||
}
|
}
|
||||||
statsString += " ----------------------\r\n";
|
statsString += " ----------------------\r\n";
|
||||||
statsString += QString(" Total: %1 nodes\r\n")
|
statsString += QString(" Total: %1 nodes\r\n")
|
||||||
|
@ -680,23 +711,23 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
statsString += "OctreeElement Children Encoding Statistics...\r\n";
|
statsString += "OctreeElement Children Encoding Statistics...\r\n";
|
||||||
|
|
||||||
statsString += QString().sprintf(" Single or No Children: %10.llu nodes (%5.2f%%)\r\n",
|
statsString += QString().sprintf(" Single or No Children: %10.llu nodes (%5.2f%%)\r\n",
|
||||||
OctreeElement::getSingleChildrenCount(),
|
OctreeElement::getSingleChildrenCount(),
|
||||||
((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT));
|
((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT));
|
||||||
statsString += QString().sprintf(" Two Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
|
statsString += QString().sprintf(" Two Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
|
||||||
OctreeElement::getTwoChildrenOffsetCount(),
|
OctreeElement::getTwoChildrenOffsetCount(),
|
||||||
((float)OctreeElement::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT));
|
((float)OctreeElement::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT));
|
||||||
statsString += QString().sprintf(" Two Children as External: %10.llu nodes (%5.2f%%)\r\n",
|
statsString += QString().sprintf(" Two Children as External: %10.llu nodes (%5.2f%%)\r\n",
|
||||||
OctreeElement::getTwoChildrenExternalCount(),
|
OctreeElement::getTwoChildrenExternalCount(),
|
||||||
((float)OctreeElement::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
|
((float)OctreeElement::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
|
||||||
statsString += QString().sprintf(" Three Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
|
statsString += QString().sprintf(" Three Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
|
||||||
OctreeElement::getThreeChildrenOffsetCount(),
|
OctreeElement::getThreeChildrenOffsetCount(),
|
||||||
((float)OctreeElement::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
|
((float)OctreeElement::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
|
||||||
statsString += QString().sprintf(" Three Children as External: %10.llu nodes (%5.2f%%)\r\n",
|
statsString += QString().sprintf(" Three Children as External: %10.llu nodes (%5.2f%%)\r\n",
|
||||||
OctreeElement::getThreeChildrenExternalCount(),
|
OctreeElement::getThreeChildrenExternalCount(),
|
||||||
((float)OctreeElement::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
|
((float)OctreeElement::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
|
||||||
statsString += QString().sprintf(" Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
|
statsString += QString().sprintf(" Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
|
||||||
OctreeElement::getExternalChildrenCount(),
|
OctreeElement::getExternalChildrenCount(),
|
||||||
((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
|
((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
|
||||||
|
|
||||||
checkSum = OctreeElement::getSingleChildrenCount() +
|
checkSum = OctreeElement::getSingleChildrenCount() +
|
||||||
OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() +
|
OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() +
|
||||||
|
@ -839,9 +870,19 @@ void OctreeServer::run() {
|
||||||
const char* STATUS_PORT = "--statusPort";
|
const char* STATUS_PORT = "--statusPort";
|
||||||
const char* statusPort = getCmdOption(_argc, _argv, STATUS_PORT);
|
const char* statusPort = getCmdOption(_argc, _argv, STATUS_PORT);
|
||||||
if (statusPort) {
|
if (statusPort) {
|
||||||
int statusPortNumber = atoi(statusPort);
|
_statusPort = atoi(statusPort);
|
||||||
initHTTPManager(statusPortNumber);
|
initHTTPManager(_statusPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* STATUS_HOST = "--statusHost";
|
||||||
|
const char* statusHost = getCmdOption(_argc, _argv, STATUS_HOST);
|
||||||
|
if (statusHost) {
|
||||||
|
qDebug("--statusHost=%s", statusHost);
|
||||||
|
_statusHost = statusHost;
|
||||||
|
} else {
|
||||||
|
_statusHost = QHostAddress(getHostOrderLocalAddress()).toString();
|
||||||
|
}
|
||||||
|
qDebug("statusHost=%s", qPrintable(_statusHost));
|
||||||
|
|
||||||
|
|
||||||
const char* JURISDICTION_FILE = "--jurisdictionFile";
|
const char* JURISDICTION_FILE = "--jurisdictionFile";
|
||||||
|
@ -1017,3 +1058,240 @@ void OctreeServer::aboutToFinish() {
|
||||||
qDebug() << qPrintable(_safeServerName) << "server ENDING about to finish...";
|
qDebug() << qPrintable(_safeServerName) << "server ENDING about to finish...";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString OctreeServer::getUptime() {
|
||||||
|
QString formattedUptime;
|
||||||
|
quint64 now = usecTimestampNow();
|
||||||
|
const int USECS_PER_MSEC = 1000;
|
||||||
|
quint64 msecsElapsed = (now - _startedUSecs) / USECS_PER_MSEC;
|
||||||
|
const int MSECS_PER_SEC = 1000;
|
||||||
|
const int SECS_PER_MIN = 60;
|
||||||
|
const int MIN_PER_HOUR = 60;
|
||||||
|
const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
|
||||||
|
|
||||||
|
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
|
||||||
|
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
||||||
|
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
|
||||||
|
|
||||||
|
if (hours > 0) {
|
||||||
|
formattedUptime += QString("%1 hour").arg(hours);
|
||||||
|
if (hours > 1) {
|
||||||
|
formattedUptime += QString("s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (minutes > 0) {
|
||||||
|
if (hours > 0) {
|
||||||
|
formattedUptime += QString(" ");
|
||||||
|
}
|
||||||
|
formattedUptime += QString("%1 minute").arg(minutes);
|
||||||
|
if (minutes > 1) {
|
||||||
|
formattedUptime += QString("s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (seconds > 0) {
|
||||||
|
if (hours > 0 || minutes > 0) {
|
||||||
|
formattedUptime += QString(" ");
|
||||||
|
}
|
||||||
|
formattedUptime += QString().sprintf("%.3f seconds", seconds);
|
||||||
|
}
|
||||||
|
return formattedUptime;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString OctreeServer::getFileLoadTime() {
|
||||||
|
QString result;
|
||||||
|
if (isInitialLoadComplete()) {
|
||||||
|
|
||||||
|
const int USECS_PER_MSEC = 1000;
|
||||||
|
const int MSECS_PER_SEC = 1000;
|
||||||
|
const int SECS_PER_MIN = 60;
|
||||||
|
const int MIN_PER_HOUR = 60;
|
||||||
|
const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
|
||||||
|
|
||||||
|
quint64 msecsElapsed = getLoadElapsedTime() / USECS_PER_MSEC;;
|
||||||
|
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
|
||||||
|
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
||||||
|
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
|
||||||
|
|
||||||
|
if (hours > 0) {
|
||||||
|
result += QString("%1 hour").arg(hours);
|
||||||
|
if (hours > 1) {
|
||||||
|
result += QString("s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (minutes > 0) {
|
||||||
|
if (hours > 0) {
|
||||||
|
result += QString(" ");
|
||||||
|
}
|
||||||
|
result += QString("%1 minute").arg(minutes);
|
||||||
|
if (minutes > 1) {
|
||||||
|
result += QString("s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (seconds >= 0) {
|
||||||
|
if (hours > 0 || minutes > 0) {
|
||||||
|
result += QString(" ");
|
||||||
|
}
|
||||||
|
result += QString().sprintf("%.3f seconds", seconds);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = "Not yet loaded...";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString OctreeServer::getConfiguration() {
|
||||||
|
QString result;
|
||||||
|
for (int i = 1; i < _argc; i++) {
|
||||||
|
result += _argv[i] + QString(" ");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString OctreeServer::getStatusLink() {
|
||||||
|
QString result;
|
||||||
|
if (_statusPort > 0) {
|
||||||
|
QString detailedStats= QString("http://") + _statusHost + QString(":%1").arg(_statusPort);
|
||||||
|
result = "<a href='" + detailedStats + "'>"+detailedStats+"</a>";
|
||||||
|
} else {
|
||||||
|
result = "Status port not enabled.";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeServer::sendStatsPacket() {
|
||||||
|
|
||||||
|
// TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and
|
||||||
|
// send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the
|
||||||
|
// the following features:
|
||||||
|
// 1) remember last state sent
|
||||||
|
// 2) only send new data
|
||||||
|
// 3) automatically break up into multiple packets
|
||||||
|
static QJsonObject statsObject1;
|
||||||
|
|
||||||
|
QString baseName = getMyServerName() + QString("Server");
|
||||||
|
|
||||||
|
statsObject1[baseName + QString(".0.1.configuration")] = getConfiguration();
|
||||||
|
|
||||||
|
statsObject1[baseName + QString(".0.2.detailed_stats_url")] = getStatusLink();
|
||||||
|
|
||||||
|
statsObject1[baseName + QString(".0.3.uptime")] = getUptime();
|
||||||
|
statsObject1[baseName + QString(".0.4.persistFileLoadTime")] = getFileLoadTime();
|
||||||
|
statsObject1[baseName + QString(".0.5.clients")] = getCurrentClientCount();
|
||||||
|
|
||||||
|
quint64 oneSecondAgo = usecTimestampNow() - USECS_PER_SECOND;
|
||||||
|
|
||||||
|
statsObject1[baseName + QString(".0.6.threads.1.processing")] = (double)howManyThreadsDidProcess(oneSecondAgo);
|
||||||
|
statsObject1[baseName + QString(".0.6.threads.2.packetDistributor")] =
|
||||||
|
(double)howManyThreadsDidPacketDistributor(oneSecondAgo);
|
||||||
|
statsObject1[baseName + QString(".0.6.threads.3.handlePacektSend")] =
|
||||||
|
(double)howManyThreadsDidHandlePacketSend(oneSecondAgo);
|
||||||
|
statsObject1[baseName + QString(".0.6.threads.4.writeDatagram")] =
|
||||||
|
(double)howManyThreadsDidCallWriteDatagram(oneSecondAgo);
|
||||||
|
|
||||||
|
statsObject1[baseName + QString(".1.1.octree.elementCount")] = (double)OctreeElement::getNodeCount();
|
||||||
|
statsObject1[baseName + QString(".1.2.octree.internalElementCount")] = (double)OctreeElement::getInternalNodeCount();
|
||||||
|
statsObject1[baseName + QString(".1.3.octree.leafElementCount")] = (double)OctreeElement::getLeafNodeCount();
|
||||||
|
|
||||||
|
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject1);
|
||||||
|
|
||||||
|
static QJsonObject statsObject2;
|
||||||
|
|
||||||
|
statsObject2[baseName + QString(".2.outbound.data.totalPackets")] = (double)OctreeSendThread::_totalPackets;
|
||||||
|
statsObject2[baseName + QString(".2.outbound.data.totalBytes")] = (double)OctreeSendThread::_totalBytes;
|
||||||
|
statsObject2[baseName + QString(".2.outbound.data.totalBytesWasted")] = (double)OctreeSendThread::_totalWastedBytes;
|
||||||
|
statsObject2[baseName + QString(".2.outbound.data.totalBytesOctalCodes")] =
|
||||||
|
(double)OctreePacketData::getTotalBytesOfOctalCodes();
|
||||||
|
statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] =
|
||||||
|
(double)OctreePacketData::getTotalBytesOfBitMasks();
|
||||||
|
statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] = (double)OctreePacketData::getTotalBytesOfColor();
|
||||||
|
|
||||||
|
statsObject2[baseName + QString(".2.outbound.timing.1.avgLoopTime")] = getAverageLoopTime();
|
||||||
|
statsObject2[baseName + QString(".2.outbound.timing.2.avgInsideTime")] = getAverageInsideTime();
|
||||||
|
statsObject2[baseName + QString(".2.outbound.timing.3.avgTreeLockTime")] = getAverageTreeWaitTime();
|
||||||
|
statsObject2[baseName + QString(".2.outbound.timing.4.avgEncodeTime")] = getAverageEncodeTime();
|
||||||
|
statsObject2[baseName + QString(".2.outbound.timing.5.avgCompressAndWriteTime")] = getAverageCompressAndWriteTime();
|
||||||
|
statsObject2[baseName + QString(".2.outbound.timing.5.avgSendTime")] = getAveragePacketSendingTime();
|
||||||
|
statsObject2[baseName + QString(".2.outbound.timing.5.nodeWaitTime")] = getAverageNodeWaitTime();
|
||||||
|
|
||||||
|
NodeList::getInstance()->sendStatsToDomainServer(statsObject2);
|
||||||
|
|
||||||
|
static QJsonObject statsObject3;
|
||||||
|
|
||||||
|
statsObject3[baseName + QString(".3.inbound.data.1.totalPackets")] =
|
||||||
|
(double)_octreeInboundPacketProcessor->getTotalPacketsProcessed();
|
||||||
|
statsObject3[baseName + QString(".3.inbound.data.2.totalElements")] =
|
||||||
|
(double)_octreeInboundPacketProcessor->getTotalElementsProcessed();
|
||||||
|
statsObject3[baseName + QString(".3.inbound.timing.1.avgTransitTimePerPacket")] =
|
||||||
|
(double)_octreeInboundPacketProcessor->getAverageTransitTimePerPacket();
|
||||||
|
statsObject3[baseName + QString(".3.inbound.timing.2.avgProcessTimePerPacket")] =
|
||||||
|
(double)_octreeInboundPacketProcessor->getAverageProcessTimePerPacket();
|
||||||
|
statsObject3[baseName + QString(".3.inbound.timing.3.avgLockWaitTimePerPacket")] =
|
||||||
|
(double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket();
|
||||||
|
statsObject3[baseName + QString(".3.inbound.timing.4.avgProcessTimePerElement")] =
|
||||||
|
(double)_octreeInboundPacketProcessor->getAverageProcessTimePerElement();
|
||||||
|
statsObject3[baseName + QString(".3.inbound.timing.5.avgLockWaitTimePerElement")] =
|
||||||
|
(double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerElement();
|
||||||
|
|
||||||
|
NodeList::getInstance()->sendStatsToDomainServer(statsObject3);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<OctreeSendThread*, quint64> OctreeServer::_threadsDidProcess;
|
||||||
|
QMap<OctreeSendThread*, quint64> OctreeServer::_threadsDidPacketDistributor;
|
||||||
|
QMap<OctreeSendThread*, quint64> OctreeServer::_threadsDidHandlePacketSend;
|
||||||
|
QMap<OctreeSendThread*, quint64> OctreeServer::_threadsDidCallWriteDatagram;
|
||||||
|
|
||||||
|
|
||||||
|
void OctreeServer::didProcess(OctreeSendThread* thread) {
|
||||||
|
_threadsDidProcess[thread] = usecTimestampNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeServer::didPacketDistributor(OctreeSendThread* thread) {
|
||||||
|
_threadsDidPacketDistributor[thread] = usecTimestampNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeServer::didHandlePacketSend(OctreeSendThread* thread) {
|
||||||
|
_threadsDidHandlePacketSend[thread] = usecTimestampNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeServer::didCallWriteDatagram(OctreeSendThread* thread) {
|
||||||
|
_threadsDidCallWriteDatagram[thread] = usecTimestampNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OctreeServer::stopTrackingThread(OctreeSendThread* thread) {
|
||||||
|
_threadsDidProcess.remove(thread);
|
||||||
|
_threadsDidPacketDistributor.remove(thread);
|
||||||
|
_threadsDidHandlePacketSend.remove(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
int howManyThreadsDidSomething(QMap<OctreeSendThread*, quint64>& something, quint64 since) {
|
||||||
|
if (since == 0) {
|
||||||
|
return something.size();
|
||||||
|
}
|
||||||
|
int count = 0;
|
||||||
|
QMap<OctreeSendThread*, quint64>::const_iterator i = something.constBegin();
|
||||||
|
while (i != something.constEnd()) {
|
||||||
|
if (i.value() > since) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int OctreeServer::howManyThreadsDidProcess(quint64 since) {
|
||||||
|
return howManyThreadsDidSomething(_threadsDidProcess, since);
|
||||||
|
}
|
||||||
|
|
||||||
|
int OctreeServer::howManyThreadsDidPacketDistributor(quint64 since) {
|
||||||
|
return howManyThreadsDidSomething(_threadsDidPacketDistributor, since);
|
||||||
|
}
|
||||||
|
|
||||||
|
int OctreeServer::howManyThreadsDidHandlePacketSend(quint64 since) {
|
||||||
|
return howManyThreadsDidSomething(_threadsDidHandlePacketSend, since);
|
||||||
|
}
|
||||||
|
|
||||||
|
int OctreeServer::howManyThreadsDidCallWriteDatagram(quint64 since) {
|
||||||
|
return howManyThreadsDidSomething(_threadsDidCallWriteDatagram, since);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,21 @@ public:
|
||||||
static void trackPacketSendingTime(float time);
|
static void trackPacketSendingTime(float time);
|
||||||
static float getAveragePacketSendingTime() { return _averagePacketSendingTime.getAverage(); }
|
static float getAveragePacketSendingTime() { return _averagePacketSendingTime.getAverage(); }
|
||||||
|
|
||||||
|
static void trackProcessWaitTime(float time);
|
||||||
|
static float getAverageProcessWaitTime() { return _averageProcessWaitTime.getAverage(); }
|
||||||
|
|
||||||
|
// these methods allow us to track which threads got to various states
|
||||||
|
static void didProcess(OctreeSendThread* thread);
|
||||||
|
static void didPacketDistributor(OctreeSendThread* thread);
|
||||||
|
static void didHandlePacketSend(OctreeSendThread* thread);
|
||||||
|
static void didCallWriteDatagram(OctreeSendThread* thread);
|
||||||
|
static void stopTrackingThread(OctreeSendThread* thread);
|
||||||
|
|
||||||
|
static int howManyThreadsDidProcess(quint64 since = 0);
|
||||||
|
static int howManyThreadsDidPacketDistributor(quint64 since = 0);
|
||||||
|
static int howManyThreadsDidHandlePacketSend(quint64 since = 0);
|
||||||
|
static int howManyThreadsDidCallWriteDatagram(quint64 since = 0);
|
||||||
|
|
||||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||||
|
|
||||||
virtual void aboutToFinish();
|
virtual void aboutToFinish();
|
||||||
|
@ -107,17 +122,24 @@ public slots:
|
||||||
void readPendingDatagrams();
|
void readPendingDatagrams();
|
||||||
void nodeAdded(SharedNodePointer node);
|
void nodeAdded(SharedNodePointer node);
|
||||||
void nodeKilled(SharedNodePointer node);
|
void nodeKilled(SharedNodePointer node);
|
||||||
|
void sendStatsPacket();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void parsePayload();
|
void parsePayload();
|
||||||
void initHTTPManager(int port);
|
void initHTTPManager(int port);
|
||||||
void resetSendingStats();
|
void resetSendingStats();
|
||||||
|
QString getUptime();
|
||||||
|
QString getFileLoadTime();
|
||||||
|
QString getConfiguration();
|
||||||
|
QString getStatusLink();
|
||||||
|
|
||||||
int _argc;
|
int _argc;
|
||||||
const char** _argv;
|
const char** _argv;
|
||||||
char** _parsedArgV;
|
char** _parsedArgV;
|
||||||
|
|
||||||
HTTPManager* _httpManager;
|
HTTPManager* _httpManager;
|
||||||
|
int _statusPort;
|
||||||
|
QString _statusHost;
|
||||||
|
|
||||||
char _persistFilename[MAX_FILENAME_LENGTH];
|
char _persistFilename[MAX_FILENAME_LENGTH];
|
||||||
int _packetsPerClientPerInterval;
|
int _packetsPerClientPerInterval;
|
||||||
|
@ -151,6 +173,7 @@ protected:
|
||||||
static int _noEncode;
|
static int _noEncode;
|
||||||
|
|
||||||
static SimpleMovingAverage _averageInsideTime;
|
static SimpleMovingAverage _averageInsideTime;
|
||||||
|
|
||||||
static SimpleMovingAverage _averageTreeWaitTime;
|
static SimpleMovingAverage _averageTreeWaitTime;
|
||||||
static SimpleMovingAverage _averageTreeShortWaitTime;
|
static SimpleMovingAverage _averageTreeShortWaitTime;
|
||||||
static SimpleMovingAverage _averageTreeLongWaitTime;
|
static SimpleMovingAverage _averageTreeLongWaitTime;
|
||||||
|
@ -174,6 +197,20 @@ protected:
|
||||||
static SimpleMovingAverage _averagePacketSendingTime;
|
static SimpleMovingAverage _averagePacketSendingTime;
|
||||||
static int _noSend;
|
static int _noSend;
|
||||||
|
|
||||||
|
static SimpleMovingAverage _averageProcessWaitTime;
|
||||||
|
static SimpleMovingAverage _averageProcessShortWaitTime;
|
||||||
|
static SimpleMovingAverage _averageProcessLongWaitTime;
|
||||||
|
static SimpleMovingAverage _averageProcessExtraLongWaitTime;
|
||||||
|
static int _extraLongProcessWait;
|
||||||
|
static int _longProcessWait;
|
||||||
|
static int _shortProcessWait;
|
||||||
|
static int _noProcessWait;
|
||||||
|
|
||||||
|
static QMap<OctreeSendThread*, quint64> _threadsDidProcess;
|
||||||
|
static QMap<OctreeSendThread*, quint64> _threadsDidPacketDistributor;
|
||||||
|
static QMap<OctreeSendThread*, quint64> _threadsDidHandlePacketSend;
|
||||||
|
static QMap<OctreeSendThread*, quint64> _threadsDidCallWriteDatagram;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __octree_server__OctreeServer__
|
#endif // __octree_server__OctreeServer__
|
||||||
|
|
|
@ -499,13 +499,19 @@ bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penet
|
||||||
//return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
|
//return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Avatar::findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions) {
|
void Avatar::updateShapePositions() {
|
||||||
_skeletonModel.updateShapePositions();
|
_skeletonModel.updateShapePositions();
|
||||||
bool collided = _skeletonModel.findCollisions(shapes, collisions);
|
|
||||||
|
|
||||||
Model& headModel = getHead()->getFaceModel();
|
Model& headModel = getHead()->getFaceModel();
|
||||||
headModel.updateShapePositions();
|
headModel.updateShapePositions();
|
||||||
collided = headModel.findCollisions(shapes, collisions);
|
}
|
||||||
|
|
||||||
|
bool Avatar::findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions) {
|
||||||
|
// TODO: Andrew to fix: also collide against _skeleton
|
||||||
|
//bool collided = _skeletonModel.findCollisions(shapes, collisions);
|
||||||
|
|
||||||
|
Model& headModel = getHead()->getFaceModel();
|
||||||
|
//collided = headModel.findCollisions(shapes, collisions) || collided;
|
||||||
|
bool collided = headModel.findCollisions(shapes, collisions);
|
||||||
return collided;
|
return collided;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -752,17 +758,30 @@ bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::applyCollision(CollisionInfo& collision) {
|
void Avatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) {
|
||||||
if (!collision._data || collision._type != MODEL_COLLISION) {
|
// compute lean angles
|
||||||
return;
|
glm::vec3 leverAxis = contactPoint - getPosition();
|
||||||
}
|
float leverLength = glm::length(leverAxis);
|
||||||
// TODO: make skeleton also respond to collisions
|
if (leverLength > EPSILON) {
|
||||||
Model* model = static_cast<Model*>(collision._data);
|
glm::quat bodyRotation = getOrientation();
|
||||||
if (model == &(getHead()->getFaceModel())) {
|
glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f);
|
||||||
getHead()->applyCollision(collision);
|
glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f);
|
||||||
|
|
||||||
|
leverAxis = leverAxis / leverLength;
|
||||||
|
glm::vec3 effectivePenetration = penetration - glm::dot(penetration, leverAxis) * leverAxis;
|
||||||
|
// we use the small-angle approximation for sine below to compute the length of
|
||||||
|
// the opposite side of a narrow right triangle
|
||||||
|
float sideways = - glm::dot(effectivePenetration, xAxis) / leverLength;
|
||||||
|
float forward = glm::dot(effectivePenetration, zAxis) / leverLength;
|
||||||
|
getHead()->addLean(sideways, forward);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Avatar::getBoundingRadius() const {
|
||||||
|
// TODO: also use head model when computing the avatar's bounding radius
|
||||||
|
return _skeletonModel.getBoundingRadius();
|
||||||
|
}
|
||||||
|
|
||||||
float Avatar::getPelvisFloatingHeight() const {
|
float Avatar::getPelvisFloatingHeight() const {
|
||||||
return -_skeletonModel.getBindExtents().minimum.y;
|
return -_skeletonModel.getBindExtents().minimum.y;
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,10 +145,11 @@ public:
|
||||||
/// \return true if we expect the avatar would move as a result of the collision
|
/// \return true if we expect the avatar would move as a result of the collision
|
||||||
bool collisionWouldMoveAvatar(CollisionInfo& collision) const;
|
bool collisionWouldMoveAvatar(CollisionInfo& collision) const;
|
||||||
|
|
||||||
/// \param collision a data structure for storing info about collisions against Models
|
void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration);
|
||||||
void applyCollision(CollisionInfo& collision);
|
|
||||||
|
|
||||||
float getBoundingRadius() const { return 0.5f * getSkeletonHeight(); }
|
/// \return bounding radius of avatar
|
||||||
|
virtual float getBoundingRadius() const;
|
||||||
|
void updateShapePositions();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateCollisionFlags();
|
void updateCollisionFlags();
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
|
|
||||||
#include <GeometryUtil.h>
|
#include <GeometryUtil.h>
|
||||||
|
#include <StreamUtils.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
|
@ -109,8 +110,6 @@ void Hand::collideAgainstAvatarOld(Avatar* avatar, bool isMyHand) {
|
||||||
for (int j = 0; j < handCollisions.size(); ++j) {
|
for (int j = 0; j < handCollisions.size(); ++j) {
|
||||||
CollisionInfo* collision = handCollisions.getCollision(j);
|
CollisionInfo* collision = handCollisions.getCollision(j);
|
||||||
if (isMyHand) {
|
if (isMyHand) {
|
||||||
// we resolve the hand from collision when it belongs to MyAvatar AND the other Avatar is
|
|
||||||
// not expected to respond to the collision (hand hit unmovable part of their Avatar)
|
|
||||||
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,55 +127,37 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2 = NUM_HANDS
|
|
||||||
int palmIndices[2];
|
|
||||||
getLeftRightPalmIndices(*palmIndices, *(palmIndices + 1));
|
|
||||||
|
|
||||||
const SkeletonModel& skeletonModel = _owningAvatar->getSkeletonModel();
|
const SkeletonModel& skeletonModel = _owningAvatar->getSkeletonModel();
|
||||||
int jointIndices[2];
|
int jointIndices[2];
|
||||||
jointIndices[0] = skeletonModel.getLeftHandJointIndex();
|
jointIndices[0] = skeletonModel.getLeftHandJointIndex();
|
||||||
jointIndices[1] = skeletonModel.getRightHandJointIndex();
|
jointIndices[1] = skeletonModel.getRightHandJointIndex();
|
||||||
|
|
||||||
palmIndices[1] = -1; // adebug temporarily disable right hand
|
for (size_t i = 0; i < 2; i++) {
|
||||||
jointIndices[1] = -1; // adebug temporarily disable right hand
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 1; i++) {
|
|
||||||
int palmIndex = palmIndices[i];
|
|
||||||
int jointIndex = jointIndices[i];
|
int jointIndex = jointIndices[i];
|
||||||
if (palmIndex == -1 || jointIndex == -1) {
|
if (jointIndex < 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
PalmData& palm = _palms[palmIndex];
|
|
||||||
if (!palm.isActive()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
|
|
||||||
playSlaps(palm, avatar);
|
|
||||||
}
|
|
||||||
|
|
||||||
handCollisions.clear();
|
handCollisions.clear();
|
||||||
QVector<const Shape*> shapes;
|
QVector<const Shape*> shapes;
|
||||||
skeletonModel.getHandShapes(jointIndex, shapes);
|
skeletonModel.getHandShapes(jointIndex, shapes);
|
||||||
bool collided = isMyHand ? avatar->findCollisions(shapes, handCollisions) : avatar->findCollisions(shapes, handCollisions);
|
|
||||||
if (collided) {
|
if (avatar->findCollisions(shapes, handCollisions)) {
|
||||||
//if (avatar->findCollisions(shapes, handCollisions)) {
|
glm::vec3 totalPenetration(0.f);
|
||||||
glm::vec3 averagePenetration;
|
|
||||||
glm::vec3 averageContactPoint;
|
glm::vec3 averageContactPoint;
|
||||||
for (int j = 0; j < handCollisions.size(); ++j) {
|
for (int j = 0; j < handCollisions.size(); ++j) {
|
||||||
CollisionInfo* collision = handCollisions.getCollision(j);
|
CollisionInfo* collision = handCollisions.getCollision(j);
|
||||||
averagePenetration += collision->_penetration;
|
totalPenetration += collision->_penetration;
|
||||||
averageContactPoint += collision->_contactPoint;
|
averageContactPoint += collision->_contactPoint;
|
||||||
}
|
}
|
||||||
averagePenetration /= float(handCollisions.size());
|
|
||||||
if (isMyHand) {
|
if (isMyHand) {
|
||||||
// our hand against other avatar
|
// our hand against other avatar
|
||||||
// for now we resolve it to test shapes/collisions
|
// TODO: resolve this penetration when we don't think the other avatar will yield
|
||||||
// TODO: only partially resolve this penetration
|
//palm.addToPenetration(averagePenetration);
|
||||||
palm.addToPosition(-averagePenetration);
|
|
||||||
} else {
|
} else {
|
||||||
// someone else's hand against MyAvatar
|
// someone else's hand against MyAvatar
|
||||||
// TODO: submit collision info to MyAvatar which should lean accordingly
|
|
||||||
averageContactPoint /= float(handCollisions.size());
|
averageContactPoint /= float(handCollisions.size());
|
||||||
|
avatar->applyCollision(averageContactPoint, totalPenetration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,7 +173,7 @@ void Hand::collideAgainstOurself() {
|
||||||
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
|
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
|
||||||
|
|
||||||
const Model& skeletonModel = _owningAvatar->getSkeletonModel();
|
const Model& skeletonModel = _owningAvatar->getSkeletonModel();
|
||||||
for (size_t i = 0; i < getNumPalms(); i++) {
|
for (int i = 0; i < int(getNumPalms()); i++) {
|
||||||
PalmData& palm = getPalms()[i];
|
PalmData& palm = getPalms()[i];
|
||||||
if (!palm.isActive()) {
|
if (!palm.isActive()) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -210,11 +191,18 @@ void Hand::collideAgainstOurself() {
|
||||||
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
||||||
}
|
}
|
||||||
// resolve penetration
|
// resolve penetration
|
||||||
palm.addToPosition(-totalPenetration);
|
palm.addToPenetration(totalPenetration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Hand::resolvePenetrations() {
|
||||||
|
for (size_t i = 0; i < getNumPalms(); ++i) {
|
||||||
|
PalmData& palm = getPalms()[i];
|
||||||
|
palm.resolvePenetrations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Hand::calculateGeometry() {
|
void Hand::calculateGeometry() {
|
||||||
// generate finger tip balls....
|
// generate finger tip balls....
|
||||||
_leapFingerTipBalls.clear();
|
_leapFingerTipBalls.clear();
|
||||||
|
@ -385,19 +373,3 @@ void Hand::renderLeapHands(bool isMine) {
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Hand::setLeapHands(const std::vector<glm::vec3>& handPositions,
|
|
||||||
const std::vector<glm::vec3>& handNormals) {
|
|
||||||
for (size_t i = 0; i < getNumPalms(); ++i) {
|
|
||||||
PalmData& palm = getPalms()[i];
|
|
||||||
if (i < handPositions.size()) {
|
|
||||||
palm.setActive(true);
|
|
||||||
palm.setRawPosition(handPositions[i]);
|
|
||||||
palm.setRawNormal(handNormals[i]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
palm.setActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,8 @@ public:
|
||||||
void collideAgainstAvatar(Avatar* avatar, bool isMyHand);
|
void collideAgainstAvatar(Avatar* avatar, bool isMyHand);
|
||||||
void collideAgainstOurself();
|
void collideAgainstOurself();
|
||||||
|
|
||||||
|
void resolvePenetrations();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// disallow copies of the Hand, copy of owning Avatar is disallowed too
|
// disallow copies of the Hand, copy of owning Avatar is disallowed too
|
||||||
Hand(const Hand&);
|
Hand(const Hand&);
|
||||||
|
@ -71,10 +73,6 @@ private:
|
||||||
std::vector<HandBall> _leapFingerTipBalls;
|
std::vector<HandBall> _leapFingerTipBalls;
|
||||||
std::vector<HandBall> _leapFingerRootBalls;
|
std::vector<HandBall> _leapFingerRootBalls;
|
||||||
|
|
||||||
// private methods
|
|
||||||
void setLeapHands(const std::vector<glm::vec3>& handPositions,
|
|
||||||
const std::vector<glm::vec3>& handNormals);
|
|
||||||
|
|
||||||
void renderLeapHands(bool isMine);
|
void renderLeapHands(bool isMine);
|
||||||
void renderLeapFingerTrails();
|
void renderLeapFingerTrails();
|
||||||
|
|
||||||
|
@ -84,3 +82,4 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -209,34 +209,6 @@ float Head::getTweakedRoll() const {
|
||||||
return glm::clamp(_roll + _rollTweak, MIN_HEAD_ROLL, MAX_HEAD_ROLL);
|
return glm::clamp(_roll + _rollTweak, MIN_HEAD_ROLL, MAX_HEAD_ROLL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Head::applyCollision(CollisionInfo& collision) {
|
|
||||||
// HACK: the collision proxies for the FaceModel are bad. As a temporary workaround
|
|
||||||
// we collide against a hard coded collision proxy.
|
|
||||||
// TODO: get a better collision proxy here.
|
|
||||||
const float HEAD_RADIUS = 0.15f;
|
|
||||||
const glm::vec3 HEAD_CENTER = _position;
|
|
||||||
|
|
||||||
// collide the contactPoint against the collision proxy to obtain a new penetration
|
|
||||||
// NOTE: that penetration is in opposite direction (points the way out for the point, not the sphere)
|
|
||||||
glm::vec3 penetration;
|
|
||||||
if (findPointSpherePenetration(collision._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) {
|
|
||||||
// compute lean angles
|
|
||||||
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
|
|
||||||
glm::quat bodyRotation = owningAvatar->getOrientation();
|
|
||||||
glm::vec3 neckPosition;
|
|
||||||
if (owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) {
|
|
||||||
glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f);
|
|
||||||
glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f);
|
|
||||||
float neckLength = glm::length(_position - neckPosition);
|
|
||||||
if (neckLength > 0.f) {
|
|
||||||
float forward = glm::dot(collision._penetration, zAxis) / neckLength;
|
|
||||||
float sideways = - glm::dot(collision._penetration, xAxis) / neckLength;
|
|
||||||
addLean(sideways, forward);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
|
void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
|
||||||
|
|
||||||
Application::getInstance()->getGlowEffect()->begin();
|
Application::getInstance()->getGlowEffect()->begin();
|
||||||
|
|
|
@ -82,8 +82,6 @@ public:
|
||||||
virtual float getTweakedPitch() const;
|
virtual float getTweakedPitch() const;
|
||||||
virtual float getTweakedYaw() const;
|
virtual float getTweakedYaw() const;
|
||||||
virtual float getTweakedRoll() const;
|
virtual float getTweakedRoll() const;
|
||||||
|
|
||||||
void applyCollision(CollisionInfo& collisionInfo);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// disallow copies of the Head, copy of owning Avatar is disallowed too
|
// disallow copies of the Head, copy of owning Avatar is disallowed too
|
||||||
|
|
|
@ -177,26 +177,6 @@ void MyAvatar::simulate(float deltaTime) {
|
||||||
_velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime);
|
_velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_collisionFlags != 0) {
|
|
||||||
Camera* myCamera = Application::getInstance()->getCamera();
|
|
||||||
|
|
||||||
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
|
|
||||||
if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) {
|
|
||||||
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cosf(0.5f * RADIANS_PER_DEGREE * myCamera->getFieldOfView()));
|
|
||||||
radius *= COLLISION_RADIUS_SCALAR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) {
|
|
||||||
updateCollisionWithEnvironment(deltaTime, radius);
|
|
||||||
}
|
|
||||||
if (_collisionFlags & COLLISION_GROUP_VOXELS) {
|
|
||||||
updateCollisionWithVoxels(deltaTime, radius);
|
|
||||||
}
|
|
||||||
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
|
|
||||||
updateCollisionWithAvatars(deltaTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add thrust to velocity
|
// add thrust to velocity
|
||||||
_velocity += _thrust * deltaTime;
|
_velocity += _thrust * deltaTime;
|
||||||
|
|
||||||
|
@ -320,7 +300,28 @@ void MyAvatar::simulate(float deltaTime) {
|
||||||
|
|
||||||
// Zero thrust out now that we've added it to velocity in this frame
|
// Zero thrust out now that we've added it to velocity in this frame
|
||||||
_thrust = glm::vec3(0, 0, 0);
|
_thrust = glm::vec3(0, 0, 0);
|
||||||
|
|
||||||
|
// now that we're done stepping the avatar forward in time, compute new collisions
|
||||||
|
if (_collisionFlags != 0) {
|
||||||
|
Camera* myCamera = Application::getInstance()->getCamera();
|
||||||
|
|
||||||
|
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
|
||||||
|
if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) {
|
||||||
|
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f));
|
||||||
|
radius *= COLLISION_RADIUS_SCALAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) {
|
||||||
|
updateCollisionWithEnvironment(deltaTime, radius);
|
||||||
|
}
|
||||||
|
if (_collisionFlags & COLLISION_GROUP_VOXELS) {
|
||||||
|
updateCollisionWithVoxels(deltaTime, radius);
|
||||||
|
}
|
||||||
|
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
|
||||||
|
updateCollisionWithAvatars(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// consider updating our billboard
|
// consider updating our billboard
|
||||||
maybeUpdateBillboard();
|
maybeUpdateBillboard();
|
||||||
}
|
}
|
||||||
|
@ -361,7 +362,7 @@ void MyAvatar::updateFromGyros(float deltaTime) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// restore rotation, lean to neutral positions
|
// restore rotation, lean to neutral positions
|
||||||
const float RESTORE_PERIOD = 1.f; // seconds
|
const float RESTORE_PERIOD = 0.25f; // seconds
|
||||||
float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f);
|
float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f);
|
||||||
head->setPitchTweak(glm::mix(head->getPitchTweak(), 0.0f, restorePercentage));
|
head->setPitchTweak(glm::mix(head->getPitchTweak(), 0.0f, restorePercentage));
|
||||||
head->setYawTweak(glm::mix(head->getYawTweak(), 0.0f, restorePercentage));
|
head->setYawTweak(glm::mix(head->getYawTweak(), 0.0f, restorePercentage));
|
||||||
|
@ -881,35 +882,31 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
||||||
// no need to compute a bunch of stuff if we have one or fewer avatars
|
// no need to compute a bunch of stuff if we have one or fewer avatars
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
updateShapePositions();
|
||||||
float myBoundingRadius = getBoundingRadius();
|
float myBoundingRadius = getBoundingRadius();
|
||||||
|
|
||||||
|
/* TODO: Andrew to fix Avatar-Avatar body collisions
|
||||||
// HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis
|
// HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis
|
||||||
// TODO: make the collision work without assuming avatar orientation
|
// TODO: make the collision work without assuming avatar orientation
|
||||||
|
Extents myStaticExtents = _skeletonModel.getStaticExtents();
|
||||||
|
glm::vec3 staticScale = myStaticExtents.maximum - myStaticExtents.minimum;
|
||||||
|
float myCapsuleRadius = 0.25f * (staticScale.x + staticScale.z);
|
||||||
|
float myCapsuleHeight = staticScale.y;
|
||||||
|
*/
|
||||||
|
|
||||||
// TODO: these local variables are not used in the live code, only in the
|
|
||||||
// commented-outTODO code below.
|
|
||||||
//Extents myStaticExtents = _skeletonModel.getStaticExtents();
|
|
||||||
//glm::vec3 staticScale = myStaticExtents.maximum - myStaticExtents.minimum;
|
|
||||||
//float myCapsuleRadius = 0.25f * (staticScale.x + staticScale.z);
|
|
||||||
//float myCapsuleHeight = staticScale.y;
|
|
||||||
|
|
||||||
CollisionInfo collisionInfo;
|
|
||||||
foreach (const AvatarSharedPointer& avatarPointer, avatars) {
|
foreach (const AvatarSharedPointer& avatarPointer, avatars) {
|
||||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||||
if (static_cast<Avatar*>(this) == avatar) {
|
if (static_cast<Avatar*>(this) == avatar) {
|
||||||
// don't collide with ourselves
|
// don't collide with ourselves
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
avatar->updateShapePositions();
|
||||||
float distance = glm::length(_position - avatar->getPosition());
|
float distance = glm::length(_position - avatar->getPosition());
|
||||||
if (_distanceToNearestAvatar > distance) {
|
if (_distanceToNearestAvatar > distance) {
|
||||||
_distanceToNearestAvatar = distance;
|
_distanceToNearestAvatar = distance;
|
||||||
}
|
}
|
||||||
float theirBoundingRadius = avatar->getBoundingRadius();
|
float theirBoundingRadius = avatar->getBoundingRadius();
|
||||||
if (distance < myBoundingRadius + theirBoundingRadius) {
|
if (distance < myBoundingRadius + theirBoundingRadius) {
|
||||||
_skeletonModel.updateShapePositions();
|
|
||||||
Model& headModel = getHead()->getFaceModel();
|
|
||||||
headModel.updateShapePositions();
|
|
||||||
|
|
||||||
/* TODO: Andrew to fix Avatar-Avatar body collisions
|
/* TODO: Andrew to fix Avatar-Avatar body collisions
|
||||||
Extents theirStaticExtents = _skeletonModel.getStaticExtents();
|
Extents theirStaticExtents = _skeletonModel.getStaticExtents();
|
||||||
glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum;
|
glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum;
|
||||||
|
@ -925,12 +922,16 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// collide our hands against them
|
// collide our hands against them
|
||||||
getHand()->collideAgainstAvatar(avatar, true);
|
// TODO: make this work when we can figure out when the other avatar won't yeild
|
||||||
|
// (for example, we're colling against their chest or leg)
|
||||||
|
//getHand()->collideAgainstAvatar(avatar, true);
|
||||||
|
|
||||||
// collide their hands against us
|
// collide their hands against us
|
||||||
avatar->getHand()->collideAgainstAvatar(this, false);
|
avatar->getHand()->collideAgainstAvatar(this, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO: uncomment this when we handle collisions that won't affect other avatar
|
||||||
|
//getHand()->resolvePenetrations();
|
||||||
}
|
}
|
||||||
|
|
||||||
class SortedAvatar {
|
class SortedAvatar {
|
||||||
|
|
|
@ -63,13 +63,30 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::getHandShapes(int jointIndex, QVector<const Shape*>& shapes) const {
|
void SkeletonModel::getHandShapes(int jointIndex, QVector<const Shape*>& shapes) const {
|
||||||
if (jointIndex == -1) {
|
if (jointIndex < 0 || jointIndex >= int(_shapes.size())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (jointIndex == getLeftHandJointIndex()
|
if (jointIndex == getLeftHandJointIndex()
|
||||||
|| jointIndex == getRightHandJointIndex()) {
|
|| jointIndex == getRightHandJointIndex()) {
|
||||||
// TODO: also add fingers and other hand-parts
|
// get all shapes that have this hand as an ancestor in the skeleton heirarchy
|
||||||
shapes.push_back(_shapes[jointIndex]);
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
|
for (int i = 0; i < _jointStates.size(); i++) {
|
||||||
|
const FBXJoint& joint = geometry.joints[i];
|
||||||
|
if (i == jointIndex) {
|
||||||
|
// this shape is the hand
|
||||||
|
shapes.push_back(_shapes[i]);
|
||||||
|
} else {
|
||||||
|
int parentIndex = joint.parentIndex;
|
||||||
|
while (parentIndex != -1) {
|
||||||
|
if (parentIndex == jointIndex) {
|
||||||
|
// this shape is a child of the hand
|
||||||
|
shapes.push_back(_shapes[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parentIndex = geometry.joints[parentIndex].parentIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ public:
|
||||||
/// \param jointIndex index of hand joint
|
/// \param jointIndex index of hand joint
|
||||||
/// \param shapes[out] list in which is stored pointers to hand shapes
|
/// \param shapes[out] list in which is stored pointers to hand shapes
|
||||||
void getHandShapes(int jointIndex, QVector<const Shape*>& shapes) const;
|
void getHandShapes(int jointIndex, QVector<const Shape*>& shapes) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void applyHandPosition(int jointIndex, const glm::vec3& position);
|
void applyHandPosition(int jointIndex, const glm::vec3& position);
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
|
|
||||||
#include <glm/gtx/transform.hpp>
|
#include <glm/gtx/transform.hpp>
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
#include <GeometryUtil.h>
|
#include <GeometryUtil.h>
|
||||||
|
|
||||||
|
@ -32,7 +33,8 @@ Model::Model(QObject* parent) :
|
||||||
_scale(1.0f, 1.0f, 1.0f),
|
_scale(1.0f, 1.0f, 1.0f),
|
||||||
_shapesAreDirty(true),
|
_shapesAreDirty(true),
|
||||||
_lodDistance(0.0f),
|
_lodDistance(0.0f),
|
||||||
_pupilDilation(0.0f) {
|
_pupilDilation(0.0f),
|
||||||
|
_boundingRadius(0.f) {
|
||||||
// we may have been created in the network thread, but we live in the main thread
|
// we may have been created in the network thread, but we live in the main thread
|
||||||
moveToThread(Application::getInstance()->thread());
|
moveToThread(Application::getInstance()->thread());
|
||||||
}
|
}
|
||||||
|
@ -164,6 +166,7 @@ void Model::createCollisionShapes() {
|
||||||
|
|
||||||
void Model::updateShapePositions() {
|
void Model::updateShapePositions() {
|
||||||
if (_shapesAreDirty && _shapes.size() == _jointStates.size()) {
|
if (_shapesAreDirty && _shapes.size() == _jointStates.size()) {
|
||||||
|
_boundingRadius = 0.f;
|
||||||
float uniformScale = extractUniformScale(_scale);
|
float uniformScale = extractUniformScale(_scale);
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
for (int i = 0; i < _jointStates.size(); i++) {
|
for (int i = 0; i < _jointStates.size(); i++) {
|
||||||
|
@ -173,7 +176,12 @@ void Model::updateShapePositions() {
|
||||||
glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation;
|
glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation;
|
||||||
_shapes[i]->setPosition(worldPosition);
|
_shapes[i]->setPosition(worldPosition);
|
||||||
_shapes[i]->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation);
|
_shapes[i]->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation);
|
||||||
|
float distance2 = glm::distance2(worldPosition, _translation);
|
||||||
|
if (distance2 > _boundingRadius) {
|
||||||
|
_boundingRadius = distance2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_boundingRadius = sqrtf(_boundingRadius);
|
||||||
_shapesAreDirty = false;
|
_shapesAreDirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,18 +329,10 @@ bool Model::getRightHandRotation(glm::quat& rotation) const {
|
||||||
return getJointRotation(getRightHandJointIndex(), rotation);
|
return getJointRotation(getRightHandJointIndex(), rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::setLeftHandPosition(const glm::vec3& position) {
|
|
||||||
return setJointPosition(getLeftHandJointIndex(), position);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Model::restoreLeftHandPosition(float percent) {
|
bool Model::restoreLeftHandPosition(float percent) {
|
||||||
return restoreJointPosition(getLeftHandJointIndex(), percent);
|
return restoreJointPosition(getLeftHandJointIndex(), percent);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::setLeftHandRotation(const glm::quat& rotation) {
|
|
||||||
return setJointRotation(getLeftHandJointIndex(), rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Model::getLeftShoulderPosition(glm::vec3& position) const {
|
bool Model::getLeftShoulderPosition(glm::vec3& position) const {
|
||||||
return getJointPosition(getLastFreeJointIndex(getLeftHandJointIndex()), position);
|
return getJointPosition(getLastFreeJointIndex(getLeftHandJointIndex()), position);
|
||||||
}
|
}
|
||||||
|
@ -341,18 +341,10 @@ float Model::getLeftArmLength() const {
|
||||||
return getLimbLength(getLeftHandJointIndex());
|
return getLimbLength(getLeftHandJointIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::setRightHandPosition(const glm::vec3& position) {
|
|
||||||
return setJointPosition(getRightHandJointIndex(), position);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Model::restoreRightHandPosition(float percent) {
|
bool Model::restoreRightHandPosition(float percent) {
|
||||||
return restoreJointPosition(getRightHandJointIndex(), percent);
|
return restoreJointPosition(getRightHandJointIndex(), percent);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::setRightHandRotation(const glm::quat& rotation) {
|
|
||||||
return setJointRotation(getRightHandJointIndex(), rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Model::getRightShoulderPosition(glm::vec3& position) const {
|
bool Model::getRightShoulderPosition(glm::vec3& position) const {
|
||||||
return getJointPosition(getLastFreeJointIndex(getRightHandJointIndex()), position);
|
return getJointPosition(getLastFreeJointIndex(getRightHandJointIndex()), position);
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,19 +135,11 @@ public:
|
||||||
/// \return true whether or not the rotation was found
|
/// \return true whether or not the rotation was found
|
||||||
bool getRightHandRotation(glm::quat& rotation) const;
|
bool getRightHandRotation(glm::quat& rotation) const;
|
||||||
|
|
||||||
/// Sets the position of the left hand using inverse kinematics.
|
|
||||||
/// \return whether or not the left hand joint was found
|
|
||||||
bool setLeftHandPosition(const glm::vec3& position);
|
|
||||||
|
|
||||||
/// Restores some percentage of the default position of the left hand.
|
/// Restores some percentage of the default position of the left hand.
|
||||||
/// \param percent the percentage of the default position to restore
|
/// \param percent the percentage of the default position to restore
|
||||||
/// \return whether or not the left hand joint was found
|
/// \return whether or not the left hand joint was found
|
||||||
bool restoreLeftHandPosition(float percent = 1.0f);
|
bool restoreLeftHandPosition(float percent = 1.0f);
|
||||||
|
|
||||||
/// Sets the rotation of the left hand.
|
|
||||||
/// \return whether or not the left hand joint was found
|
|
||||||
bool setLeftHandRotation(const glm::quat& rotation);
|
|
||||||
|
|
||||||
/// Gets the position of the left shoulder.
|
/// Gets the position of the left shoulder.
|
||||||
/// \return whether or not the left shoulder joint was found
|
/// \return whether or not the left shoulder joint was found
|
||||||
bool getLeftShoulderPosition(glm::vec3& position) const;
|
bool getLeftShoulderPosition(glm::vec3& position) const;
|
||||||
|
@ -155,19 +147,11 @@ public:
|
||||||
/// Returns the extended length from the left hand to its last free ancestor.
|
/// Returns the extended length from the left hand to its last free ancestor.
|
||||||
float getLeftArmLength() const;
|
float getLeftArmLength() const;
|
||||||
|
|
||||||
/// Sets the position of the right hand using inverse kinematics.
|
|
||||||
/// \return whether or not the right hand joint was found
|
|
||||||
bool setRightHandPosition(const glm::vec3& position);
|
|
||||||
|
|
||||||
/// Restores some percentage of the default position of the right hand.
|
/// Restores some percentage of the default position of the right hand.
|
||||||
/// \param percent the percentage of the default position to restore
|
/// \param percent the percentage of the default position to restore
|
||||||
/// \return whether or not the right hand joint was found
|
/// \return whether or not the right hand joint was found
|
||||||
bool restoreRightHandPosition(float percent = 1.0f);
|
bool restoreRightHandPosition(float percent = 1.0f);
|
||||||
|
|
||||||
/// Sets the rotation of the right hand.
|
|
||||||
/// \return whether or not the right hand joint was found
|
|
||||||
bool setRightHandRotation(const glm::quat& rotation);
|
|
||||||
|
|
||||||
/// Gets the position of the right shoulder.
|
/// Gets the position of the right shoulder.
|
||||||
/// \return whether or not the right shoulder joint was found
|
/// \return whether or not the right shoulder joint was found
|
||||||
bool getRightShoulderPosition(glm::vec3& position) const;
|
bool getRightShoulderPosition(glm::vec3& position) const;
|
||||||
|
@ -195,6 +179,8 @@ public:
|
||||||
/// Use the collision to affect the model
|
/// Use the collision to affect the model
|
||||||
void applyCollision(CollisionInfo& collision);
|
void applyCollision(CollisionInfo& collision);
|
||||||
|
|
||||||
|
float getBoundingRadius() const { return _boundingRadius; }
|
||||||
|
|
||||||
/// Sets blended vertices computed in a separate thread.
|
/// Sets blended vertices computed in a separate thread.
|
||||||
void setBlendedVertices(const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
|
void setBlendedVertices(const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
|
||||||
|
|
||||||
|
@ -254,7 +240,7 @@ protected:
|
||||||
/// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's
|
/// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's
|
||||||
/// first free ancestor.
|
/// first free ancestor.
|
||||||
float getLimbLength(int jointIndex) const;
|
float getLimbLength(int jointIndex) const;
|
||||||
|
|
||||||
void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true);
|
void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -280,6 +266,8 @@ private:
|
||||||
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
|
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
|
||||||
|
|
||||||
QVector<Model*> _attachments;
|
QVector<Model*> _attachments;
|
||||||
|
|
||||||
|
float _boundingRadius;
|
||||||
|
|
||||||
static ProgramObject _program;
|
static ProgramObject _program;
|
||||||
static ProgramObject _normalMapProgram;
|
static ProgramObject _normalMapProgram;
|
||||||
|
|
|
@ -61,11 +61,12 @@ void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
PalmData::PalmData(HandData* owningHandData) :
|
PalmData::PalmData(HandData* owningHandData) :
|
||||||
_rawRotation(0, 0, 0, 1),
|
_rawRotation(0.f, 0.f, 0.f, 1.f),
|
||||||
_rawPosition(0, 0, 0),
|
_rawPosition(0.f),
|
||||||
_rawNormal(0, 1, 0),
|
_rawNormal(0.f, 1.f, 0.f),
|
||||||
_rawVelocity(0, 0, 0),
|
_rawVelocity(0.f),
|
||||||
_rotationalVelocity(0, 0, 0),
|
_rotationalVelocity(0.f),
|
||||||
|
_totalPenetration(0.f),
|
||||||
_controllerButtons(0),
|
_controllerButtons(0),
|
||||||
_isActive(false),
|
_isActive(false),
|
||||||
_leapID(LEAPID_INVALID),
|
_leapID(LEAPID_INVALID),
|
||||||
|
|
|
@ -154,6 +154,9 @@ public:
|
||||||
void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }
|
void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }
|
||||||
const glm::vec3& getRawVelocity() const { return _rawVelocity; }
|
const glm::vec3& getRawVelocity() const { return _rawVelocity; }
|
||||||
void addToPosition(const glm::vec3& delta);
|
void addToPosition(const glm::vec3& delta);
|
||||||
|
|
||||||
|
void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; }
|
||||||
|
void resolvePenetrations() { addToPosition(-_totalPenetration); _totalPenetration = glm::vec3(0.f); }
|
||||||
|
|
||||||
void setTipPosition(const glm::vec3& position) { _tipPosition = position; }
|
void setTipPosition(const glm::vec3& position) { _tipPosition = position; }
|
||||||
const glm::vec3 getTipPosition() const { return _owningHandData->leapPositionToWorldPosition(_tipPosition); }
|
const glm::vec3 getTipPosition() const { return _owningHandData->leapPositionToWorldPosition(_tipPosition); }
|
||||||
|
@ -203,6 +206,7 @@ private:
|
||||||
|
|
||||||
glm::vec3 _tipPosition;
|
glm::vec3 _tipPosition;
|
||||||
glm::vec3 _tipVelocity;
|
glm::vec3 _tipVelocity;
|
||||||
|
glm::vec3 _totalPenetration; // accumulator for per-frame penetrations
|
||||||
unsigned int _controllerButtons;
|
unsigned int _controllerButtons;
|
||||||
unsigned int _lastControllerButtons;
|
unsigned int _lastControllerButtons;
|
||||||
float _trigger;
|
float _trigger;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QHttpMultiPart>
|
#include <QHttpMultiPart>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "AccountManager.h"
|
#include "AccountManager.h"
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@ static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB
|
||||||
FstReader::FstReader() :
|
FstReader::FstReader() :
|
||||||
_lodCount(-1),
|
_lodCount(-1),
|
||||||
_texturesCount(-1),
|
_texturesCount(-1),
|
||||||
|
_totalSize(0),
|
||||||
_isHead(false),
|
_isHead(false),
|
||||||
_readyToSend(false),
|
_readyToSend(false),
|
||||||
_dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType))
|
_dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType))
|
||||||
|
@ -49,11 +51,18 @@ bool FstReader::zip() {
|
||||||
"Select your .fst file ...",
|
"Select your .fst file ...",
|
||||||
QStandardPaths::writableLocation(QStandardPaths::DownloadLocation),
|
QStandardPaths::writableLocation(QStandardPaths::DownloadLocation),
|
||||||
"*.fst");
|
"*.fst");
|
||||||
|
if (filename == "") {
|
||||||
|
// If the user canceled we return.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// First we check the FST file
|
// First we check the FST file
|
||||||
QFile fst(filename);
|
QFile fst(filename);
|
||||||
if (!fst.open(QFile::ReadOnly | QFile::Text)) {
|
if (!fst.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
qDebug() << "[ERROR] Could not open FST file : " << fst.fileName();
|
QMessageBox::warning(NULL,
|
||||||
|
QString("ModelUploader::zip()"),
|
||||||
|
QString("Could not open FST file."),
|
||||||
|
QMessageBox::Ok);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +87,10 @@ bool FstReader::zip() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_totalSize > MAX_SIZE) {
|
if (_totalSize > MAX_SIZE) {
|
||||||
qDebug() << "[ERROR] Model too big, over " << MAX_SIZE << " Bytes.";
|
QMessageBox::warning(NULL,
|
||||||
|
QString("ModelUploader::zip()"),
|
||||||
|
QString("Model too big, over %1 Bytes.").arg(MAX_SIZE),
|
||||||
|
QMessageBox::Ok);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +106,10 @@ bool FstReader::zip() {
|
||||||
} else if (line[1] == FILENAME_FIELD) {
|
} else if (line[1] == FILENAME_FIELD) {
|
||||||
QFileInfo fbx(QFileInfo(fst).path() + "/" + line[1]);
|
QFileInfo fbx(QFileInfo(fst).path() + "/" + line[1]);
|
||||||
if (!fbx.exists() || !fbx.isFile()) { // Check existence
|
if (!fbx.exists() || !fbx.isFile()) { // Check existence
|
||||||
qDebug() << "[ERROR] FBX file " << fbx.absoluteFilePath() << " doesn't exist.";
|
QMessageBox::warning(NULL,
|
||||||
|
QString("ModelUploader::zip()"),
|
||||||
|
QString("FBX file %1 could not be found.").arg(fbx.fileName()),
|
||||||
|
QMessageBox::Ok);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Compress and copy
|
// Compress and copy
|
||||||
|
@ -108,7 +123,10 @@ bool FstReader::zip() {
|
||||||
} else if (line[1] == TEXDIR_FIELD) { // Check existence
|
} else if (line[1] == TEXDIR_FIELD) { // Check existence
|
||||||
QFileInfo texdir(QFileInfo(fst).path() + "/" + line[1]);
|
QFileInfo texdir(QFileInfo(fst).path() + "/" + line[1]);
|
||||||
if (!texdir.exists() || !texdir.isDir()) {
|
if (!texdir.exists() || !texdir.isDir()) {
|
||||||
qDebug() << "[ERROR] Texture directory " << texdir.absolutePath() << " doesn't exist.";
|
QMessageBox::warning(NULL,
|
||||||
|
QString("ModelUploader::zip()"),
|
||||||
|
QString("Texture directory could not be found."),
|
||||||
|
QMessageBox::Ok);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!addTextures(texdir)) { // Recursive compress and copy
|
if (!addTextures(texdir)) { // Recursive compress and copy
|
||||||
|
@ -117,7 +135,10 @@ bool FstReader::zip() {
|
||||||
} else if (line[1] == LOD_FIELD) {
|
} else if (line[1] == LOD_FIELD) {
|
||||||
QFileInfo lod(QFileInfo(fst).path() + "/" + line[1]);
|
QFileInfo lod(QFileInfo(fst).path() + "/" + line[1]);
|
||||||
if (!lod.exists() || !lod.isFile()) { // Check existence
|
if (!lod.exists() || !lod.isFile()) { // Check existence
|
||||||
qDebug() << "[ERROR] FBX file " << lod.absoluteFilePath() << " doesn't exist.";
|
QMessageBox::warning(NULL,
|
||||||
|
QString("ModelUploader::zip()"),
|
||||||
|
QString("FBX file %1 could not be found.").arg(lod.fileName()),
|
||||||
|
QMessageBox::Ok);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Compress and copy
|
// Compress and copy
|
||||||
|
@ -180,8 +201,6 @@ bool FstReader::addTextures(const QFileInfo& texdir) {
|
||||||
if (!addTextures(info)) {
|
if (!addTextures(info)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
qDebug() << "[DEBUG] Invalid file type : " << info.filePath();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +216,10 @@ bool FstReader::compressFile(const QString &inFileName, const QString &outFileNa
|
||||||
if (!outFile.open(QIODevice::WriteOnly)) {
|
if (!outFile.open(QIODevice::WriteOnly)) {
|
||||||
QDir(_zipDir.path()).mkpath(QFileInfo(outFileName).path());
|
QDir(_zipDir.path()).mkpath(QFileInfo(outFileName).path());
|
||||||
if (!outFile.open(QIODevice::WriteOnly)) {
|
if (!outFile.open(QIODevice::WriteOnly)) {
|
||||||
qDebug() << "[ERROR] Could not compress " << inFileName << ".";
|
QMessageBox::warning(NULL,
|
||||||
|
QString("ModelUploader::compressFile()"),
|
||||||
|
QString("Could not compress %1").arg(inFileName),
|
||||||
|
QMessageBox::Ok);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,7 +233,10 @@ bool FstReader::compressFile(const QString &inFileName, const QString &outFileNa
|
||||||
bool FstReader::addPart(const QString &path, const QString& name) {
|
bool FstReader::addPart(const QString &path, const QString& name) {
|
||||||
QFile* file = new QFile(path);
|
QFile* file = new QFile(path);
|
||||||
if (!file->open(QIODevice::ReadOnly)) {
|
if (!file->open(QIODevice::ReadOnly)) {
|
||||||
qDebug() << "[ERROR] Couldn't open " << file->fileName();
|
QMessageBox::warning(NULL,
|
||||||
|
QString("ModelUploader::addPart()"),
|
||||||
|
QString("Could not open %1").arg(path),
|
||||||
|
QMessageBox::Ok);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,8 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col
|
||||||
if (absAxialDistance < totalRadius + capsuleB->getHalfHeight()) {
|
if (absAxialDistance < totalRadius + capsuleB->getHalfHeight()) {
|
||||||
glm::vec3 radialAxis = BA + axialDistance * capsuleAxis; // points from A to axis of B
|
glm::vec3 radialAxis = BA + axialDistance * capsuleAxis; // points from A to axis of B
|
||||||
float radialDistance2 = glm::length2(radialAxis);
|
float radialDistance2 = glm::length2(radialAxis);
|
||||||
if (radialDistance2 > totalRadius * totalRadius) {
|
float totalRadius2 = totalRadius * totalRadius;
|
||||||
|
if (radialDistance2 > totalRadius2) {
|
||||||
// sphere is too far from capsule axis
|
// sphere is too far from capsule axis
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -95,6 +96,9 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col
|
||||||
float sign = (axialDistance > 0.f) ? 1.f : -1.f;
|
float sign = (axialDistance > 0.f) ? 1.f : -1.f;
|
||||||
radialAxis = BA + (sign * capsuleB->getHalfHeight()) * capsuleAxis;
|
radialAxis = BA + (sign * capsuleB->getHalfHeight()) * capsuleAxis;
|
||||||
radialDistance2 = glm::length2(radialAxis);
|
radialDistance2 = glm::length2(radialAxis);
|
||||||
|
if (radialDistance2 > totalRadius2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (radialDistance2 > EPSILON * EPSILON) {
|
if (radialDistance2 > EPSILON * EPSILON) {
|
||||||
CollisionInfo* collision = collisions.getNewCollision();
|
CollisionInfo* collision = collisions.getNewCollision();
|
||||||
|
@ -147,7 +151,8 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col
|
||||||
if (absAxialDistance < totalRadius + capsuleA->getHalfHeight()) {
|
if (absAxialDistance < totalRadius + capsuleA->getHalfHeight()) {
|
||||||
glm::vec3 radialAxis = AB + axialDistance * capsuleAxis; // from sphereB to axis of capsuleA
|
glm::vec3 radialAxis = AB + axialDistance * capsuleAxis; // from sphereB to axis of capsuleA
|
||||||
float radialDistance2 = glm::length2(radialAxis);
|
float radialDistance2 = glm::length2(radialAxis);
|
||||||
if (radialDistance2 > totalRadius * totalRadius) {
|
float totalRadius2 = totalRadius * totalRadius;
|
||||||
|
if (radialDistance2 > totalRadius2) {
|
||||||
// sphere is too far from capsule axis
|
// sphere is too far from capsule axis
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -162,6 +167,9 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col
|
||||||
closestApproach = capsuleA->getPosition() + (sign * capsuleA->getHalfHeight()) * capsuleAxis;
|
closestApproach = capsuleA->getPosition() + (sign * capsuleA->getHalfHeight()) * capsuleAxis;
|
||||||
radialAxis = closestApproach - sphereB->getPosition();
|
radialAxis = closestApproach - sphereB->getPosition();
|
||||||
radialDistance2 = glm::length2(radialAxis);
|
radialDistance2 = glm::length2(radialAxis);
|
||||||
|
if (radialDistance2 > totalRadius2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (radialDistance2 > EPSILON * EPSILON) {
|
if (radialDistance2 > EPSILON * EPSILON) {
|
||||||
CollisionInfo* collision = collisions.getNewCollision();
|
CollisionInfo* collision = collisions.getNewCollision();
|
||||||
|
|
75
libraries/shared/src/StreamUtils.cpp
Normal file
75
libraries/shared/src/StreamUtils.cpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
//
|
||||||
|
// StreamUtils.cpp
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows on 2014.02.21
|
||||||
|
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
|
#include "StreamUtils.h"
|
||||||
|
|
||||||
|
const char* hex_digits = "0123456789abcdef";
|
||||||
|
|
||||||
|
void StreamUtil::dump(std::ostream& s, const QByteArray& buffer) {
|
||||||
|
int row_size = 32;
|
||||||
|
int i = 0;
|
||||||
|
while (i < buffer.size()) {
|
||||||
|
for(int j = 0; i < buffer.size() && j < row_size; ++j) {
|
||||||
|
char byte = buffer[i];
|
||||||
|
s << hex_digits[(byte >> 4) & 0x0f] << hex_digits[byte & 0x0f] << " ";
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
s << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& s, const glm::vec3& v) {
|
||||||
|
s << "<" << v.x << " " << v.y << " " << v.z << ">";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& s, const glm::quat& q) {
|
||||||
|
s << "<" << q.x << " " << q.y << " " << q.z << " " << q.w << ">";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& s, const glm::mat4& m) {
|
||||||
|
s << "[";
|
||||||
|
for (int j = 0; j < 4; ++j) {
|
||||||
|
s << " " << m[0][j] << " " << m[1][j] << " " << m[2][j] << " " << m[3][j] << ";";
|
||||||
|
}
|
||||||
|
s << " ]";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// less common utils can be enabled with DEBUG
|
||||||
|
#ifdef DEBUG
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& s, const CollisionInfo& c) {
|
||||||
|
s << "{penetration=" << c._penetration
|
||||||
|
<< ", contactPoint=" << c._contactPoint
|
||||||
|
<< ", addedVelocity=" << c._addedVelocity
|
||||||
|
<< "}";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& s, const SphereShape& sphere) {
|
||||||
|
s << "{type='sphere', center=" << sphere.getPosition()
|
||||||
|
<< ", radius=" << sphere.getRadius()
|
||||||
|
<< "}";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) {
|
||||||
|
s << "{type='capsule', center=" << capsule.getPosition()
|
||||||
|
<< ", radius=" << capsule.getRadius()
|
||||||
|
<< ", length=" << (2.f * capsule.getHalfHeight())
|
||||||
|
<< ", begin=" << capsule.getStartPoint()
|
||||||
|
<< ", end=" << capsule.getEndPoint()
|
||||||
|
<< "}";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DEBUG
|
||||||
|
|
39
libraries/shared/src/StreamUtils.h
Normal file
39
libraries/shared/src/StreamUtils.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// StreamUtils.h
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows on 2014.02.21
|
||||||
|
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __tests__StreamUtils__
|
||||||
|
#define __tests__StreamUtils__
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace StreamUtil {
|
||||||
|
// dump the buffer, 32 bytes per row, each byte in hex, separated by whitespace
|
||||||
|
void dump(std::ostream& s, const QByteArray& buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& s, const glm::vec3& v);
|
||||||
|
std::ostream& operator<<(std::ostream& s, const glm::quat& q);
|
||||||
|
std::ostream& operator<<(std::ostream& s, const glm::mat4& m);
|
||||||
|
|
||||||
|
// less common utils can be enabled with DEBUG
|
||||||
|
#ifdef DEBUG
|
||||||
|
#include "CollisionInfo.h"
|
||||||
|
#include "SphereShape.h"
|
||||||
|
#include "CapsuleShape.h"
|
||||||
|
std::ostream& operator<<(std::ostream& s, const CollisionInfo& c);
|
||||||
|
std::ostream& operator<<(std::ostream& s, const SphereShape& shape);
|
||||||
|
std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule);
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
#endif // __tests__StreamUtils__
|
|
@ -13,12 +13,17 @@
|
||||||
|
|
||||||
#include <CollisionInfo.h>
|
#include <CollisionInfo.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
#include <StreamUtils.h>
|
||||||
|
|
||||||
#include "CollisionInfoTests.h"
|
#include "CollisionInfoTests.h"
|
||||||
#include "PhysicsTestUtil.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
static glm::vec3 xAxis(1.f, 0.f, 0.f);
|
||||||
|
static glm::vec3 xZxis(0.f, 1.f, 0.f);
|
||||||
|
static glm::vec3 xYxis(0.f, 0.f, 1.f);
|
||||||
|
|
||||||
void CollisionInfoTests::rotateThenTranslate() {
|
void CollisionInfoTests::rotateThenTranslate() {
|
||||||
CollisionInfo collision;
|
CollisionInfo collision;
|
||||||
collision._penetration = xAxis;
|
collision._penetration = xAxis;
|
||||||
|
|
|
@ -16,11 +16,14 @@
|
||||||
#include <ShapeCollider.h>
|
#include <ShapeCollider.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <SphereShape.h>
|
#include <SphereShape.h>
|
||||||
|
#include <StreamUtils.h>
|
||||||
|
|
||||||
#include "PhysicsTestUtil.h"
|
|
||||||
#include "ShapeColliderTests.h"
|
#include "ShapeColliderTests.h"
|
||||||
|
|
||||||
const glm::vec3 origin(0.f);
|
const glm::vec3 origin(0.f);
|
||||||
|
static const glm::vec3 xAxis(1.f, 0.f, 0.f);
|
||||||
|
static const glm::vec3 yAxis(0.f, 1.f, 0.f);
|
||||||
|
static const glm::vec3 zAxis(0.f, 0.f, 1.f);
|
||||||
|
|
||||||
void ShapeColliderTests::sphereMissesSphere() {
|
void ShapeColliderTests::sphereMissesSphere() {
|
||||||
// non-overlapping spheres of unequal size
|
// non-overlapping spheres of unequal size
|
||||||
|
|
Loading…
Reference in a new issue