merge upstream/master into avatar-interaction

This commit is contained in:
Andrew Meadows 2014-04-07 10:05:25 -07:00
commit 84d7e39d72
15 changed files with 708 additions and 614 deletions

View file

@ -63,7 +63,7 @@
#include <UUID.h>
#include <OctreeSceneStats.h>
#include <LocalVoxelsList.h>
#include <FstReader.h>
#include <ModelUploader.h>
#include "Application.h"
#include "InterfaceVersion.h"
@ -81,6 +81,7 @@
#include "ui/InfoView.h"
#include "ui/Snapshot.h"
#include "ui/TextRenderer.h"
#include "ui/Stats.h"
using namespace std;
@ -110,8 +111,6 @@ const float MIRROR_FIELD_OF_VIEW = 30.0f;
const QString CHECK_VERSION_URL = "http://highfidelity.io/latestVersion.xml";
const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
const int STATS_PELS_PER_LINE = 20;
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (message.size() > 0) {
QString dateString = QDateTime::currentDateTime().toTimeSpec(Qt::LocalTime).toString(Qt::ISODate);
@ -135,7 +134,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
QApplication(argc, argv),
_window(new QMainWindow(desktop())),
_glWidget(new GLCanvas()),
_statsExpanded(false),
_nodeThread(new QThread(this)),
_datagramProcessor(),
_frameCount(0),
@ -149,7 +147,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_lastQueriedViewFrustum(),
_lastQueriedTime(usecTimestampNow()),
_audioScope(256, 200, true),
_trailingAudioLoudness(0.f),
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
_mouseX(0),
_mouseY(0),
@ -166,8 +163,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_voxelHideShowThread(&_voxels),
_packetsPerSecond(0),
_bytesPerSecond(0),
_recentMaxPackets(0),
_resetRecentMaxPacketsSoon(true),
_previousScriptLocation(),
_logger(new FileLogger(this))
{
@ -515,7 +510,7 @@ void Application::paintGL() {
_myCamera.setDistance(0.0f);
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
_myCamera.setTargetRotation(_myAvatar->getHead()->getOrientation());
_myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay
@ -617,6 +612,14 @@ void Application::resizeGL(int width, int height) {
updateProjectionMatrix();
glLoadIdentity();
// update Stats width
int horizontalOffset = 0;
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
// mirror is enabled, let's set horizontal offset to give stats some margin
horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
}
Stats::getInstance()->resetWidth(width, horizontalOffset);
}
void Application::updateProjectionMatrix() {
@ -1059,7 +1062,11 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
_mousePressed = false;
checkBandwidthMeterClick();
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
checkStatsClick();
int horizontalOffset = 0;
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
horizontalOffset = MIRROR_VIEW_WIDTH;
}
Stats::getInstance()->checkClick(_mouseX, _mouseY, _mouseDragStartedX, _mouseDragStartedY, horizontalOffset);
}
}
}
@ -2598,11 +2605,17 @@ void Application::displayOverlay() {
glPointSize(1.0f);
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
int horizontalOffset = 0;
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
// mirror is enabled, let's set horizontal offset to give stats some margin
horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
}
int voxelPacketsToProcess = _voxelProcessor.packetsToProcessCount();
// Onscreen text about position, servers, etc
displayStats();
Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, _fps, _packetsPerSecond, _bytesPerSecond, voxelPacketsToProcess);
// Bandwidth meter
if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) {
displayStatsBackground(0x33333399, _glWidget->width() - 296, _glWidget->height() - 68, 296, 68);
Stats::drawBackground(0x33333399, _glWidget->width() - 296, _glWidget->height() - 68, 296, 68);
_bandwidthMeter.render(_glWidget->width(), _glWidget->height());
}
}
@ -2624,415 +2637,6 @@ void Application::displayOverlay() {
glPopMatrix();
}
// translucent background box that makes stats more readable
void Application::displayStatsBackground(unsigned int rgba, int x, int y, int width, int height) {
glBegin(GL_QUADS);
glColor4f(((rgba >> 24) & 0xff) / 255.0f,
((rgba >> 16) & 0xff) / 255.0f,
((rgba >> 8) & 0xff) / 255.0f,
(rgba & 0xff) / 255.0f);
glVertex3f(x, y, 0);
glVertex3f(x + width, y, 0);
glVertex3f(x + width, y + height, 0);
glVertex3f(x , y + height, 0);
glEnd();
glColor4f(1, 1, 1, 1);
}
// display expanded or contracted stats
void Application::displayStats() {
unsigned int backgroundColor = 0x33333399;
int verticalOffset = 0, horizontalOffset = 0, lines = 0;
bool mirrorEnabled = Menu::getInstance()->isOptionChecked(MenuOption::Mirror);
QLocale locale(QLocale::English);
std::stringstream voxelStats;
glPointSize(1.0f);
// we need to take one avatar out so we don't include ourselves
int totalAvatars = _avatarManager.size() - 1;
int totalServers = NodeList::getInstance()->size();
if (mirrorEnabled) {
horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
}
lines = _statsExpanded ? 5 : 3;
displayStatsBackground(backgroundColor, horizontalOffset, 0, 165, lines * STATS_PELS_PER_LINE + 10);
horizontalOffset += 5;
char serverNodes[30];
sprintf(serverNodes, "Servers: %d", totalServers);
char avatarNodes[30];
sprintf(avatarNodes, "Avatars: %d", totalAvatars);
char framesPerSecond[30];
sprintf(framesPerSecond, "Framerate: %3.0f FPS", _fps);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, serverNodes, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarNodes, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, framesPerSecond, WHITE_TEXT);
if (_statsExpanded) {
char packetsPerSecond[30];
sprintf(packetsPerSecond, "Pkts/sec: %d", _packetsPerSecond);
char averageMegabitsPerSecond[30];
sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)_bytesPerSecond * 8.f / 1000000.f);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, packetsPerSecond, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, averageMegabitsPerSecond, WHITE_TEXT);
}
verticalOffset = 0;
horizontalOffset += 161;
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0;
NodeList* nodeList = NodeList::getInstance();
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : 0;
pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : 0;
// Now handle voxel servers, since there could be more than one, we average their ping times
unsigned long totalPingVoxel = 0;
int voxelServerCount = 0;
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
if (node->getType() == NodeType::VoxelServer) {
totalPingVoxel += node->getPingMs();
voxelServerCount++;
if (pingVoxelMax < node->getPingMs()) {
pingVoxelMax = node->getPingMs();
}
}
}
if (voxelServerCount) {
pingVoxel = totalPingVoxel/voxelServerCount;
}
lines = _statsExpanded ? 4 : 3;
displayStatsBackground(backgroundColor, horizontalOffset, 0, 175, lines * STATS_PELS_PER_LINE + 10);
horizontalOffset += 5;
char audioJitter[30];
sprintf(audioJitter,
"Buffer msecs %.1f",
(float) (_audio.getNetworkBufferLengthSamplesPerChannel() + (float) _audio.getJitterBufferSamples()) /
(float)_audio.getNetworkSampleRate() * 1000.f);
drawText(30, _glWidget->height() - 22, 0.10f, 0.f, 2.f, audioJitter, WHITE_TEXT);
char audioPing[30];
sprintf(audioPing, "Audio ping: %d", pingAudio);
char avatarPing[30];
sprintf(avatarPing, "Avatar ping: %d", pingAvatar);
char voxelAvgPing[30];
sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, audioPing, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPing, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelAvgPing, WHITE_TEXT);
if (_statsExpanded) {
char voxelMaxPing[30];
sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelMaxPing, WHITE_TEXT);
}
verticalOffset = 0;
horizontalOffset += 171;
}
glm::vec3 avatarPos = _myAvatar->getPosition();
lines = _statsExpanded ? 5 : 3;
displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - (mirrorEnabled ? 301 : 411) - horizontalOffset, lines * STATS_PELS_PER_LINE + 10);
horizontalOffset += 5;
char avatarPosition[200];
if (mirrorEnabled) {
// shorthand formatting
sprintf(avatarPosition, "Pos: %.0f,%.0f,%.0f", avatarPos.x, avatarPos.y, avatarPos.z);
} else {
// longhand way
sprintf(avatarPosition, "Position: %.1f, %.1f, %.1f", avatarPos.x, avatarPos.y, avatarPos.z);
}
char avatarVelocity[30];
sprintf(avatarVelocity, "Velocity: %.1f", glm::length(_myAvatar->getVelocity()));
char avatarBodyYaw[30];
sprintf(avatarBodyYaw, "Yaw: %.1f", _myAvatar->getBodyYaw());
char avatarMixerStats[200];
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPosition, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarVelocity, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarBodyYaw, WHITE_TEXT);
if (_statsExpanded) {
SharedNodePointer avatarMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AvatarMixer);
if (avatarMixer) {
sprintf(avatarMixerStats, "Avatar Mixer: %.f kbps, %.f pps",
roundf(avatarMixer->getAverageKilobitsPerSecond()),
roundf(avatarMixer->getAveragePacketsPerSecond()));
} else {
sprintf(avatarMixerStats, "No Avatar Mixer");
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarMixerStats, WHITE_TEXT);
stringstream downloadStats;
downloadStats << "Downloads: ";
foreach (Resource* resource, ResourceCache::getLoadingRequests()) {
const float MAXIMUM_PERCENTAGE = 100.0f;
downloadStats << roundf(resource->getProgress() * MAXIMUM_PERCENTAGE) << "% ";
}
downloadStats << "(" << ResourceCache::getPendingRequestCount() << " pending)";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, downloadStats.str().c_str(), WHITE_TEXT);
}
verticalOffset = 0;
horizontalOffset = _glWidget->width() - (mirrorEnabled ? 300 : 410);
lines = _statsExpanded ? 12 : 3;
displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - horizontalOffset, lines * STATS_PELS_PER_LINE + 10);
horizontalOffset += 5;
if (_statsExpanded) {
// Local Voxel Memory Usage
voxelStats.str("");
voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
voxelStats.str("");
voxelStats <<
"Geometry RAM: " << _voxels.getVoxelMemoryUsageRAM() / 1000000.f << "MB / " <<
"VBO: " << _voxels.getVoxelMemoryUsageVBO() / 1000000.f << "MB";
if (_voxels.hasVoxelMemoryUsageGPU()) {
voxelStats << " / GPU: " << _voxels.getVoxelMemoryUsageGPU() / 1000000.f << "MB";
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
// Voxel Rendering
voxelStats.str("");
voxelStats.precision(4);
voxelStats << "Voxel Rendering Slots Max: " << _voxels.getMaxVoxels() / 1000.f << "K";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
}
voxelStats.str("");
voxelStats.precision(4);
voxelStats << "Drawn: " << _voxels.getVoxelsWritten() / 1000.f << "K " <<
"Abandoned: " << _voxels.getAbandonedVoxels() / 1000.f << "K ";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
// iterate all the current voxel stats, and list their sending modes, and total voxel counts
std::stringstream sendingMode("");
sendingMode << "Octree Sending Mode: [";
int serverCount = 0;
int movingServerCount = 0;
unsigned long totalNodes = 0;
unsigned long totalInternal = 0;
unsigned long totalLeaves = 0;
for(NodeToOctreeSceneStatsIterator i = _octreeServerSceneStats.begin(); i != _octreeServerSceneStats.end(); i++) {
//const QUuid& uuid = i->first;
OctreeSceneStats& stats = i->second;
serverCount++;
if (_statsExpanded) {
if (serverCount > 1) {
sendingMode << ",";
}
if (stats.isMoving()) {
sendingMode << "M";
movingServerCount++;
} else {
sendingMode << "S";
}
}
// calculate server node totals
totalNodes += stats.getTotalElements();
if (_statsExpanded) {
totalInternal += stats.getTotalInternal();
totalLeaves += stats.getTotalLeaves();
}
}
if (_statsExpanded) {
if (serverCount == 0) {
sendingMode << "---";
}
sendingMode << "] " << serverCount << " servers";
if (movingServerCount > 0) {
sendingMode << " <SCENE NOT STABLE>";
} else {
sendingMode << " <SCENE STABLE>";
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)sendingMode.str().c_str(), WHITE_TEXT);
}
// Incoming packets
int voxelPacketsToProcess = _voxelProcessor.packetsToProcessCount();
if (_statsExpanded) {
voxelStats.str("");
QString packetsString = locale.toString((int)voxelPacketsToProcess);
QString maxString = locale.toString((int)_recentMaxPackets);
voxelStats << "Voxel Packets to Process: " << qPrintable(packetsString)
<< " [Recent Max: " << qPrintable(maxString) << "]";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
}
if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) {
_recentMaxPackets = 0;
_resetRecentMaxPacketsSoon = false;
}
if (voxelPacketsToProcess == 0) {
_resetRecentMaxPacketsSoon = true;
} else {
if (voxelPacketsToProcess > _recentMaxPackets) {
_recentMaxPackets = voxelPacketsToProcess;
}
}
verticalOffset += (_statsExpanded ? STATS_PELS_PER_LINE : 0);
QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' ');
// Server Voxels
voxelStats.str("");
voxelStats << "Server voxels: " << qPrintable(serversTotalString);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
if (_statsExpanded) {
QString serversInternalString = locale.toString((uint)totalInternal);
QString serversLeavesString = locale.toString((uint)totalLeaves);
voxelStats.str("");
voxelStats <<
"Internal: " << qPrintable(serversInternalString) << " " <<
"Leaves: " << qPrintable(serversLeavesString) << "";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
}
unsigned long localTotal = VoxelTreeElement::getNodeCount();
QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' ');
// Local Voxels
voxelStats.str("");
voxelStats << "Local voxels: " << qPrintable(localTotalString);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
if (_statsExpanded) {
unsigned long localInternal = VoxelTreeElement::getInternalNodeCount();
unsigned long localLeaves = VoxelTreeElement::getLeafNodeCount();
QString localInternalString = locale.toString((uint)localInternal);
QString localLeavesString = locale.toString((uint)localLeaves);
voxelStats.str("");
voxelStats <<
"Internal: " << qPrintable(localInternalString) << " " <<
"Leaves: " << qPrintable(localLeavesString) << "";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT);
}
// LOD Details
if (_statsExpanded) {
voxelStats.str("");
QString displayLODDetails = Menu::getInstance()->getLODFeedbackText();
voxelStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed());
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
}
}
// called on mouse click release
// check for clicks over stats in order to expand or contract them
void Application::checkStatsClick() {
if (0 != glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY)))) {
// not worried about dragging on stats
return;
}
int statsHeight = 0, statsWidth = 0, statsX = 0, statsY = 0, lines = 0;
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
statsX += MIRROR_VIEW_WIDTH;
}
// top-left stats click
lines = _statsExpanded ? 5 : 3;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
statsWidth = 165;
if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) {
toggleStatsExpanded();
return;
}
// ping stats click
lines = _statsExpanded ? 4 : 3;
statsX += statsWidth;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
statsWidth = 175;
if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) {
toggleStatsExpanded();
return;
}
// top-center stats panel click
lines = _statsExpanded ? 4 : 3;
statsX += statsWidth;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
statsWidth = _glWidget->width() - 411 - statsX;
if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) {
toggleStatsExpanded();
return;
}
// top-right stats click
lines = _statsExpanded ? 11 : 3;
statsX = _glWidget->width() - 410;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
statsWidth = _glWidget->width() - statsX;
if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) {
toggleStatsExpanded();
return;
}
}
void Application::toggleStatsExpanded() {
_statsExpanded = !_statsExpanded;
}
glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) {
float horizontalScale = _glWidget->width() / 2.0f;
float verticalScale = _glWidget->height() / 2.0f;
@ -3640,9 +3244,9 @@ void Application::toggleRunningScriptsWidget()
}
void Application::uploadFST(bool isHead) {
FstReader reader(isHead);
if (reader.zip()) {
reader.send();
ModelUploader* uploader = new ModelUploader(isHead);
if (uploader->zip()) {
uploader->send();
}
}

View file

@ -333,10 +333,6 @@ private:
void updateShadowMap();
void displayOverlay();
void displayStatsBackground(unsigned int rgba, int x, int y, int width, int height);
void displayStats();
void checkStatsClick();
void toggleStatsExpanded();
void renderRearViewMirror(const QRect& region, bool billboard = false);
void renderViewFrustum(ViewFrustum& viewFrustum);
@ -357,7 +353,6 @@ private:
QMainWindow* _window;
GLCanvas* _glWidget; // our GLCanvas has a couple extra features
bool _statsExpanded;
BandwidthMeter _bandwidthMeter;
QThread* _nodeThread;
@ -470,9 +465,6 @@ private:
int _packetsPerSecond;
int _bytesPerSecond;
int _recentMaxPackets; // recent max incoming voxel packets to process
bool _resetRecentMaxPacketsSoon;
StDev _idleLoopStdev;
float _idleLoopMeasuredJitter;

View file

@ -1260,20 +1260,22 @@ void Menu::autoAdjustLOD(float currentFPS) {
quint64 now = usecTimestampNow();
const float ADJUST_AVATAR_LOD_DOWN_FPS = 30.0f;
const quint64 ADJUST_AVATAR_LOD_DOWN_DELAY = 1000 * 1000;
if (_fastFPSAverage.getAverage() < ADJUST_LOD_DOWN_FPS) {
if (_fastFPSAverage.getAverage() < ADJUST_AVATAR_LOD_DOWN_FPS) {
if (now - _lastAvatarDetailDrop > ADJUST_AVATAR_LOD_DOWN_DELAY) {
// attempt to lower the detail in proportion to the fps difference
float targetFps = (ADJUST_LOD_DOWN_FPS + ADJUST_LOD_UP_FPS) * 0.5f;
float targetFps = (ADJUST_AVATAR_LOD_DOWN_FPS + ADJUST_LOD_UP_FPS) * 0.5f;
float averageFps = _fastFPSAverage.getAverage();
const float MAXIMUM_MULTIPLIER_SCALE = 2.0f;
_avatarLODDistanceMultiplier *= (averageFps < EPSILON) ? MAXIMUM_MULTIPLIER_SCALE :
qMin(MAXIMUM_MULTIPLIER_SCALE, targetFps / averageFps);
const float MAXIMUM_DISTANCE_MULTIPLIER = 15.0f;
_avatarLODDistanceMultiplier = qMin(MAXIMUM_DISTANCE_MULTIPLIER, _avatarLODDistanceMultiplier *
(averageFps < EPSILON ? MAXIMUM_MULTIPLIER_SCALE : qMin(MAXIMUM_MULTIPLIER_SCALE, targetFps / averageFps)));
_lastAvatarDetailDrop = now;
}
} else if (_fastFPSAverage.getAverage() > ADJUST_LOD_UP_FPS) {
// let the detail level creep slowly upwards
const float DISTANCE_DECREASE_RATE = 0.02f;
const float DISTANCE_DECREASE_RATE = 0.05f;
const float MINIMUM_DISTANCE_MULTIPLIER = 0.1f;
_avatarLODDistanceMultiplier = qMax(MINIMUM_DISTANCE_MULTIPLIER,
_avatarLODDistanceMultiplier - DISTANCE_DECREASE_RATE);

View file

@ -18,7 +18,7 @@ FaceModel::FaceModel(Head* owningHead) :
{
}
void FaceModel::simulate(float deltaTime) {
void FaceModel::simulate(float deltaTime, bool fullUpdate) {
updateGeometry();
Avatar* owningAvatar = static_cast<Avatar*>(_owningHead->_owningAvatar);
glm::vec3 neckPosition;

View file

@ -21,7 +21,7 @@ public:
FaceModel(Head* owningHead);
void simulate(float deltaTime);
virtual void simulate(float deltaTime, bool fullUpdate = true);
protected:

View file

@ -14,6 +14,7 @@
#include "Head.h"
#include "Menu.h"
#include "Util.h"
#include "devices/OculusManager.h"
using namespace std;
@ -198,6 +199,9 @@ glm::quat Head::getFinalOrientation() const {
}
glm::quat Head::getCameraOrientation () const {
if (OculusManager::isConnected()) {
return getOrientation();
}
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.f, 0.0f)));
}

View file

@ -169,9 +169,6 @@ void MyAvatar::simulate(float deltaTime) {
// Collect thrust forces from keyboard and devices
updateThrust(deltaTime);
// copy velocity so we can use it later for acceleration
glm::vec3 oldVelocity = getVelocity();
// calculate speed
_speed = glm::length(_velocity);
@ -231,29 +228,6 @@ void MyAvatar::simulate(float deltaTime) {
// update the euler angles
setOrientation(orientation);
// Compute instantaneous acceleration
float forwardAcceleration = glm::length(glm::dot(getBodyFrontDirection(), getVelocity() - oldVelocity)) / deltaTime;
const float OCULUS_ACCELERATION_PULL_THRESHOLD = 1.0f;
const int OCULUS_YAW_OFFSET_THRESHOLD = 10;
if (!Application::getInstance()->getFaceshift()->isActive() && OculusManager::isConnected() &&
fabsf(forwardAcceleration) > OCULUS_ACCELERATION_PULL_THRESHOLD &&
fabs(getHead()->getBaseYaw()) > OCULUS_YAW_OFFSET_THRESHOLD) {
// if we're wearing the oculus
// and this acceleration is above the pull threshold
// and the head yaw if off the body by more than OCULUS_YAW_OFFSET_THRESHOLD
// match the body yaw to the oculus yaw
_bodyYaw = getAbsoluteHeadYaw();
// set the head yaw to zero for this draw
getHead()->setBaseYaw(0);
// correct the oculus yaw offset
OculusManager::updateYawOffset();
}
const float WALKING_SPEED_THRESHOLD = 0.2f;
// use speed and angular velocity to determine walking vs. standing
if (_speed + fabs(_bodyYawDelta) > WALKING_SPEED_THRESHOLD) {
@ -308,7 +282,7 @@ void MyAvatar::simulate(float deltaTime) {
head->simulate(deltaTime, true);
// Zero thrust out now that we've added it to velocity in this frame
_thrust = glm::vec3(0, 0, 0);
_thrust = glm::vec3(0.f);
// now that we're done stepping the avatar forward in time, compute new collisions
if (_collisionFlags != 0) {

View file

@ -8,11 +8,10 @@
#include <QDialog>
#include <QDialogButtonBox>
#include <QEventLoop>
#include <QGridLayout>
#include <QHeaderView>
#include <QMessageBox>
#include <QStringListModel>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QXmlStreamReader>
@ -33,27 +32,42 @@ static const QString KEY_NAME = "Key";
static const QString LOADING_MSG = "Loading...";
static const QString ERROR_MSG = "Error loading files";
static const QString DO_NOT_MODIFY_TAG = "DoNotModify";
enum ModelMetaData {
NAME,
CREATOR,
UPLOAD_DATE,
TYPE,
GENDER,
DATE_ADDED,
TOTAL_SIZE,
POLY_NUM,
TAGS,
MODEL_METADATA_COUNT
};
static const QString propertiesNames[MODEL_METADATA_COUNT] = {
"Name",
"Creator",
"Upload Date",
"Type",
"Gender"
"Date Added",
"Total Size",
"Poly#",
"Tags"
};
static const QString propertiesIds[MODEL_METADATA_COUNT] = {
DO_NOT_MODIFY_TAG,
"Creator",
"Date-Added",
"Total-Size",
"Poly-Num",
"Tags"
};
ModelsBrowser::ModelsBrowser(ModelType modelsType, QWidget* parent) :
QWidget(parent),
_handler(new ModelHandler(modelsType))
{
connect(_handler, SIGNAL(doneDownloading()), SLOT(resizeView()));
connect(_handler, SIGNAL(updated()), SLOT(resizeView()));
// Connect handler
_handler->connect(this, SIGNAL(startDownloading()), SLOT(download()));
_handler->connect(_handler, SIGNAL(doneDownloading()), SLOT(update()));
@ -86,7 +100,7 @@ void ModelsBrowser::applyFilter(const QString &filter) {
for (int k = 0; k < filters.count(); ++k) {
match = false;
for (int j = 0; j < MODEL_METADATA_COUNT; ++j) {
if (model->item(i, j)->text().contains(filters.at(k))) {
if (model->item(i, j)->text().contains(filters.at(k), Qt::CaseInsensitive)) {
match = true;
break;
}
@ -106,6 +120,12 @@ void ModelsBrowser::applyFilter(const QString &filter) {
_handler->unlockModel();
}
void ModelsBrowser::resizeView() {
for (int i = 0; i < MODEL_METADATA_COUNT; ++i) {
_view.resizeColumnToContents(i);
}
}
void ModelsBrowser::browse() {
QDialog dialog;
dialog.setWindowTitle("Browse models");
@ -145,8 +165,6 @@ ModelHandler::ModelHandler(ModelType modelsType, QWidget* parent) :
_initiateExit(false),
_type(modelsType)
{
connect(&_downloader, SIGNAL(done(QNetworkReply::NetworkError)), SLOT(downloadFinished()));
// set headers data
QStringList headerData;
for (int i = 0; i < MODEL_METADATA_COUNT; ++i) {
@ -156,9 +174,6 @@ ModelHandler::ModelHandler(ModelType modelsType, QWidget* parent) :
}
void ModelHandler::download() {
// Query models list
queryNewFiles();
_lock.lockForWrite();
if (_initiateExit) {
_lock.unlock();
@ -169,10 +184,25 @@ void ModelHandler::download() {
loadingItem->setEnabled(false);
_model.appendRow(loadingItem);
_lock.unlock();
// Query models list
queryNewFiles();
}
void ModelHandler::update() {
// Will be implemented in my next PR
_lock.lockForWrite();
if (_initiateExit) {
_lock.unlock();
return;
}
for (int i = 0; i < _model.rowCount(); ++i) {
QUrl url(_model.item(i,0)->data(Qt::UserRole).toString());
QNetworkAccessManager* accessManager = new QNetworkAccessManager(this);
QNetworkRequest request(url);
accessManager->head(request);
connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*)));
}
_lock.unlock();
}
void ModelHandler::exit() {
@ -180,7 +210,6 @@ void ModelHandler::exit() {
_initiateExit = true;
// Disconnect everything
_downloader.disconnect();
disconnect();
thread()->disconnect();
@ -191,12 +220,16 @@ void ModelHandler::exit() {
_lock.unlock();
}
void ModelHandler::downloadFinished() {
if (_downloader.getData().startsWith("<?xml")) {
parseXML(_downloader.getData());
void ModelHandler::downloadFinished(QNetworkReply* reply) {
QByteArray data = reply->readAll();
if (!data.isEmpty()) {
parseXML(data);
} else {
qDebug() << _downloader.getData();
parseHeaders(reply);
}
reply->deleteLater();
sender()->deleteLater();
}
void ModelHandler::queryNewFiles(QString marker) {
@ -219,7 +252,11 @@ void ModelHandler::queryNewFiles(QString marker) {
// Download
url.setQuery(query);
_downloader.download(url);
QNetworkAccessManager* accessManager = new QNetworkAccessManager(this);
QNetworkRequest request(url);
accessManager->get(request);
connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(downloadFinished(QNetworkReply*)));
}
bool ModelHandler::parseXML(QByteArray xmlFile) {
@ -266,18 +303,9 @@ bool ModelHandler::parseXML(QByteArray xmlFile) {
QList<QStandardItem*> model;
model << new QStandardItem(QFileInfo(xml.text().toString()).baseName());
model.first()->setData(PUBLIC_URL + "/" + xml.text().toString(), Qt::UserRole);
// Rand properties for now (Will be taken out in the next PR)
static QString creator[] = {"Ryan", "Philip", "Andzrej"};
static QString type[] = {"human", "beast", "pet", "elfe"};
static QString gender[] = {"male", "female", "none"};
model << new QStandardItem(creator[randIntInRange(0, 2)]);
model << new QStandardItem(QDate(randIntInRange(2013, 2014),
randIntInRange(1, 12),
randIntInRange(1, 30)).toString());
model << new QStandardItem(type[randIntInRange(0, 3)]);
model << new QStandardItem(gender[randIntInRange(0, 2)]);
////////////////////////////////////////////////////////////
for (int i = 1; i < MODEL_METADATA_COUNT; ++i) {
model << new QStandardItem();
}
_model.appendRow(model);
}
@ -312,9 +340,32 @@ bool ModelHandler::parseXML(QByteArray xmlFile) {
_lock.unlock();
if (!truncated) {
qDebug() << "Emitting...";
emit doneDownloading();
}
return true;
}
}
bool ModelHandler::parseHeaders(QNetworkReply* reply) {
_lock.lockForWrite();
QList<QStandardItem*> items = _model.findItems(QFileInfo(reply->url().toString()).baseName());
if (items.isEmpty() || items.first()->text() == DO_NOT_MODIFY_TAG) {
return false;
}
for (int i = 0; i < MODEL_METADATA_COUNT; ++i) {
for (int k = 1; k < reply->rawHeaderPairs().count(); ++k) {
QString key = reply->rawHeaderPairs().at(k).first.data();
QString item = reply->rawHeaderPairs().at(k).second.data();
if (key == propertiesIds[i]) {
_model.item(_model.indexFromItem(items.first()).row(), i)->setText(item);
}
}
}
_lock.unlock();
emit updated();
return true;
}

View file

@ -9,13 +9,10 @@
#ifndef __hifi__ModelsBrowser__
#define __hifi__ModelsBrowser__
#include <QAbstractTableModel>
#include <QStandardItem>
#include <QTreeView>
#include <QVector>
#include <QReadWriteLock>
#include <QStandardItemModel>
#include <QTreeView>
#include "FileDownloader.h"
enum ModelType {
Head,
@ -33,7 +30,7 @@ public:
signals:
void doneDownloading();
void doneUpdating();
void updated();
public slots:
void download();
@ -41,17 +38,17 @@ public slots:
void exit();
private slots:
void downloadFinished();
void downloadFinished(QNetworkReply* reply);
private:
bool _initiateExit;
ModelType _type;
FileDownloader _downloader;
QReadWriteLock _lock;
QStandardItemModel _model;
void queryNewFiles(QString marker = QString());
bool parseXML(QByteArray xmlFile);
bool parseHeaders(QNetworkReply* reply);
};
@ -71,6 +68,7 @@ public slots:
private slots:
void applyFilter(const QString& filter);
void resizeView();
private:
ModelHandler* _handler;

486
interface/src/ui/Stats.cpp Normal file
View file

@ -0,0 +1,486 @@
//
// Stats.cpp
// interface
//
// Created by Lucas Crisman on 22/03/14.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved
//
#include <sstream>
#include <stdlib.h>
#include <glm/glm.hpp>
#include <glm/gtx/component_wise.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/vector_angle.hpp>
#include "Stats.h"
#include "InterfaceConfig.h"
#include "Menu.h"
#include "Util.h"
const int STATS_PELS_PER_LINE = 20;
const int STATS_GENERAL_MIN_WIDTH = 165;
const int STATS_PING_MIN_WIDTH = 190;
const int STATS_GEO_MIN_WIDTH = 240;
const int STATS_VOXEL_MIN_WIDTH = 410;
Stats* Stats::getInstance() {
static Stats stats;
return &stats;
}
Stats::Stats():
_expanded(false),
_recentMaxPackets(0),
_resetRecentMaxPacketsSoon(true),
_generalStatsWidth(STATS_GENERAL_MIN_WIDTH),
_pingStatsWidth(STATS_PING_MIN_WIDTH),
_geoStatsWidth(STATS_GEO_MIN_WIDTH),
_voxelStatsWidth(STATS_VOXEL_MIN_WIDTH),
_lastHorizontalOffset(0)
{
QGLWidget* glWidget = Application::getInstance()->getGLWidget();
resetWidth(glWidget->width(), 0);
}
void Stats::toggleExpanded() {
_expanded = !_expanded;
}
// called on mouse click release
// check for clicks over stats in order to expand or contract them
void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset) {
QGLWidget* glWidget = Application::getInstance()->getGLWidget();
if (0 != glm::compMax(glm::abs(glm::ivec2(mouseX - mouseDragStartedX, mouseY - mouseDragStartedY)))) {
// not worried about dragging on stats
return;
}
int statsHeight = 0,
statsWidth = 0,
statsX = 0,
statsY = 0,
lines = 0;
statsX = horizontalOffset;
// top-left stats click
lines = _expanded ? 5 : 3;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
if (mouseX > statsX && mouseX < statsX + _generalStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
toggleExpanded();
return;
}
statsX += _generalStatsWidth;
// ping stats click
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
lines = _expanded ? 4 : 3;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
if (mouseX > statsX && mouseX < statsX + _pingStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
toggleExpanded();
return;
}
statsX += _pingStatsWidth;
}
// geo stats panel click
lines = _expanded ? 4 : 3;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
if (mouseX > statsX && mouseX < statsX + _geoStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
toggleExpanded();
return;
}
statsX += _geoStatsWidth;
// top-right stats click
lines = _expanded ? 11 : 3;
statsHeight = lines * STATS_PELS_PER_LINE + 10;
statsWidth = glWidget->width() - statsX;
if (mouseX > statsX && mouseX < statsX + statsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
toggleExpanded();
return;
}
}
void Stats::resetWidth(int width, int horizontalOffset) {
QGLWidget* glWidget = Application::getInstance()->getGLWidget();
int extraSpace = glWidget->width() - horizontalOffset -2
- STATS_GENERAL_MIN_WIDTH
- (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0)
- STATS_GEO_MIN_WIDTH
- STATS_VOXEL_MIN_WIDTH;
int panels = 4;
_generalStatsWidth = STATS_GENERAL_MIN_WIDTH;
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
_pingStatsWidth = STATS_PING_MIN_WIDTH;
} else {
_pingStatsWidth = 0;
panels = 3;
}
_geoStatsWidth = STATS_GEO_MIN_WIDTH;
_voxelStatsWidth = STATS_VOXEL_MIN_WIDTH;
if (extraSpace > panels) {
_generalStatsWidth += (int) extraSpace / panels;
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
_pingStatsWidth += (int) extraSpace / panels;
}
_geoStatsWidth += (int) extraSpace / panels;
_voxelStatsWidth += glWidget->width() - (_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3);
}
}
// translucent background box that makes stats more readable
void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int height) {
glBegin(GL_QUADS);
glColor4f(((rgba >> 24) & 0xff) / 255.0f,
((rgba >> 16) & 0xff) / 255.0f,
((rgba >> 8) & 0xff) / 255.0f,
(rgba & 0xff) / 255.0f);
glVertex3f(x, y, 0);
glVertex3f(x + width, y, 0);
glVertex3f(x + width, y + height, 0);
glVertex3f(x , y + height, 0);
glEnd();
glColor4f(1, 1, 1, 1);
}
// display expanded or contracted stats
void Stats::display(
const float* color,
int horizontalOffset,
float fps,
int packetsPerSecond,
int bytesPerSecond,
int voxelPacketsToProcess)
{
QGLWidget* glWidget = Application::getInstance()->getGLWidget();
unsigned int backgroundColor = 0x33333399;
int verticalOffset = 0, lines = 0;
QLocale locale(QLocale::English);
std::stringstream voxelStats;
if (_lastHorizontalOffset != horizontalOffset) {
resetWidth(glWidget->width(), horizontalOffset);
_lastHorizontalOffset = horizontalOffset;
}
glPointSize(1.0f);
// we need to take one avatar out so we don't include ourselves
int totalAvatars = Application::getInstance()->getAvatarManager().size() - 1;
int totalServers = NodeList::getInstance()->size();
lines = _expanded ? 5 : 3;
drawBackground(backgroundColor, horizontalOffset, 0, _generalStatsWidth, lines * STATS_PELS_PER_LINE + 10);
horizontalOffset += 5;
char serverNodes[30];
sprintf(serverNodes, "Servers: %d", totalServers);
char avatarNodes[30];
sprintf(avatarNodes, "Avatars: %d", totalAvatars);
char framesPerSecond[30];
sprintf(framesPerSecond, "Framerate: %3.0f FPS", fps);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, serverNodes, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarNodes, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, framesPerSecond, color);
if (_expanded) {
char packetsPerSecondString[30];
sprintf(packetsPerSecondString, "Pkts/sec: %d", packetsPerSecond);
char averageMegabitsPerSecond[30];
sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)bytesPerSecond * 8.f / 1000000.f);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, packetsPerSecondString, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, averageMegabitsPerSecond, color);
}
verticalOffset = 0;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth +1;
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0;
NodeList* nodeList = NodeList::getInstance();
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : 0;
pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : 0;
// Now handle voxel servers, since there could be more than one, we average their ping times
unsigned long totalPingVoxel = 0;
int voxelServerCount = 0;
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
if (node->getType() == NodeType::VoxelServer) {
totalPingVoxel += node->getPingMs();
voxelServerCount++;
if (pingVoxelMax < node->getPingMs()) {
pingVoxelMax = node->getPingMs();
}
}
}
if (voxelServerCount) {
pingVoxel = totalPingVoxel/voxelServerCount;
}
lines = _expanded ? 4 : 3;
drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10);
horizontalOffset += 5;
Audio* audio = Application::getInstance()->getAudio();
char audioJitter[30];
sprintf(audioJitter,
"Buffer msecs %.1f",
(float) (audio->getNetworkBufferLengthSamplesPerChannel() + (float) audio->getJitterBufferSamples()) /
(float) audio->getNetworkSampleRate() * 1000.f);
drawText(30, glWidget->height() - 22, 0.10f, 0.f, 2.f, audioJitter, color);
char audioPing[30];
sprintf(audioPing, "Audio ping: %d", pingAudio);
char avatarPing[30];
sprintf(avatarPing, "Avatar ping: %d", pingAvatar);
char voxelAvgPing[30];
sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, audioPing, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPing, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelAvgPing, color);
if (_expanded) {
char voxelMaxPing[30];
sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelMaxPing, color);
}
verticalOffset = 0;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2;
}
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();
lines = _expanded ? 4 : 3;
drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, lines * STATS_PELS_PER_LINE + 10);
horizontalOffset += 5;
char avatarPosition[200];
sprintf(avatarPosition, "Position: %.1f, %.1f, %.1f", avatarPos.x, avatarPos.y, avatarPos.z);
char avatarVelocity[30];
sprintf(avatarVelocity, "Velocity: %.1f", glm::length(myAvatar->getVelocity()));
char avatarBodyYaw[30];
sprintf(avatarBodyYaw, "Yaw: %.1f", myAvatar->getBodyYaw());
char avatarMixerStats[200];
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPosition, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarVelocity, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarBodyYaw, color);
if (_expanded) {
SharedNodePointer avatarMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AvatarMixer);
if (avatarMixer) {
sprintf(avatarMixerStats, "Avatar Mixer: %.f kbps, %.f pps",
roundf(avatarMixer->getAverageKilobitsPerSecond()),
roundf(avatarMixer->getAveragePacketsPerSecond()));
} else {
sprintf(avatarMixerStats, "No Avatar Mixer");
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarMixerStats, color);
}
verticalOffset = 0;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3;
VoxelSystem* voxels = Application::getInstance()->getVoxels();
lines = _expanded ? 12 : 3;
drawBackground(backgroundColor, horizontalOffset, 0, glWidget->width() - horizontalOffset, lines * STATS_PELS_PER_LINE + 10);
horizontalOffset += 5;
if (_expanded) {
// Local Voxel Memory Usage
voxelStats.str("");
voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
voxelStats.str("");
voxelStats <<
"Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.f << "MB / " <<
"VBO: " << voxels->getVoxelMemoryUsageVBO() / 1000000.f << "MB";
if (voxels->hasVoxelMemoryUsageGPU()) {
voxelStats << " / GPU: " << voxels->getVoxelMemoryUsageGPU() / 1000000.f << "MB";
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
// Voxel Rendering
voxelStats.str("");
voxelStats.precision(4);
voxelStats << "Voxel Rendering Slots Max: " << voxels->getMaxVoxels() / 1000.f << "K";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
}
voxelStats.str("");
voxelStats.precision(4);
voxelStats << "Drawn: " << voxels->getVoxelsWritten() / 1000.f << "K " <<
"Abandoned: " << voxels->getAbandonedVoxels() / 1000.f << "K ";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
// iterate all the current voxel stats, and list their sending modes, and total voxel counts
std::stringstream sendingMode("");
sendingMode << "Octree Sending Mode: [";
int serverCount = 0;
int movingServerCount = 0;
unsigned long totalNodes = 0;
unsigned long totalInternal = 0;
unsigned long totalLeaves = 0;
NodeToOctreeSceneStats* octreeServerSceneStats = Application::getInstance()->getOcteeSceneStats();
for(NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) {
//const QUuid& uuid = i->first;
OctreeSceneStats& stats = i->second;
serverCount++;
if (_expanded) {
if (serverCount > 1) {
sendingMode << ",";
}
if (stats.isMoving()) {
sendingMode << "M";
movingServerCount++;
} else {
sendingMode << "S";
}
}
// calculate server node totals
totalNodes += stats.getTotalElements();
if (_expanded) {
totalInternal += stats.getTotalInternal();
totalLeaves += stats.getTotalLeaves();
}
}
if (_expanded) {
if (serverCount == 0) {
sendingMode << "---";
}
sendingMode << "] " << serverCount << " servers";
if (movingServerCount > 0) {
sendingMode << " <SCENE NOT STABLE>";
} else {
sendingMode << " <SCENE STABLE>";
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)sendingMode.str().c_str(), color);
}
// Incoming packets
if (_expanded) {
voxelStats.str("");
QString packetsString = locale.toString((int)voxelPacketsToProcess);
QString maxString = locale.toString((int)_recentMaxPackets);
voxelStats << "Voxel Packets to Process: " << qPrintable(packetsString)
<< " [Recent Max: " << qPrintable(maxString) << "]";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
}
if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) {
_recentMaxPackets = 0;
_resetRecentMaxPacketsSoon = false;
}
if (voxelPacketsToProcess == 0) {
_resetRecentMaxPacketsSoon = true;
} else {
if (voxelPacketsToProcess > _recentMaxPackets) {
_recentMaxPackets = voxelPacketsToProcess;
}
}
verticalOffset += (_expanded ? STATS_PELS_PER_LINE : 0);
QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' ');
// Server Voxels
voxelStats.str("");
voxelStats << "Server voxels: " << qPrintable(serversTotalString);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
if (_expanded) {
QString serversInternalString = locale.toString((uint)totalInternal);
QString serversLeavesString = locale.toString((uint)totalLeaves);
voxelStats.str("");
voxelStats <<
"Internal: " << qPrintable(serversInternalString) << " " <<
"Leaves: " << qPrintable(serversLeavesString) << "";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
}
unsigned long localTotal = VoxelTreeElement::getNodeCount();
QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' ');
// Local Voxels
voxelStats.str("");
voxelStats << "Local voxels: " << qPrintable(localTotalString);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
if (_expanded) {
unsigned long localInternal = VoxelTreeElement::getInternalNodeCount();
unsigned long localLeaves = VoxelTreeElement::getLeafNodeCount();
QString localInternalString = locale.toString((uint)localInternal);
QString localLeavesString = locale.toString((uint)localLeaves);
voxelStats.str("");
voxelStats <<
"Internal: " << qPrintable(localInternalString) << " " <<
"Leaves: " << qPrintable(localLeavesString) << "";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), color);
}
// LOD Details
if (_expanded) {
voxelStats.str("");
QString displayLODDetails = Menu::getInstance()->getLODFeedbackText();
voxelStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed());
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
}
}

41
interface/src/ui/Stats.h Normal file
View file

@ -0,0 +1,41 @@
//
// Stats.h
// interface
//
// Created by Lucas Crisman on 22/03/14.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QObject>
#include <NodeList.h>
class Stats: public QObject {
Q_OBJECT
public:
static Stats* getInstance();
Stats();
static void drawBackground(unsigned int rgba, int x, int y, int width, int height);
void toggleExpanded();
void checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset);
void resetWidth(int width, int horizontalOffset);
void display(const float* color, int horizontalOffset, float fps, int packetsPerSecond, int bytesPerSecond, int voxelPacketsToProcess);
private:
static Stats* _sharedInstance;
bool _expanded;
int _recentMaxPackets; // recent max incoming voxel packets to process
bool _resetRecentMaxPacketsSoon;
int _generalStatsWidth;
int _pingStatsWidth;
int _geoStatsWidth;
int _voxelStatsWidth;
int _lastHorizontalOffset;
};

View file

@ -1,45 +0,0 @@
//
// FileDownloader.cpp
// hifi
//
// Created by Clement Brisset on 3/14/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
//
#include <QUrl>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QTimer>
#include "FileDownloader.h"
FileDownloader::FileDownloader(QObject* parent) : QObject(parent) {
connect(&_networkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(processReply(QNetworkReply*)));
}
void FileDownloader::download(const QUrl& dataURL, QNetworkAccessManager::Operation operation) {
QNetworkRequest request(dataURL);
_downloadedData.clear();
switch (operation) {
case QNetworkAccessManager::GetOperation:
_networkAccessManager.get(request);
break;
case QNetworkAccessManager::HeadOperation:
_networkAccessManager.head(request);
break;
default:
emit done(QNetworkReply::ProtocolInvalidOperationError);
break;
}
}
void FileDownloader::processReply(QNetworkReply *reply) {
if (reply->error() == QNetworkReply::NoError) {
_downloadedData = reply->readAll();
}
reply->deleteLater();
emit done(reply->error());
}

View file

@ -1,41 +0,0 @@
//
// FileDownloader.h
// hifi
//
// Created by Clement Brisset on 3/14/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
//
#ifndef __hifi__FileDownloader__
#define __hifi__FileDownloader__
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QThread>
class FileDownloader : public QObject {
Q_OBJECT
public:
FileDownloader(QObject* parent = NULL);
QByteArray getData() const { return _downloadedData; }
signals:
void done(QNetworkReply::NetworkError error);
public slots:
void download(const QUrl& dataURL, QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation);
private slots:
void processReply(QNetworkReply* reply);
private:
QNetworkAccessManager _networkAccessManager;
QByteArray _downloadedData;
};
#endif /* defined(__hifi__FileDownloader__) */

View file

@ -1,5 +1,5 @@
//
// FstReader.cpp
// ModelUploader.cpp
// hifi
//
// Created by Clément Brisset on 3/4/14.
@ -18,7 +18,7 @@
#include <QMessageBox>
#include "AccountManager.h"
#include "FstReader.h"
#include "ModelUploader.h"
static const QString NAME_FIELD = "name";
@ -38,7 +38,7 @@ public:
}
};
FstReader::FstReader(bool isHead) :
ModelUploader::ModelUploader(bool isHead) :
_zipDir(new TemporaryDir()),
_lodCount(-1),
_texturesCount(-1),
@ -51,11 +51,11 @@ FstReader::FstReader(bool isHead) :
}
FstReader::~FstReader() {
ModelUploader::~ModelUploader() {
delete _dataMultiPart;
}
bool FstReader::zip() {
bool ModelUploader::zip() {
// File Dialog
QString filename = QFileDialog::getOpenFileName(NULL,
"Select your .fst file ...",
@ -157,9 +157,9 @@ bool FstReader::zip() {
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
" name=\"model_category\"");
if (_isHead) {
textPart.setBody("head");
textPart.setBody("heads");
} else {
textPart.setBody("skeleton");
textPart.setBody("skeletons");
}
_dataMultiPart->append(textPart);
@ -167,20 +167,41 @@ bool FstReader::zip() {
return true;
}
bool FstReader::send() {
bool ModelUploader::send() {
if (!_readyToSend) {
return false;
}
AccountManager::getInstance().authenticatedRequest(MODEL_URL, QNetworkAccessManager::PostOperation, JSONCallbackParameters(), QByteArray(), _dataMultiPart);
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.jsonCallbackMethod = "uploadSuccess";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "uploadFailed";
AccountManager::getInstance().authenticatedRequest(MODEL_URL, QNetworkAccessManager::PostOperation, callbackParams, QByteArray(), _dataMultiPart);
_zipDir = NULL;
_dataMultiPart = NULL;
qDebug() << "Model sent.";
qDebug() << "Sending model...";
return true;
}
bool FstReader::addTextures(const QFileInfo& texdir) {
void ModelUploader::uploadSuccess(const QJsonObject& jsonResponse) {
qDebug() << "Model sent with success to the data server.";
qDebug() << "It might take a few minute for it to appear in your model browser.";
deleteLater();
}
void ModelUploader::uploadFailed(QNetworkReply::NetworkError errorCode, const QString& errorString) {
QMessageBox::warning(NULL,
QString("ModelUploader::uploadFailed()"),
QString("Model could not be sent to the data server."),
QMessageBox::Ok);
qDebug() << "Model upload failed (" << errorCode << "): " << errorString;
deleteLater();
}
bool ModelUploader::addTextures(const QFileInfo& texdir) {
QStringList filter;
filter << "*.png" << "*.tif" << "*.jpg" << "*.jpeg";
@ -209,7 +230,7 @@ bool FstReader::addTextures(const QFileInfo& texdir) {
return true;
}
bool FstReader::compressFile(const QString &inFileName, const QString &outFileName) {
bool ModelUploader::compressFile(const QString &inFileName, const QString &outFileName) {
QFile inFile(inFileName);
inFile.open(QIODevice::ReadOnly);
QByteArray buffer = inFile.readAll();
@ -233,7 +254,7 @@ bool FstReader::compressFile(const QString &inFileName, const QString &outFileNa
}
bool FstReader::addPart(const QString &path, const QString& name) {
bool ModelUploader::addPart(const QString &path, const QString& name) {
QFile* file = new QFile(path);
if (!file->open(QIODevice::ReadOnly)) {
QMessageBox::warning(NULL,

View file

@ -1,5 +1,5 @@
//
// FstReader.h
// ModelUploader.h
// hifi
//
// Created by Clément Brisset on 3/4/14.
@ -7,20 +7,27 @@
//
//
#ifndef __hifi__FstReader__
#define __hifi__FstReader__
#ifndef __hifi__ModelUploader__
#define __hifi__ModelUploader__
class TemporaryDir;
class QHttpMultiPart;
class QFileInfo;
class FstReader : public QObject {
class ModelUploader : public QObject {
Q_OBJECT
public:
FstReader(bool isHead);
~FstReader();
ModelUploader(bool isHead);
~ModelUploader();
bool zip();
bool send();
private slots:
void uploadSuccess(const QJsonObject& jsonResponse);
void uploadFailed(QNetworkReply::NetworkError errorCode, const QString& errorString);
private:
TemporaryDir* _zipDir;
int _lodCount;
@ -37,4 +44,4 @@ private:
bool addPart(const QString& path, const QString& name);
};
#endif /* defined(__hifi__FstReader__) */
#endif /* defined(__hifi__ModelUploader__) */