From 925f8241e5062baf69cdc3d63209c41458596034 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 25 Mar 2014 11:48:46 -0700 Subject: [PATCH 01/53] adding joint animation --- examples/bot.js | 53 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/examples/bot.js b/examples/bot.js index ea78f40de9..c606b55274 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -23,10 +23,10 @@ function printVector(string, vector) { } var CHANCE_OF_MOVING = 0.005; -var CHANCE_OF_SOUND = 0.005; +var CHANCE_OF_SOUND = 0.000; var CHANCE_OF_HEAD_TURNING = 0.05; var CHANCE_OF_BIG_MOVE = 0.1; -var CHANCE_OF_WAVING = 0.005; // Currently this isn't working +var CHANCE_OF_WAVING = 0.009; var shouldReceiveVoxels = true; var VOXEL_FPS = 60.0; @@ -39,12 +39,16 @@ var isWaving = false; var waveFrequency = 0.0; var waveAmplitude = 0.0; -var X_MIN = 0.0; -var X_MAX = 5.0; -var Z_MIN = 0.0; -var Z_MAX = 5.0; +var X_MIN = 20.0; +var X_MAX = 25.0; +var Z_MIN = 20.0; +var Z_MAX = 25.0; var Y_PELVIS = 2.5; -var SHOULDER_JOINT_NUMBER = 15; +var SPINE_JOINT_NUMBER = 13; +var SHOULDER_JOINT_NUMBER = 17; +var ELBOW_JOINT_NUMBER = 18; +var JOINT_R_HIP = 1; +var JOINT_R_KNEE = 2; var MOVE_RANGE_SMALL = 0.5; var MOVE_RANGE_BIG = Math.max(X_MAX - X_MIN, Z_MAX - Z_MIN) / 2.0; @@ -61,6 +65,9 @@ var targetDirection = { x: 0, y: 0, z: 0, w: 0 }; var currentDirection = { x: 0, y: 0, z: 0, w: 0 }; var targetHeadPitch = 0.0; +var walkFrequency = 5.0; +var walkAmplitude = 45.0; + var cumulativeTime = 0.0; var sounds = []; @@ -115,12 +122,29 @@ printVector("New bot, position = ", Avatar.position); function stopWaving() { isWaving = false; Avatar.clearJointData(SHOULDER_JOINT_NUMBER); + Avatar.clearJointData(ELBOW_JOINT_NUMBER); + Avatar.clearJointData(SPINE_JOINT_NUMBER); +} + +function keepWalking() { + Avatar.setJointData(JOINT_R_HIP, Quat.fromPitchYawRollDegrees(walkAmplitude * Math.sin(cumulativeTime * walkFrequency), 0.0, 0.0)); +} + +function stopWalking() { + Avatar.clearJointData(JOINT_R_HIP); + Avatar.clearJointData(JOINT_R_KNEE); } function updateBehavior(deltaTime) { cumulativeTime += deltaTime; + // Hack - right now you need to set the avatar position a bit after the avatar is made to make sure it's there. + + if (CHANCE_OF_MOVING = 0.000) { + Avatar.position = firstPosition; + } + if (shouldReceiveVoxels && ((cumulativeTime - lastVoxelQueryTime) > (1.0 / VOXEL_FPS))) { VoxelViewer.setPosition(Avatar.position); VoxelViewer.setOrientation(Avatar.orientation); @@ -134,13 +158,18 @@ function updateBehavior(deltaTime) { if (!isWaving && (Math.random() < CHANCE_OF_WAVING)) { isWaving = true; - waveFrequency = 1.0 + Math.random() * 5.0; + waveFrequency = 3.0 + Math.random() * 5.0; waveAmplitude = 5.0 + Math.random() * 60.0; Script.setTimeout(stopWaving, 1000 + Math.random() * 2000); + Avatar.setJointData(ELBOW_JOINT_NUMBER, Quat.fromPitchYawRollDegrees(0.0, 45, 0.0)); // Initially turn the palm outward } else if (isWaving) { - Avatar.setJointData(SHOULDER_JOINT_NUMBER, Quat.fromPitchYawRollDegrees(0.0, 0.0, waveAmplitude * Math.sin(cumulativeTime * waveFrequency))); + Avatar.setJointData(SHOULDER_JOINT_NUMBER, Quat.fromPitchYawRollDegrees(0.0, 0.0, 60 + waveAmplitude * Math.sin((cumulativeTime - 0.25) * waveFrequency))); + Avatar.setJointData(ELBOW_JOINT_NUMBER, Quat.fromPitchYawRollDegrees(0.0, 0.0, 25 + waveAmplitude/2.0 * Math.sin(cumulativeTime * 1.2 * waveFrequency))); + Avatar.setJointData(SPINE_JOINT_NUMBER, Quat.fromPitchYawRollDegrees(0.0, 0.0, 60 + waveAmplitude/4.0 * Math.sin(cumulativeTime * waveFrequency))); + } + if (Math.random() < CHANCE_OF_SOUND) { playRandomSound(); } @@ -168,11 +197,13 @@ function updateBehavior(deltaTime) { targetPosition.y = Y_PELVIS; isMoving = true; - } else { + } else if (isMoving) { + keepWalking(); Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(Vec3.subtract(targetPosition, Avatar.position), MOVE_RATE)); Avatar.orientation = Quat.mix(Avatar.orientation, targetDirection, TURN_RATE); if (Vec3.length(Vec3.subtract(Avatar.position, targetPosition)) < STOP_TOLERANCE) { - isMoving = false; + isMoving = false; + stopWalking(); } } } From d90244c01ef4715b2600e28e229cab73c19b432c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 25 Mar 2014 22:31:44 -0700 Subject: [PATCH 02/53] first pass, clipping audio meter --- interface/src/Application.cpp | 38 ++++++++++++++++++++++++++++++++++- interface/src/Audio.cpp | 10 +++++++++ interface/src/Audio.h | 2 ++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 13c05fa702..549d1f04e2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2494,7 +2494,7 @@ void Application::displayOverlay() { renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude()); } } - + if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { _audio.renderMuteIcon(1, _glWidget->height() - 50); if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { @@ -2502,6 +2502,42 @@ void Application::displayOverlay() { _audioScope.render(25, oscilloscopeTop); } } + + const int AUDIO_METER_WIDTH = 300; + const int AUDIO_METER_INSET = 2; + const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET; + const int AUDIO_METER_HEIGHT = 8; + const int AUDIO_METER_Y = _glWidget->height() - 40; + const int AUDIO_METER_X = 25; + const float CLIPPING_INDICATOR_TIME = 1.0f; + float audioLevel = log10(_audio.getLastInputLoudness() + 1.0) / log10(32767.0) * (float)AUDIO_METER_SCALE_WIDTH; + bool isClipping = ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); + + if (isClipping) { + glColor3f(1, 0, 0); + } else { + glColor3f(0, 0, 0); + } + glBegin(GL_QUADS); + // Draw background Quad + glVertex2i(AUDIO_METER_X, AUDIO_METER_Y); + glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, AUDIO_METER_Y); + glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, AUDIO_METER_Y + AUDIO_METER_HEIGHT); + glVertex2i(AUDIO_METER_X, AUDIO_METER_Y + AUDIO_METER_HEIGHT); + + // Draw Meter Quad + if (isClipping) { + glColor3f(1, 1, 1); + } else { + glColor3f(0, 1, 1); + } + // Draw Meter quad + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, AUDIO_METER_Y + AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, AUDIO_METER_Y + AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, AUDIO_METER_Y + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, AUDIO_METER_Y + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); + + glEnd(); if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) { _myAvatar->renderHeadMouse(); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 734b5345fb..92bbea3afe 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -60,6 +60,7 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p _measuredJitter(0), _jitterBufferSamples(initialJitterBufferSamples), _lastInputLoudness(0), + _timeSinceLastClip(-1.0), _dcOffset(0), _noiseGateMeasuredFloor(0), _noiseGateSampleCounter(0), @@ -469,13 +470,22 @@ void Audio::handleAudioInput() { const int NOISE_GATE_CLOSE_FRAME_DELAY = 5; const int NOISE_GATE_FRAMES_TO_AVERAGE = 5; const float DC_OFFSET_AVERAGING = 0.99f; + const float CLIPPING_THRESHOLD = 0.90f; float measuredDcOffset = 0.f; + // Increment the time since the last clip + if (_timeSinceLastClip >= 0.0f) { + _timeSinceLastClip += (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float) SAMPLE_RATE; + } + for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { measuredDcOffset += monoAudioSamples[i]; monoAudioSamples[i] -= (int16_t) _dcOffset; thisSample = fabsf(monoAudioSamples[i]); + if (thisSample > (32767.f * CLIPPING_THRESHOLD)) { + _timeSinceLastClip = 0.0f; + } loudness += thisSample; // Noise Reduction: Count peaks above the average loudness if (thisSample > (_noiseGateMeasuredFloor * NOISE_GATE_HEIGHT)) { diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 7aa1ef5afe..2d79e82918 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -47,6 +47,7 @@ public: Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* parent = 0); float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.f); } + float getTimeSinceLastClip() const { return _timeSinceLastClip; } float getAudioAverageInputLoudness() const { return _lastInputLoudness; } void setNoiseGateEnabled(bool noiseGateEnabled) { _noiseGateEnabled = noiseGateEnabled; } @@ -130,6 +131,7 @@ private: float _measuredJitter; int16_t _jitterBufferSamples; float _lastInputLoudness; + float _timeSinceLastClip; float _dcOffset; float _noiseGateMeasuredFloor; float* _noiseSampleFrames; From 5cce4b3c03f96d26af9d5df8b4873aeea9df50b0 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 26 Mar 2014 12:14:03 -0700 Subject: [PATCH 03/53] added more movement --- examples/bot.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/bot.js b/examples/bot.js index c606b55274..acd837e3b8 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -128,6 +128,7 @@ function stopWaving() { function keepWalking() { Avatar.setJointData(JOINT_R_HIP, Quat.fromPitchYawRollDegrees(walkAmplitude * Math.sin(cumulativeTime * walkFrequency), 0.0, 0.0)); + Avatar.setJointData(JOINT_R_KNEE, Quat.fromPitchYawRollDegrees(walkAmplitude * Math.sin(cumulativeTime * walkFrequency), 0.0, 0.0)); } function stopWalking() { @@ -141,7 +142,7 @@ function updateBehavior(deltaTime) { // Hack - right now you need to set the avatar position a bit after the avatar is made to make sure it's there. - if (CHANCE_OF_MOVING = 0.000) { + if (CHANCE_OF_MOVING == 0.000) { Avatar.position = firstPosition; } From 718b8f7eb9adca76dbb4a37733c362816ee01d71 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 26 Mar 2014 15:04:45 -0700 Subject: [PATCH 04/53] Changed back to upload either head or skeleton --- interface/src/Application.cpp | 12 ++++++++++-- interface/src/Application.h | 4 +++- interface/src/Menu.cpp | 3 ++- interface/src/Menu.h | 3 ++- libraries/shared/src/FstReader.cpp | 9 +++------ libraries/shared/src/FstReader.h | 2 +- 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 13c05fa702..5e0d49a94a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3512,13 +3512,21 @@ void Application::reloadAllScripts() { } } -void Application::uploadFST() { - FstReader reader; +void Application::uploadFST(bool isHead) { + FstReader reader(isHead); if (reader.zip()) { reader.send(); } } +void Application::uploadHead() { + uploadFST(true); +} + +void Application::uploadSkeleton() { + uploadFST(false); +} + void Application::removeScriptName(const QString& fileNameString) { _activeScripts.removeOne(fileNameString); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 15778f2a17..6350d1b63e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -261,7 +261,9 @@ public slots: void stopAllScripts(); void reloadAllScripts(); - void uploadFST(); + void uploadFST(bool isHead); + void uploadHead(); + void uploadSkeleton(); private slots: void timer(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 30be26ee96..d9dcb23b77 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -144,7 +144,8 @@ Menu::Menu() : SLOT(goTo())); addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model"); - addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadFST, 0, Application::getInstance(), SLOT(uploadFST())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0, Application::getInstance(), SLOT(uploadSkeleton())); addDisabledActionAndSeparator(fileMenu, "Settings"); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 9452ba220d..25810f0bdd 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -298,7 +298,8 @@ namespace MenuOption { const QString StopAllScripts = "Stop All Scripts"; const QString TestPing = "Test Ping"; const QString TransmitterDrive = "Transmitter Drive"; - const QString UploadFST = "Upload FST file"; + const QString UploadHead = "Upload Head Model"; + const QString UploadSkeleton = "Upload Skeleton Model"; const QString Visage = "Visage"; const QString Quit = "Quit"; const QString Voxels = "Voxels"; diff --git a/libraries/shared/src/FstReader.cpp b/libraries/shared/src/FstReader.cpp index 0fb9c46458..551731a542 100644 --- a/libraries/shared/src/FstReader.cpp +++ b/libraries/shared/src/FstReader.cpp @@ -25,17 +25,16 @@ static const QString NAME_FIELD = "name"; static const QString FILENAME_FIELD = "filename"; static const QString TEXDIR_FIELD = "texdir"; static const QString LOD_FIELD = "lod"; -static const QString HEAD_SPECIFIC_FIELD = "bs"; static const QString MODEL_URL = "/api/v1/models"; static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB -FstReader::FstReader() : +FstReader::FstReader(bool isHead) : _lodCount(-1), _texturesCount(-1), _totalSize(0), - _isHead(false), + _isHead(isHead), _readyToSend(false), _dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)) { @@ -95,9 +94,7 @@ bool FstReader::zip() { } // according to what is read, we modify the command - if (line[1] == HEAD_SPECIFIC_FIELD) { - _isHead = true; - } else if (line[1] == NAME_FIELD) { + if (line[0] == NAME_FIELD) { QHttpPart textPart; textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" " name=\"model_name\""); diff --git a/libraries/shared/src/FstReader.h b/libraries/shared/src/FstReader.h index 1d9da71641..d06742810b 100644 --- a/libraries/shared/src/FstReader.h +++ b/libraries/shared/src/FstReader.h @@ -16,7 +16,7 @@ class QHttpMultiPart; class FstReader { public: - FstReader(); + FstReader(bool isHead); ~FstReader(); bool zip(); From fa2a60448bb808cb82b453faaf6281492bc20aa3 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 26 Mar 2014 15:09:44 -0700 Subject: [PATCH 05/53] Fixed double free crash --- libraries/shared/src/FstReader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/shared/src/FstReader.cpp b/libraries/shared/src/FstReader.cpp index 551731a542..15e31cee07 100644 --- a/libraries/shared/src/FstReader.cpp +++ b/libraries/shared/src/FstReader.cpp @@ -170,6 +170,7 @@ bool FstReader::send() { } AccountManager::getInstance().authenticatedRequest(MODEL_URL, QNetworkAccessManager::PostOperation, JSONCallbackParameters(), QByteArray(), _dataMultiPart); + _dataMultiPart = NULL; return true; } From ce186cf0f79689cdf4b4c03a23af90434ba7eefc Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 26 Mar 2014 15:10:18 -0700 Subject: [PATCH 06/53] Added bunch of feedback --- libraries/shared/src/FstReader.cpp | 40 +++++++++++++++++------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/libraries/shared/src/FstReader.cpp b/libraries/shared/src/FstReader.cpp index 15e31cee07..e3b88f3870 100644 --- a/libraries/shared/src/FstReader.cpp +++ b/libraries/shared/src/FstReader.cpp @@ -62,19 +62,19 @@ bool FstReader::zip() { QString("ModelUploader::zip()"), QString("Could not open FST file."), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("Could not open FST file."); return false; } + qDebug() << "Reading FST file : " << QFileInfo(fst).filePath(); // Compress and copy the fst if (!compressFile(QFileInfo(fst).filePath(), _zipDir.path() + "/" + QFileInfo(fst).fileName())) { return false; } - _totalSize += QFileInfo(fst).size(); if (!addPart(_zipDir.path() + "/" + QFileInfo(fst).fileName(), QString("fst"))) { return false; } - qDebug() << "Reading FST file : " << QFileInfo(fst).filePath(); // Let's read through the FST file QTextStream stream(&fst); @@ -85,14 +85,6 @@ bool FstReader::zip() { continue; } - if (_totalSize > MAX_SIZE) { - QMessageBox::warning(NULL, - QString("ModelUploader::zip()"), - QString("Model too big, over %1 Bytes.").arg(MAX_SIZE), - QMessageBox::Ok); - return false; - } - // according to what is read, we modify the command if (line[0] == NAME_FIELD) { QHttpPart textPart; @@ -100,56 +92,56 @@ bool FstReader::zip() { " name=\"model_name\""); textPart.setBody(line[1].toUtf8()); _dataMultiPart->append(textPart); - } else if (line[1] == FILENAME_FIELD) { + } else if (line[0] == FILENAME_FIELD) { QFileInfo fbx(QFileInfo(fst).path() + "/" + line[1]); if (!fbx.exists() || !fbx.isFile()) { // Check existence QMessageBox::warning(NULL, QString("ModelUploader::zip()"), QString("FBX file %1 could not be found.").arg(fbx.fileName()), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(fbx.fileName()); return false; } // Compress and copy if (!compressFile(fbx.filePath(), _zipDir.path() + "/" + line[1])) { return false; } - _totalSize += fbx.size(); if (!addPart(_zipDir.path() + "/" + line[1], "fbx")) { return false; } - } else if (line[1] == TEXDIR_FIELD) { // Check existence + } else if (line[0] == TEXDIR_FIELD) { // Check existence QFileInfo texdir(QFileInfo(fst).path() + "/" + line[1]); if (!texdir.exists() || !texdir.isDir()) { QMessageBox::warning(NULL, QString("ModelUploader::zip()"), QString("Texture directory could not be found."), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("Texture directory could not be found."); return false; } if (!addTextures(texdir)) { // Recursive compress and copy return false; } - } else if (line[1] == LOD_FIELD) { + } else if (line[0] == LOD_FIELD) { QFileInfo lod(QFileInfo(fst).path() + "/" + line[1]); if (!lod.exists() || !lod.isFile()) { // Check existence QMessageBox::warning(NULL, QString("ModelUploader::zip()"), QString("FBX file %1 could not be found.").arg(lod.fileName()), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(lod.fileName()); return false; } // Compress and copy if (!compressFile(lod.filePath(), _zipDir.path() + "/" + line[1])) { return false; } - _totalSize += lod.size(); if (!addPart(_zipDir.path() + "/" + line[1], QString("lod%1").arg(++_lodCount))) { return false; } } } - QHttpPart textPart; textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" " name=\"model_category\""); @@ -190,7 +182,6 @@ bool FstReader::addTextures(const QFileInfo& texdir) { if (!compressFile(info.filePath(), _zipDir.path() + "/" + info.fileName())) { return false; } - _totalSize += info.size(); if (!addPart(_zipDir.path() + "/" + info.fileName(), QString("texture%1").arg(++_texturesCount))) { return false; @@ -218,6 +209,7 @@ bool FstReader::compressFile(const QString &inFileName, const QString &outFileNa QString("ModelUploader::compressFile()"), QString("Could not compress %1").arg(inFileName), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("Could not compress %1").arg(inFileName); return false; } } @@ -235,6 +227,7 @@ bool FstReader::addPart(const QString &path, const QString& name) { QString("ModelUploader::addPart()"), QString("Could not open %1").arg(path), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("Could not open %1").arg(path); return false; } @@ -247,6 +240,19 @@ bool FstReader::addPart(const QString &path, const QString& name) { _dataMultiPart->append(part); file->setParent(_dataMultiPart); + + qDebug() << "File " << QFileInfo(*file).fileName() << " added to model."; + _totalSize += file->size(); + if (_totalSize > MAX_SIZE) { + QMessageBox::warning(NULL, + QString("ModelUploader::zip()"), + QString("Model too big, over %1 Bytes.").arg(MAX_SIZE), + QMessageBox::Ok); + qDebug() << "[Warning] " << QString("Model too big, over %1 Bytes.").arg(MAX_SIZE); + return false; + } + qDebug() << "Current model size: " << _totalSize; + return true; } From d32797b54c468411aa74e0c2e830b3c47a67fdb0 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 26 Mar 2014 15:10:18 -0700 Subject: [PATCH 07/53] Added bunch of feedback --- libraries/shared/src/FstReader.cpp | 41 +++++++++++++++++------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/libraries/shared/src/FstReader.cpp b/libraries/shared/src/FstReader.cpp index 15e31cee07..205ab1794d 100644 --- a/libraries/shared/src/FstReader.cpp +++ b/libraries/shared/src/FstReader.cpp @@ -62,19 +62,19 @@ bool FstReader::zip() { QString("ModelUploader::zip()"), QString("Could not open FST file."), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("Could not open FST file."); return false; } + qDebug() << "Reading FST file : " << QFileInfo(fst).filePath(); // Compress and copy the fst if (!compressFile(QFileInfo(fst).filePath(), _zipDir.path() + "/" + QFileInfo(fst).fileName())) { return false; } - _totalSize += QFileInfo(fst).size(); if (!addPart(_zipDir.path() + "/" + QFileInfo(fst).fileName(), QString("fst"))) { return false; } - qDebug() << "Reading FST file : " << QFileInfo(fst).filePath(); // Let's read through the FST file QTextStream stream(&fst); @@ -85,14 +85,6 @@ bool FstReader::zip() { continue; } - if (_totalSize > MAX_SIZE) { - QMessageBox::warning(NULL, - QString("ModelUploader::zip()"), - QString("Model too big, over %1 Bytes.").arg(MAX_SIZE), - QMessageBox::Ok); - return false; - } - // according to what is read, we modify the command if (line[0] == NAME_FIELD) { QHttpPart textPart; @@ -100,56 +92,56 @@ bool FstReader::zip() { " name=\"model_name\""); textPart.setBody(line[1].toUtf8()); _dataMultiPart->append(textPart); - } else if (line[1] == FILENAME_FIELD) { + } else if (line[0] == FILENAME_FIELD) { QFileInfo fbx(QFileInfo(fst).path() + "/" + line[1]); if (!fbx.exists() || !fbx.isFile()) { // Check existence QMessageBox::warning(NULL, QString("ModelUploader::zip()"), QString("FBX file %1 could not be found.").arg(fbx.fileName()), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(fbx.fileName()); return false; } // Compress and copy if (!compressFile(fbx.filePath(), _zipDir.path() + "/" + line[1])) { return false; } - _totalSize += fbx.size(); if (!addPart(_zipDir.path() + "/" + line[1], "fbx")) { return false; } - } else if (line[1] == TEXDIR_FIELD) { // Check existence + } else if (line[0] == TEXDIR_FIELD) { // Check existence QFileInfo texdir(QFileInfo(fst).path() + "/" + line[1]); if (!texdir.exists() || !texdir.isDir()) { QMessageBox::warning(NULL, QString("ModelUploader::zip()"), QString("Texture directory could not be found."), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("Texture directory could not be found."); return false; } if (!addTextures(texdir)) { // Recursive compress and copy return false; } - } else if (line[1] == LOD_FIELD) { + } else if (line[0] == LOD_FIELD) { QFileInfo lod(QFileInfo(fst).path() + "/" + line[1]); if (!lod.exists() || !lod.isFile()) { // Check existence QMessageBox::warning(NULL, QString("ModelUploader::zip()"), QString("FBX file %1 could not be found.").arg(lod.fileName()), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(lod.fileName()); return false; } // Compress and copy if (!compressFile(lod.filePath(), _zipDir.path() + "/" + line[1])) { return false; } - _totalSize += lod.size(); if (!addPart(_zipDir.path() + "/" + line[1], QString("lod%1").arg(++_lodCount))) { return false; } } } - QHttpPart textPart; textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" " name=\"model_category\""); @@ -171,6 +163,7 @@ bool FstReader::send() { AccountManager::getInstance().authenticatedRequest(MODEL_URL, QNetworkAccessManager::PostOperation, JSONCallbackParameters(), QByteArray(), _dataMultiPart); _dataMultiPart = NULL; + qDebug() << "Model sent."; return true; } @@ -190,7 +183,6 @@ bool FstReader::addTextures(const QFileInfo& texdir) { if (!compressFile(info.filePath(), _zipDir.path() + "/" + info.fileName())) { return false; } - _totalSize += info.size(); if (!addPart(_zipDir.path() + "/" + info.fileName(), QString("texture%1").arg(++_texturesCount))) { return false; @@ -218,6 +210,7 @@ bool FstReader::compressFile(const QString &inFileName, const QString &outFileNa QString("ModelUploader::compressFile()"), QString("Could not compress %1").arg(inFileName), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("Could not compress %1").arg(inFileName); return false; } } @@ -235,6 +228,7 @@ bool FstReader::addPart(const QString &path, const QString& name) { QString("ModelUploader::addPart()"), QString("Could not open %1").arg(path), QMessageBox::Ok); + qDebug() << "[Warning] " << QString("Could not open %1").arg(path); return false; } @@ -247,6 +241,19 @@ bool FstReader::addPart(const QString &path, const QString& name) { _dataMultiPart->append(part); file->setParent(_dataMultiPart); + + qDebug() << "File " << QFileInfo(*file).fileName() << " added to model."; + _totalSize += file->size(); + if (_totalSize > MAX_SIZE) { + QMessageBox::warning(NULL, + QString("ModelUploader::zip()"), + QString("Model too big, over %1 Bytes.").arg(MAX_SIZE), + QMessageBox::Ok); + qDebug() << "[Warning] " << QString("Model too big, over %1 Bytes.").arg(MAX_SIZE); + return false; + } + qDebug() << "Current model size: " << _totalSize; + return true; } From 4c64c111444383aaa8d282fd7cb5a000430d27ad Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 26 Mar 2014 16:58:47 -0700 Subject: [PATCH 08/53] making some type casts abide by coding standard --- interface/src/avatar/Hand.cpp | 4 ++-- interface/src/avatar/SkeletonModel.cpp | 2 +- interface/src/renderer/FBXReader.cpp | 6 +++--- interface/src/renderer/GeometryCache.cpp | 6 +++--- interface/src/ui/BandwidthMeter.cpp | 8 ++++---- interface/src/ui/Oscilloscope.cpp | 4 ++-- libraries/metavoxels/src/MetavoxelData.cpp | 2 +- libraries/shared/src/qtimespan.cpp | 4 ++-- tests/physics/src/ShapeColliderTests.cpp | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 77586dd7ae..8c202ae1b1 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -167,7 +167,7 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { averagePenetration += collision->_penetration; averageContactPoint += collision->_contactPoint; } - averagePenetration /= float(handCollisions.size()); + averagePenetration /= (float)handCollisions.size(); if (isMyHand) { // our hand against other avatar // for now we resolve it to test shapes/collisions @@ -176,7 +176,7 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { } else { // someone else's hand against MyAvatar // TODO: submit collision info to MyAvatar which should lean accordingly - averageContactPoint /= float(handCollisions.size()); + averageContactPoint /= (float)handCollisions.size(); } } } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 9e4740df15..b0eac98913 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -129,7 +129,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin direction += fingerVector / length; } fingerVector = glm::inverse(palmRotation) * fingerVector * -sign; - IndexValue indexValue = { int(i), atan2f(fingerVector.z, fingerVector.x) }; + IndexValue indexValue = { (int)i, atan2f(fingerVector.z, fingerVector.x) }; fingerIndices.append(indexValue); } qSort(fingerIndices.begin(), fingerIndices.end()); diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 53f4e04b0b..c264fe70a2 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -1588,7 +1588,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) int numVertices = extracted.mesh.vertices.size(); jointShapeInfo.numVertices = numVertices; if (numVertices > 0) { - averageVertex /= float(jointShapeInfo.numVertices); + averageVertex /= (float)jointShapeInfo.numVertices; float averageRadius = 0.f; foreach (const glm::vec3& vertex, extracted.mesh.vertices) { averageRadius += glm::distance(vertex, averageVertex); @@ -1619,7 +1619,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else { // collide the joint like a sphere if (jointShapeInfo.numVertices > 0) { - jointShapeInfo.averageVertex /= float(jointShapeInfo.numVertices); + jointShapeInfo.averageVertex /= (float)jointShapeInfo.numVertices; joint.shapePosition = jointShapeInfo.averageVertex; } else { joint.shapePosition = glm::vec3(0.f); @@ -1629,7 +1629,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) && jointShapeInfo.numVertices > 0) { // the bone projection algorithm was not able to compute the joint radius // so we use an alternative measure - jointShapeInfo.averageRadius /= float(jointShapeInfo.numVertices); + jointShapeInfo.averageRadius /= (float)jointShapeInfo.numVertices; joint.boneRadius = jointShapeInfo.averageRadius; } } diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index c4a0d15baa..b835e91a12 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -31,11 +31,11 @@ void GeometryCache::renderHemisphere(int slices, int stacks) { GLfloat* vertexData = new GLfloat[vertices * 3]; GLfloat* vertex = vertexData; for (int i = 0; i < stacks - 1; i++) { - float phi = PI_OVER_TWO * float(i) / float(stacks - 1); + float phi = PI_OVER_TWO * (float)i / (float)(stacks - 1); float z = sinf(phi), radius = cosf(phi); for (int j = 0; j < slices; j++) { - float theta = TWO_PI * float(j) / float(slices); + float theta = TWO_PI * (float)j / (float)slices; *(vertex++) = sinf(theta) * radius; *(vertex++) = cosf(theta) * radius; @@ -181,7 +181,7 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) { float y = (float)i / (stacks - 1); for (int j = 0; j <= slices; j++) { - float theta = 3.f * PI_OVER_TWO + PI * float(j) / float(slices); + float theta = 3.f * PI_OVER_TWO + PI * (float)j / (float)slices; //normals *(vertex++) = sinf(theta); diff --git a/interface/src/ui/BandwidthMeter.cpp b/interface/src/ui/BandwidthMeter.cpp index dfc142fb95..64ff74b846 100644 --- a/interface/src/ui/BandwidthMeter.cpp +++ b/interface/src/ui/BandwidthMeter.cpp @@ -160,7 +160,7 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) { // Center of coordinate system -> upper left of bar glPushMatrix(); - glTranslatef(float(barX), float(y), 0.0f); + glTranslatef((float)barX, (float)y, 0.0f); // Render captions setColorRGBA(COLOR_TEXT); @@ -202,7 +202,7 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) { // Render scale indicators setColorRGBA(COLOR_INDICATOR); for (int j = NUMBER_OF_MARKERS; --j > 0;) { - renderVerticalLine(int(barWidth * j / NUMBER_OF_MARKERS), 0, h); + renderVerticalLine((barWidth * j) / NUMBER_OF_MARKERS, 0, h); } // Render bars @@ -210,8 +210,8 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) { for (size_t i = 0; i < N_CHANNELS; ++i) { ChannelIndex chIdx = ChannelIndex(i); - int wIn = int(barWidth * inputStream(chIdx).getValue() * UNIT_SCALE / scaleMax); - int wOut = int(barWidth * outputStream(chIdx).getValue() * UNIT_SCALE / scaleMax); + int wIn = (int)(barWidth * inputStream(chIdx).getValue() * UNIT_SCALE / scaleMax); + int wOut = (int)(barWidth * outputStream(chIdx).getValue() * UNIT_SCALE / scaleMax); setColorRGBA(channelInfo(chIdx).colorRGBA); diff --git a/interface/src/ui/Oscilloscope.cpp b/interface/src/ui/Oscilloscope.cpp index 90d2d49926..a47586b436 100644 --- a/interface/src/ui/Oscilloscope.cpp +++ b/interface/src/ui/Oscilloscope.cpp @@ -127,7 +127,7 @@ void Oscilloscope::render(int x, int y) { } // fetch low pass factor (and convert to fix point) / downsample factor - int lowPassFixPt = -int(std::numeric_limits::min()) * _lowPassCoeff; + int lowPassFixPt = -(int)(std::numeric_limits::min()) * _lowPassCoeff; unsigned downsample = _downsampleRatio; // keep half of the buffer for writing and ensure an even vertex count unsigned usedWidth = min(_width, MAX_SAMPLES_PER_CHANNEL / (downsample * 2)) & ~1u; @@ -141,7 +141,7 @@ void Oscilloscope::render(int x, int y) { short const* inPtr = _samples + _writePos[ch]; short* outPtr = _vertices + MAX_COORDS_PER_CHANNEL * ch; int sample = 0, x = usedWidth; - for (int i = int(usedSamples); --i >= 0 ;) { + for (int i = (int)usedSamples; --i >= 0 ;) { if (inPtr == basePtr) { // handle boundary, reading the circular sample buffer inPtr = endPtr; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 397e10d45f..cab5a73076 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -1096,7 +1096,7 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin QScriptValue minimum = infoValue.property(guide->_minimumHandle); MetavoxelInfo info = { glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()), - float(infoValue.property(guide->_sizeHandle).toNumber()), guide->_visitation->info.inputValues, + (float)infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.inputValues, guide->_visitation->info.outputValues, infoValue.property(guide->_isLeafHandle).toBool() }; // extract and convert the values provided by the script diff --git a/libraries/shared/src/qtimespan.cpp b/libraries/shared/src/qtimespan.cpp index 2ddddb30f3..f3482cfb14 100644 --- a/libraries/shared/src/qtimespan.cpp +++ b/libraries/shared/src/qtimespan.cpp @@ -1594,7 +1594,7 @@ void QTimeSpan::setFromMonths(qreal months) { Q_ASSERT_X(hasValidReference(), "setFromMonths", "Can not set interval from time unit month if there is no reference date."); - int fullMonths = int(months); + int fullMonths = (int)months; qreal fractionalMonth = months - fullMonths; QDateTime endDate = d->reference; @@ -1631,7 +1631,7 @@ void QTimeSpan::setFromYears(qreal years) { Q_ASSERT_X(hasValidReference(), "setFromYears", "Can not set interval from time unit year if there is no reference date."); - int fullYears = int(years); + int fullYears = (int)years; qreal fractionalYear = years - fullYears; QDateTime endDate = d->reference; diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 6d7e9a6db1..f19f6d6f95 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -189,7 +189,7 @@ void ShapeColliderTests::sphereMissesCapsule() { float delta = 1.3f * (totalRadius + halfHeightB) / (numberOfSteps - 1); for (int i = 0; i < numberOfSteps; ++i) { // translate sphereA into world-frame - glm::vec3 localPosition = localStartPosition + (float(i) * delta) * yAxis; + glm::vec3 localPosition = localStartPosition + ((float)i * delta) * yAxis; sphereA.setPosition(rotation * localPosition + translation); // sphereA agains capsuleB From 0b13a7173cb065f9c8fbe897ea0cb66ed0215859 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 26 Mar 2014 23:24:36 -0700 Subject: [PATCH 09/53] disable wheel for size scaling --- examples/editVoxels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 5e4b77a10f..4922caf0d8 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -1326,7 +1326,7 @@ function wheelEvent(event) { } } -Controller.wheelEvent.connect(wheelEvent); +// Controller.wheelEvent.connect(wheelEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mouseMoveEvent.connect(mouseMoveEvent); From b6f2150d2e7a4bc02b3fc79dca875d02d2deb54e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 27 Mar 2014 10:37:07 -0700 Subject: [PATCH 10/53] HeadData::_yaw and friends change to _baseYaw --- interface/src/avatar/Head.cpp | 10 +++++----- interface/src/avatar/MyAvatar.cpp | 22 +++++++++++----------- libraries/avatars/src/AvatarData.cpp | 6 +++--- libraries/avatars/src/AvatarData.h | 4 ++-- libraries/avatars/src/HeadData.cpp | 20 ++++++++++---------- libraries/avatars/src/HeadData.h | 24 ++++++++++++------------ 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index fc18eed885..0653a702cb 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -50,7 +50,7 @@ void Head::init() { } void Head::reset() { - _yaw = _pitch = _roll = 0.0f; + _baseYaw = _basePitch = _baseRoll = 0.0f; _leanForward = _leanSideways = 0.0f; _faceModel.reset(); } @@ -185,7 +185,7 @@ glm::quat Head::getTweakedOrientation() const { glm::quat Head::getCameraOrientation () const { Avatar* owningAvatar = static_cast(_owningAvatar); - return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_pitch, 0.f, 0.0f))); + return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.f, 0.0f))); } glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { @@ -198,15 +198,15 @@ glm::vec3 Head::getScalePivot() const { } float Head::getTweakedYaw() const { - return glm::clamp(_yaw + _yawTweak, MIN_HEAD_YAW, MAX_HEAD_YAW); + return glm::clamp(_baseYaw + _yawTweak, MIN_HEAD_YAW, MAX_HEAD_YAW); } float Head::getTweakedPitch() const { - return glm::clamp(_pitch + _pitchTweak, MIN_HEAD_PITCH, MAX_HEAD_PITCH); + return glm::clamp(_basePitch + _pitchTweak, MIN_HEAD_PITCH, MAX_HEAD_PITCH); } float Head::getTweakedRoll() const { - return glm::clamp(_roll + _rollTweak, MIN_HEAD_ROLL, MAX_HEAD_ROLL); + return glm::clamp(_baseRoll + _rollTweak, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 618cda1199..5c4dbd88a4 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -117,9 +117,9 @@ void MyAvatar::update(float deltaTime) { OculusManager::getEulerAngles(yaw, pitch, roll); // but these euler angles are stored in degrees - head->setYaw(yaw * DEGREES_PER_RADIAN); - head->setPitch(pitch * DEGREES_PER_RADIAN); - head->setRoll(roll * DEGREES_PER_RADIAN); + head->setBaseYaw(yaw * DEGREES_PER_RADIAN); + head->setBasePitch(pitch * DEGREES_PER_RADIAN); + head->setBaseRoll(roll * DEGREES_PER_RADIAN); } // Get audio loudness data from audio input device @@ -229,7 +229,7 @@ void MyAvatar::simulate(float deltaTime) { if (!Application::getInstance()->getFaceshift()->isActive() && OculusManager::isConnected() && fabsf(forwardAcceleration) > OCULUS_ACCELERATION_PULL_THRESHOLD && - fabs(getHead()->getYaw()) > OCULUS_YAW_OFFSET_THRESHOLD) { + fabs(getHead()->getBaseYaw()) > OCULUS_YAW_OFFSET_THRESHOLD) { // if we're wearing the oculus // and this acceleration is above the pull threshold @@ -239,7 +239,7 @@ void MyAvatar::simulate(float deltaTime) { _bodyYaw = getAbsoluteHeadYaw(); // set the head yaw to zero for this draw - getHead()->setYaw(0); + getHead()->setBaseYaw(0); // correct the oculus yaw offset OculusManager::updateYawOffset(); @@ -505,7 +505,7 @@ void MyAvatar::saveData(QSettings* settings) { settings->setValue("bodyPitch", _bodyPitch); settings->setValue("bodyRoll", _bodyRoll); - settings->setValue("headPitch", getHead()->getPitch()); + settings->setValue("headPitch", getHead()->getBasePitch()); settings->setValue("position_x", _position.x); settings->setValue("position_y", _position.y); @@ -531,7 +531,7 @@ void MyAvatar::loadData(QSettings* settings) { _bodyPitch = loadSetting(settings, "bodyPitch", 0.0f); _bodyRoll = loadSetting(settings, "bodyRoll", 0.0f); - getHead()->setPitch(loadSetting(settings, "headPitch", 0.0f)); + getHead()->setBasePitch(loadSetting(settings, "headPitch", 0.0f)); _position.x = loadSetting(settings, "position_x", 0.0f); _position.y = loadSetting(settings, "position_y", 0.0f); @@ -574,9 +574,9 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) { setOrientation(orientation); // then vertically - float oldPitch = getHead()->getPitch(); - getHead()->setPitch(oldPitch - deltaY * ANGULAR_SCALE); - rotation = glm::angleAxis(glm::radians((getHead()->getPitch() - oldPitch)), orientation * IDENTITY_RIGHT); + float oldPitch = getHead()->getBasePitch(); + getHead()->setBasePitch(oldPitch - deltaY * ANGULAR_SCALE); + rotation = glm::angleAxis(glm::radians((getHead()->getBasePitch() - oldPitch)), orientation * IDENTITY_RIGHT); setPosition(position + rotation * (getPosition() - position)); } @@ -682,7 +682,7 @@ void MyAvatar::updateThrust(float deltaTime) { _thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up; _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime; - getHead()->setPitch(getHead()->getPitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); + getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); // If thrust keys are being held down, slowly increase thrust to allow reaching great speeds if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index bb0fcd27e6..0899d67cb1 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -288,9 +288,9 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) { } return maxAvailableSize; } - _headData->setYaw(headYaw); - _headData->setPitch(headPitch); - _headData->setRoll(headRoll); + _headData->setBaseYaw(headYaw); + _headData->setBasePitch(headPitch); + _headData->setBaseRoll(headRoll); } // 6 bytes // Head lean (relative to pelvis) diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a89639d68d..2ea20c1041 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -128,8 +128,8 @@ public: void setHeadOrientation(const glm::quat& orientation) { _headData->setOrientation(orientation); } // access to Head().set/getMousePitch (degrees) - float getHeadPitch() const { return _headData->getPitch(); } - void setHeadPitch(float value) { _headData->setPitch(value); }; + float getHeadPitch() const { return _headData->getBasePitch(); } + void setHeadPitch(float value) { _headData->setBasePitch(value); }; // access to Head().set/getAverageLoudness float getAudioLoudness() const { return _headData->getAudioLoudness(); } diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index cf48aeabfa..da1bdca23b 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -14,9 +14,9 @@ #include "HeadData.h" HeadData::HeadData(AvatarData* owningAvatar) : - _yaw(0.0f), - _pitch(0.0f), - _roll(0.0f), + _baseYaw(0.0f), + _basePitch(0.0f), + _baseRoll(0.0f), _leanSideways(0.0f), _leanForward(0.0f), _lookAtPosition(0.0f, 0.0f, 0.0f), @@ -32,7 +32,7 @@ HeadData::HeadData(AvatarData* owningAvatar) : } glm::quat HeadData::getOrientation() const { - return _owningAvatar->getOrientation() * glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll))); + return _owningAvatar->getOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, _baseYaw, _baseRoll))); } void HeadData::setOrientation(const glm::quat& orientation) { @@ -44,21 +44,21 @@ void HeadData::setOrientation(const glm::quat& orientation) { // the rest goes to the head glm::vec3 eulers = glm::degrees(safeEulerAngles(glm::inverse(bodyOrientation) * orientation)); - _pitch = eulers.x; - _yaw = eulers.y; - _roll = eulers.z; + _basePitch = eulers.x; + _baseYaw = eulers.y; + _baseRoll = eulers.z; } void HeadData::addYaw(float yaw) { - setYaw(_yaw + yaw); + setBaseYaw(_baseYaw + yaw); } void HeadData::addPitch(float pitch) { - setPitch(_pitch + pitch); + setBasePitch(_basePitch + pitch); } void HeadData::addRoll(float roll) { - setRoll(_roll + roll); + setBaseRoll(_baseRoll + roll); } diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index b199ff19d2..8016d8b674 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -36,15 +36,15 @@ public: void setLeanSideways(float leanSideways) { _leanSideways = leanSideways; } float getLeanForward() const { return _leanForward; } void setLeanForward(float leanForward) { _leanForward = leanForward; } - float getYaw() const { return _yaw; } - void setYaw(float yaw) { _yaw = glm::clamp(yaw, MIN_HEAD_YAW, MAX_HEAD_YAW); } - float getPitch() const { return _pitch; } - void setPitch(float pitch) { _pitch = glm::clamp(pitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH); } - float getRoll() const { return _roll; } - void setRoll(float roll) { _roll = glm::clamp(roll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } - virtual float getTweakedYaw() const { return _yaw; } - virtual float getTweakedPitch() const { return _pitch; } - virtual float getTweakedRoll() const { return _roll; } + float getBaseYaw() const { return _baseYaw; } + void setBaseYaw(float yaw) { _baseYaw = glm::clamp(yaw, MIN_HEAD_YAW, MAX_HEAD_YAW); } + float getBasePitch() const { return _basePitch; } + void setBasePitch(float pitch) { _basePitch = glm::clamp(pitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH); } + float getBaseRoll() const { return _baseRoll; } + void setBaseRoll(float roll) { _baseRoll = glm::clamp(roll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } + virtual float getTweakedYaw() const { return _baseYaw; } + virtual float getTweakedPitch() const { return _basePitch; } + virtual float getTweakedRoll() const { return _baseRoll; } glm::quat getOrientation() const; void setOrientation(const glm::quat& orientation); @@ -73,9 +73,9 @@ public: protected: // degrees - float _yaw; - float _pitch; - float _roll; + float _baseYaw; + float _basePitch; + float _baseRoll; float _leanSideways; float _leanForward; From c354e980a804c174fcfdd0c4b46259391a2bf98e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 27 Mar 2014 11:03:42 -0700 Subject: [PATCH 11/53] getTweakedOrientation() to getFinalOrientation() --- interface/src/Audio.cpp | 2 +- interface/src/avatar/FaceModel.cpp | 2 +- interface/src/avatar/Head.cpp | 2 +- interface/src/avatar/Head.h | 6 +++++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 67f2e2caec..2f0b3df277 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -522,7 +522,7 @@ void Audio::handleAudioInput() { if (audioMixer && audioMixer->getActiveSocket()) { MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar(); glm::vec3 headPosition = interfaceAvatar->getHead()->getPosition(); - glm::quat headOrientation = interfaceAvatar->getHead()->getTweakedOrientation(); + glm::quat headOrientation = interfaceAvatar->getHead()->getFinalOrientation(); // we need the amount of bytes in the buffer + 1 for type // + 12 for 3 floats for position + float for bearing + 1 attenuation byte diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 19faa0da42..b7eba1e98e 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -60,7 +60,7 @@ void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJ // likewise with the eye joints glm::mat4 inverse = glm::inverse(parentState.transform * glm::translate(state.translation) * joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); - glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getTweakedOrientation() * IDENTITY_FRONT, 0.0f)); + glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientation() * IDENTITY_FRONT, 0.0f)); glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() + _owningHead->getSaccade() - _translation, 1.0f)); glm::quat between = rotationBetween(front, lookAt); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 0653a702cb..c72e53b1a0 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -178,7 +178,7 @@ void Head::setScale (float scale) { _scale = scale; } -glm::quat Head::getTweakedOrientation() const { +glm::quat Head::getFinalOrientation() const { return _owningAvatar->getOrientation() * glm::quat(glm::radians( glm::vec3(getTweakedPitch(), getTweakedYaw(), getTweakedRoll() ))); } diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 733323abc5..311d5e887a 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -45,8 +45,12 @@ public: void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; } - glm::quat getTweakedOrientation() const; + /// \return orientationBody * orientationBase+Delta + glm::quat getFinalOrientation() const; + + /// \return orientationBody * orientationBasePitch glm::quat getCameraOrientation () const; + const glm::vec3& getAngularVelocity() const { return _angularVelocity; } void setAngularVelocity(glm::vec3 angularVelocity) { _angularVelocity = angularVelocity; } From 7949be0319f991c45c51c9b2a7c3b402b43a0fcf Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 27 Mar 2014 11:14:26 -0700 Subject: [PATCH 12/53] PitchTweak and friends become DeltaPitch --- interface/src/avatar/FaceModel.cpp | 6 +++--- interface/src/avatar/Head.cpp | 20 ++++++++++---------- interface/src/avatar/Head.h | 24 ++++++++++++------------ interface/src/avatar/MyAvatar.cpp | 12 ++++++------ libraries/avatars/src/AvatarData.cpp | 6 +++--- libraries/avatars/src/HeadData.h | 6 +++--- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index b7eba1e98e..19120d10be 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -50,9 +50,9 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX glm::mat3 axes = glm::mat3_cast(_rotation); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) * joint.preTransform * glm::mat4_cast(joint.preRotation))); - state.rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getTweakedRoll(), glm::normalize(inverse * axes[2])) - * glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getTweakedYaw(), glm::normalize(inverse * axes[1])) - * glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getTweakedPitch(), glm::normalize(inverse * axes[0])) + state.rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2])) + * glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getFinalYaw(), glm::normalize(inverse * axes[1])) + * glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalPitch(), glm::normalize(inverse * axes[0])) * joint.rotation; } diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index c72e53b1a0..d432830252 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -36,9 +36,9 @@ Head::Head(Avatar* owningAvatar) : _leftEyeBlinkVelocity(0.0f), _rightEyeBlinkVelocity(0.0f), _timeWithoutTalking(0.0f), - _pitchTweak(0.f), - _yawTweak(0.f), - _rollTweak(0.f), + _deltaPitch(0.f), + _deltaYaw(0.f), + _deltaRoll(0.f), _isCameraMoving(false), _faceModel(this) { @@ -180,7 +180,7 @@ void Head::setScale (float scale) { glm::quat Head::getFinalOrientation() const { return _owningAvatar->getOrientation() * glm::quat(glm::radians( - glm::vec3(getTweakedPitch(), getTweakedYaw(), getTweakedRoll() ))); + glm::vec3(getFinalPitch(), getFinalYaw(), getFinalRoll() ))); } glm::quat Head::getCameraOrientation () const { @@ -197,16 +197,16 @@ glm::vec3 Head::getScalePivot() const { return _faceModel.isActive() ? _faceModel.getTranslation() : _position; } -float Head::getTweakedYaw() const { - return glm::clamp(_baseYaw + _yawTweak, MIN_HEAD_YAW, MAX_HEAD_YAW); +float Head::getFinalYaw() const { + return glm::clamp(_baseYaw + _deltaYaw, MIN_HEAD_YAW, MAX_HEAD_YAW); } -float Head::getTweakedPitch() const { - return glm::clamp(_basePitch + _pitchTweak, MIN_HEAD_PITCH, MAX_HEAD_PITCH); +float Head::getFinalPitch() const { + return glm::clamp(_basePitch + _deltaPitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH); } -float Head::getTweakedRoll() const { - return glm::clamp(_baseRoll + _rollTweak, MIN_HEAD_ROLL, MAX_HEAD_ROLL); +float Head::getFinalRoll() const { + return glm::clamp(_baseRoll + _deltaRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 311d5e887a..915cf1e268 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -74,18 +74,18 @@ public: /// Returns the point about which scaling occurs. glm::vec3 getScalePivot() const; - void setPitchTweak(float pitch) { _pitchTweak = pitch; } - float getPitchTweak() const { return _pitchTweak; } + void setDeltaPitch(float pitch) { _deltaPitch = pitch; } + float getDeltaPitch() const { return _deltaPitch; } - void setYawTweak(float yaw) { _yawTweak = yaw; } - float getYawTweak() const { return _yawTweak; } + void setDeltaYaw(float yaw) { _deltaYaw = yaw; } + float getDeltaYaw() const { return _deltaYaw; } - void setRollTweak(float roll) { _rollTweak = roll; } - float getRollTweak() const { return _rollTweak; } + void setDeltaRoll(float roll) { _deltaRoll = roll; } + float getDeltaRoll() const { return _deltaRoll; } - virtual float getTweakedPitch() const; - virtual float getTweakedYaw() const; - virtual float getTweakedRoll() const; + virtual float getFinalPitch() const; + virtual float getFinalYaw() const; + virtual float getFinalRoll() const; private: // disallow copies of the Head, copy of owning Avatar is disallowed too @@ -111,9 +111,9 @@ private: float _timeWithoutTalking; // tweaked angles affect the rendered head, but not the camera - float _pitchTweak; - float _yawTweak; - float _rollTweak; + float _deltaPitch; + float _deltaYaw; + float _deltaRoll; bool _isCameraMoving; FaceModel _faceModel; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5c4dbd88a4..c1c8a9a616 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -364,9 +364,9 @@ void MyAvatar::updateFromGyros(float deltaTime) { // restore rotation, lean to neutral positions const float RESTORE_PERIOD = 0.25f; // seconds float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f); - head->setPitchTweak(glm::mix(head->getPitchTweak(), 0.0f, restorePercentage)); - head->setYawTweak(glm::mix(head->getYawTweak(), 0.0f, restorePercentage)); - head->setRollTweak(glm::mix(head->getRollTweak(), 0.0f, restorePercentage)); + head->setDeltaPitch(glm::mix(head->getDeltaPitch(), 0.0f, restorePercentage)); + head->setDeltaYaw(glm::mix(head->getDeltaYaw(), 0.0f, restorePercentage)); + head->setDeltaRoll(glm::mix(head->getDeltaRoll(), 0.0f, restorePercentage)); head->setLeanSideways(glm::mix(head->getLeanSideways(), 0.0f, restorePercentage)); head->setLeanForward(glm::mix(head->getLeanForward(), 0.0f, restorePercentage)); return; @@ -377,9 +377,9 @@ void MyAvatar::updateFromGyros(float deltaTime) { const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f; const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f; const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f; - head->setPitchTweak(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY); - head->setYawTweak(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY); - head->setRollTweak(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY); + head->setDeltaPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY); + head->setDeltaYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY); + head->setDeltaRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY); // Update torso lean distance based on accelerometer data const float TORSO_LENGTH = 0.5f; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 0899d67cb1..31639b6836 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -93,9 +93,9 @@ QByteArray AvatarData::toByteArray() { destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _targetScale); // Head rotation (NOTE: This needs to become a quaternion to save two bytes) - destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedYaw()); - destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedPitch()); - destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedRoll()); + destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getFinalYaw()); + destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getFinalPitch()); + destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getFinalRoll()); // Head lean X,Z (head lateral and fwd/back motion relative to torso) memcpy(destinationBuffer, &_headData->_leanSideways, sizeof(_headData->_leanSideways)); diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index 8016d8b674..a907c5b694 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -42,9 +42,9 @@ public: void setBasePitch(float pitch) { _basePitch = glm::clamp(pitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH); } float getBaseRoll() const { return _baseRoll; } void setBaseRoll(float roll) { _baseRoll = glm::clamp(roll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } - virtual float getTweakedYaw() const { return _baseYaw; } - virtual float getTweakedPitch() const { return _basePitch; } - virtual float getTweakedRoll() const { return _baseRoll; } + virtual float getFinalYaw() const { return _baseYaw; } + virtual float getFinalPitch() const { return _basePitch; } + virtual float getFinalRoll() const { return _baseRoll; } glm::quat getOrientation() const; void setOrientation(const glm::quat& orientation); From 88e7b8e68f636fb0f56f69397288f7d11310865a Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 27 Mar 2014 11:16:52 -0700 Subject: [PATCH 13/53] Fixed "memory leak" in Account Manager. --- libraries/shared/src/AccountManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/shared/src/AccountManager.cpp b/libraries/shared/src/AccountManager.cpp index bb32896ca0..2fe5007ad1 100644 --- a/libraries/shared/src/AccountManager.cpp +++ b/libraries/shared/src/AccountManager.cpp @@ -148,6 +148,7 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager:: if (dataMultiPart) { if (operation == QNetworkAccessManager::PostOperation) { networkReply = _networkAccessManager->post(authenticatedRequest, dataMultiPart); + dataMultiPart->setParent(networkReply); } else { networkReply = _networkAccessManager->put(authenticatedRequest, dataMultiPart); } @@ -199,6 +200,7 @@ void AccountManager::passSuccessToCallback() { qDebug() << jsonResponse; } } + delete requestReply; } void AccountManager::passErrorToCallback(QNetworkReply::NetworkError errorCode) { @@ -219,6 +221,7 @@ void AccountManager::passErrorToCallback(QNetworkReply::NetworkError errorCode) qDebug() << "Error" << errorCode << "-" << requestReply->errorString(); } } + delete requestReply; } bool AccountManager::hasValidAccessToken() { From d9c48d63fe5dc8e143ac9f95d7bf43bb9fbc4f11 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 27 Mar 2014 11:18:32 -0700 Subject: [PATCH 14/53] Made _zipDir a children of _dataMultiPart so it gets deleted at the right time. --- libraries/shared/src/FstReader.cpp | 33 +++++++++++++++++++++--------- libraries/shared/src/FstReader.h | 7 +++---- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/libraries/shared/src/FstReader.cpp b/libraries/shared/src/FstReader.cpp index 205ab1794d..14cff957b1 100644 --- a/libraries/shared/src/FstReader.cpp +++ b/libraries/shared/src/FstReader.cpp @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include "AccountManager.h" - #include "FstReader.h" @@ -30,7 +30,16 @@ static const QString MODEL_URL = "/api/v1/models"; static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB +// Class providing the QObject parent system to QTemporaryDir +class TemporaryDir : public QTemporaryDir, public QObject { +public: + virtual ~TemporaryDir() { + // ensuring the entire object gets deleted by the QObject parent. + } +}; + FstReader::FstReader(bool isHead) : + _zipDir(new TemporaryDir()), _lodCount(-1), _texturesCount(-1), _totalSize(0), @@ -38,6 +47,8 @@ FstReader::FstReader(bool isHead) : _readyToSend(false), _dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)) { + _zipDir->setParent(_dataMultiPart); + } FstReader::~FstReader() { @@ -68,10 +79,10 @@ bool FstReader::zip() { qDebug() << "Reading FST file : " << QFileInfo(fst).filePath(); // Compress and copy the fst - if (!compressFile(QFileInfo(fst).filePath(), _zipDir.path() + "/" + QFileInfo(fst).fileName())) { + if (!compressFile(QFileInfo(fst).filePath(), _zipDir->path() + "/" + QFileInfo(fst).fileName())) { return false; } - if (!addPart(_zipDir.path() + "/" + QFileInfo(fst).fileName(), + if (!addPart(_zipDir->path() + "/" + QFileInfo(fst).fileName(), QString("fst"))) { return false; } @@ -103,10 +114,10 @@ bool FstReader::zip() { return false; } // Compress and copy - if (!compressFile(fbx.filePath(), _zipDir.path() + "/" + line[1])) { + if (!compressFile(fbx.filePath(), _zipDir->path() + "/" + line[1])) { return false; } - if (!addPart(_zipDir.path() + "/" + line[1], "fbx")) { + if (!addPart(_zipDir->path() + "/" + line[1], "fbx")) { return false; } } else if (line[0] == TEXDIR_FIELD) { // Check existence @@ -133,10 +144,10 @@ bool FstReader::zip() { return false; } // Compress and copy - if (!compressFile(lod.filePath(), _zipDir.path() + "/" + line[1])) { + if (!compressFile(lod.filePath(), _zipDir->path() + "/" + line[1])) { return false; } - if (!addPart(_zipDir.path() + "/" + line[1], QString("lod%1").arg(++_lodCount))) { + if (!addPart(_zipDir->path() + "/" + line[1], QString("lod%1").arg(++_lodCount))) { return false; } } @@ -162,6 +173,7 @@ bool FstReader::send() { } AccountManager::getInstance().authenticatedRequest(MODEL_URL, QNetworkAccessManager::PostOperation, JSONCallbackParameters(), QByteArray(), _dataMultiPart); + _zipDir = NULL; _dataMultiPart = NULL; qDebug() << "Model sent."; @@ -180,10 +192,10 @@ bool FstReader::addTextures(const QFileInfo& texdir) { foreach (QFileInfo info, list) { if (info.isFile()) { // Compress and copy - if (!compressFile(info.filePath(), _zipDir.path() + "/" + info.fileName())) { + if (!compressFile(info.filePath(), _zipDir->path() + "/" + info.fileName())) { return false; } - if (!addPart(_zipDir.path() + "/" + info.fileName(), + if (!addPart(_zipDir->path() + "/" + info.fileName(), QString("texture%1").arg(++_texturesCount))) { return false; } @@ -204,7 +216,7 @@ bool FstReader::compressFile(const QString &inFileName, const QString &outFileNa QFile outFile(outFileName); if (!outFile.open(QIODevice::WriteOnly)) { - QDir(_zipDir.path()).mkpath(QFileInfo(outFileName).path()); + QDir(_zipDir->path()).mkpath(QFileInfo(outFileName).path()); if (!outFile.open(QIODevice::WriteOnly)) { QMessageBox::warning(NULL, QString("ModelUploader::compressFile()"), @@ -229,6 +241,7 @@ bool FstReader::addPart(const QString &path, const QString& name) { QString("Could not open %1").arg(path), QMessageBox::Ok); qDebug() << "[Warning] " << QString("Could not open %1").arg(path); + delete file; return false; } diff --git a/libraries/shared/src/FstReader.h b/libraries/shared/src/FstReader.h index d06742810b..6d1cac01c4 100644 --- a/libraries/shared/src/FstReader.h +++ b/libraries/shared/src/FstReader.h @@ -10,11 +10,10 @@ #ifndef __hifi__FstReader__ #define __hifi__FstReader__ -#include - +class TemporaryDir; class QHttpMultiPart; -class FstReader { +class FstReader : public QObject { public: FstReader(bool isHead); ~FstReader(); @@ -23,7 +22,7 @@ public: bool send(); private: - QTemporaryDir _zipDir; + TemporaryDir* _zipDir; int _lodCount; int _texturesCount; int _totalSize; From a978f533e11f1dcf54f354c34f05f14ae8166206 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 27 Mar 2014 11:24:03 -0700 Subject: [PATCH 15/53] update a comment --- interface/src/avatar/Head.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 915cf1e268..dc96aa318f 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -110,7 +110,7 @@ private: float _rightEyeBlinkVelocity; float _timeWithoutTalking; - // tweaked angles affect the rendered head, but not the camera + // delta angles for local head rotation float _deltaPitch; float _deltaYaw; float _deltaRoll; From 21eef6c15ee86dd6ce33f96308487724ddfab0c4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Mar 2014 13:10:03 -0700 Subject: [PATCH 16/53] make sure VoxelServer and ParticleServer are in Agent interest list --- assignment-client/src/Agent.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 25dcfcdd95..b5199e87e8 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -138,7 +138,11 @@ void Agent::run() { ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); NodeList* nodeList = NodeList::getInstance(); - nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer); + nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() + << NodeType::AudioMixer + << NodeType::AvatarMixer + << NodeType::VoxelServer + << NodeType::ParticleServer); // figure out the URL for the script for this agent assignment QString scriptURLString("http://%1:8080/assignment/%2"); From 7cc72307456b15e06691b4132be05e441a2a2058 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 13:18:45 -0700 Subject: [PATCH 17/53] Adjust the shadow shader to prevent seeing shadows on the opposite sides of objects. Closes #2544. --- interface/resources/shaders/shadow_map.frag | 5 ++-- interface/resources/shaders/shadow_map.vert | 30 +++++++++++++++++++++ interface/src/voxels/VoxelSystem.cpp | 15 ++++------- 3 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 interface/resources/shaders/shadow_map.vert diff --git a/interface/resources/shaders/shadow_map.frag b/interface/resources/shaders/shadow_map.frag index 4bd8b8b768..810fc6a947 100644 --- a/interface/resources/shaders/shadow_map.frag +++ b/interface/resources/shaders/shadow_map.frag @@ -10,7 +10,8 @@ uniform sampler2DShadow shadowMap; +varying vec4 shadowColor; + void main(void) { - gl_FragColor = gl_Color * mix(vec4(0.8, 0.8, 0.8, 1.0), vec4(1.0, 1.0, 1.0, 1.0), - shadow2D(shadowMap, gl_TexCoord[0].stp)); + gl_FragColor = gl_Color * mix(shadowColor, vec4(1.0, 1.0, 1.0, 1.0), shadow2D(shadowMap, gl_TexCoord[0].stp)); } diff --git a/interface/resources/shaders/shadow_map.vert b/interface/resources/shaders/shadow_map.vert new file mode 100644 index 0000000000..a15cd070b9 --- /dev/null +++ b/interface/resources/shaders/shadow_map.vert @@ -0,0 +1,30 @@ +#version 120 + +// +// shadow_map.vert +// vertex shader +// +// Created by Andrzej Kapolka on 3/27/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +varying vec4 shadowColor; + +void main(void) { + // the shadow color depends on the light product + vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); + float lightProduct = dot(normal, gl_LightSource[0].position); + shadowColor = mix(vec4(1.0, 1.0, 1.0, 1.0), vec4(0.8, 0.8, 0.8, 1.0), step(0.0, lightProduct)); + + // standard diffuse lighting + gl_FrontColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient + + gl_LightSource[0].diffuse * max(0.0, lightProduct)); + + // generate the shadow texture coordinate using the eye position + vec4 eyePosition = gl_ModelViewMatrix * gl_Vertex; + gl_TexCoord[0] = vec4(dot(gl_EyePlaneS[0], eyePosition), dot(gl_EyePlaneT[0], eyePosition), + dot(gl_EyePlaneR[0], eyePosition), 1.0); + + // use the fixed function transform + gl_Position = ftransform(); +} diff --git a/interface/src/voxels/VoxelSystem.cpp b/interface/src/voxels/VoxelSystem.cpp index 4db5af3c04..5c68485436 100644 --- a/interface/src/voxels/VoxelSystem.cpp +++ b/interface/src/voxels/VoxelSystem.cpp @@ -506,8 +506,10 @@ void VoxelSystem::initVoxelMemory() { _perlinModulateProgram.setUniformValue("permutationNormalTexture", 0); _perlinModulateProgram.release(); - _shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() - + "shaders/shadow_map.frag"); + _shadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/shadow_map.vert"); + _shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/shadow_map.frag"); _shadowMapProgram.link(); _shadowMapProgram.bind(); @@ -1471,10 +1473,6 @@ void VoxelSystem::applyScaleAndBindProgram(bool texture) { if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { _shadowMapProgram.bind(); glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID()); - glEnable(GL_TEXTURE_GEN_S); - glEnable(GL_TEXTURE_GEN_T); - glEnable(GL_TEXTURE_GEN_R); - glEnable(GL_TEXTURE_2D); glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]); glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]); @@ -1496,10 +1494,7 @@ void VoxelSystem::removeScaleAndReleaseProgram(bool texture) { if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { _shadowMapProgram.release(); glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_GEN_S); - glDisable(GL_TEXTURE_GEN_T); - glDisable(GL_TEXTURE_GEN_R); - glDisable(GL_TEXTURE_2D); + } else if (texture) { _perlinModulateProgram.release(); From bda96ef935f00abc36ccbb89e3f0a134be61805c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 13:35:22 -0700 Subject: [PATCH 18/53] added back use of nodeWithUUID() instead of sharedpointer --- .../src/octree/OctreeSendThread.cpp | 25 ++++++++-- .../src/octree/OctreeSendThread.h | 3 +- assignment-client/src/octree/OctreeServer.cpp | 46 +++++++++++++------ assignment-client/src/octree/OctreeServer.h | 4 ++ libraries/shared/src/NodeList.cpp | 18 ++++++-- libraries/shared/src/NodeList.h | 2 +- 6 files changed, 73 insertions(+), 25 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 382d8aa528..023f3f3d2c 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -21,8 +21,9 @@ quint64 endSceneSleepTime = 0; OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer node) : _myServer(myServer), - _node(node), + _nodeUUID(node->getUUID()), _packetData(), + _nodeMissingCount(0), _processLock(), _isShuttingDown(false) { @@ -43,7 +44,6 @@ OctreeSendThread::~OctreeSendThread() { } qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected " "- ending sending thread [" << this << "]"; - _node.clear(); OctreeServer::clientDisconnected(); } @@ -72,13 +72,28 @@ bool OctreeSendThread::process() { // don't do any send processing until the initial load of the octree is complete... if (_myServer->isInitialLoadComplete()) { - if (!_node.isNull()) { - OctreeQueryNode* nodeData = static_cast(_node->getLinkedData()); + SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID, false); + if (node) { + _nodeMissingCount = 0; + OctreeQueryNode* nodeData = static_cast(node->getLinkedData()); // Sometimes the node data has not yet been linked, in which case we can't really do anything if (nodeData && !nodeData->isShuttingDown()) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - packetDistributor(_node, nodeData, viewFrustumChanged); + packetDistributor(node, nodeData, viewFrustumChanged); + } + } else { + _nodeMissingCount++; + const int MANY_FAILED_LOCKS = 1; + if (_nodeMissingCount >= MANY_FAILED_LOCKS) { + + QString safeServerName("Octree"); + if (_myServer) { + safeServerName = _myServer->getMyServerName(); + } + + qDebug() << qPrintable(safeServerName) << "server: sending thread [" << this << "]" + << "failed to get nodeWithUUID() " << _nodeUUID <<". Failed:" << _nodeMissingCount << "times"; } } } diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 4e18ee9b2a..4b1b6d8c92 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -38,14 +38,15 @@ protected: virtual bool process(); private: - SharedNodePointer _node; OctreeServer* _myServer; + QUuid _nodeUUID; int handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent); int packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged); OctreePacketData _packetData; + int _nodeMissingCount; QMutex _processLock; // don't allow us to have our nodeData, or our thread to be deleted while we're processing bool _isShuttingDown; }; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index fa087cced2..fd3f9e6cb7 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1160,7 +1160,6 @@ QString OctreeServer::getStatusLink() { } 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: @@ -1241,59 +1240,78 @@ QMap OctreeServer::_threadsDidPacketDistributor; QMap OctreeServer::_threadsDidHandlePacketSend; QMap OctreeServer::_threadsDidCallWriteDatagram; +QMutex OctreeServer::_threadsDidProcessMutex; +QMutex OctreeServer::_threadsDidPacketDistributorMutex; +QMutex OctreeServer::_threadsDidHandlePacketSendMutex; +QMutex OctreeServer::_threadsDidCallWriteDatagramMutex; + void OctreeServer::didProcess(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidProcessMutex); _threadsDidProcess[thread] = usecTimestampNow(); } void OctreeServer::didPacketDistributor(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidPacketDistributorMutex); _threadsDidPacketDistributor[thread] = usecTimestampNow(); } void OctreeServer::didHandlePacketSend(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidHandlePacketSendMutex); _threadsDidHandlePacketSend[thread] = usecTimestampNow(); } void OctreeServer::didCallWriteDatagram(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidCallWriteDatagramMutex); _threadsDidCallWriteDatagram[thread] = usecTimestampNow(); } void OctreeServer::stopTrackingThread(OctreeSendThread* thread) { + QMutexLocker lockerA(&_threadsDidProcessMutex); + QMutexLocker lockerB(&_threadsDidPacketDistributorMutex); + QMutexLocker lockerC(&_threadsDidHandlePacketSendMutex); + QMutexLocker lockerD(&_threadsDidCallWriteDatagramMutex); + _threadsDidProcess.remove(thread); _threadsDidPacketDistributor.remove(thread); _threadsDidHandlePacketSend.remove(thread); + _threadsDidCallWriteDatagram.remove(thread); } -int howManyThreadsDidSomething(QMap& something, quint64 since) { - if (since == 0) { - return something.size(); - } +int howManyThreadsDidSomething(QMutex& mutex, QMap& something, quint64 since) { int count = 0; - QMap::const_iterator i = something.constBegin(); - while (i != something.constEnd()) { - if (i.value() > since) { - count++; + if (mutex.tryLock()) { + if (since == 0) { + count = something.size(); + } else { + QMap::const_iterator i = something.constBegin(); + while (i != something.constEnd()) { + if (i.value() > since) { + count++; + } + ++i; + } } - ++i; + mutex.unlock(); } return count; } int OctreeServer::howManyThreadsDidProcess(quint64 since) { - return howManyThreadsDidSomething(_threadsDidProcess, since); + return howManyThreadsDidSomething(_threadsDidProcessMutex, _threadsDidProcess, since); } int OctreeServer::howManyThreadsDidPacketDistributor(quint64 since) { - return howManyThreadsDidSomething(_threadsDidPacketDistributor, since); + return howManyThreadsDidSomething(_threadsDidPacketDistributorMutex, _threadsDidPacketDistributor, since); } int OctreeServer::howManyThreadsDidHandlePacketSend(quint64 since) { - return howManyThreadsDidSomething(_threadsDidHandlePacketSend, since); + return howManyThreadsDidSomething(_threadsDidHandlePacketSendMutex, _threadsDidHandlePacketSend, since); } int OctreeServer::howManyThreadsDidCallWriteDatagram(quint64 since) { - return howManyThreadsDidSomething(_threadsDidCallWriteDatagram, since); + return howManyThreadsDidSomething(_threadsDidCallWriteDatagramMutex, _threadsDidCallWriteDatagram, since); } diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 3dac42709f..63d43b6634 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -211,6 +211,10 @@ protected: static QMap _threadsDidHandlePacketSend; static QMap _threadsDidCallWriteDatagram; + static QMutex _threadsDidProcessMutex; + static QMutex _threadsDidPacketDistributorMutex; + static QMutex _threadsDidHandlePacketSendMutex; + static QMutex _threadsDidCallWriteDatagramMutex; }; #endif // __octree_server__OctreeServer__ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index dc5a419295..761ea40d55 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -357,10 +357,20 @@ int NodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) { return 0; } -SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID) { - QMutexLocker locker(&_nodeHashMutex); - return _nodeHash.value(nodeUUID); -} +SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID, bool blockingLock) { + const int WAIT_TIME = 10; // wait up to 10ms in the try lock case + SharedNodePointer node; + // if caller wants us to block and guarantee the correct answer, then honor that request + if (blockingLock) { + // this will block till we can get access + QMutexLocker locker(&_nodeHashMutex); + node = _nodeHash.value(nodeUUID); + } else if (_nodeHashMutex.tryLock(WAIT_TIME)) { // some callers are willing to get wrong answers but not block + node = _nodeHash.value(nodeUUID); + _nodeHashMutex.unlock(); + } + return node; + } SharedNodePointer NodeList::sendingNodeForPacket(const QByteArray& packet) { QUuid nodeUUID = uuidFromPacketHeader(packet); diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index f10e01f3f4..d05d6a2fbc 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -103,7 +103,7 @@ public: QByteArray constructPingReplyPacket(const QByteArray& pingPacket); void pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& node); - SharedNodePointer nodeWithUUID(const QUuid& nodeUUID); + SharedNodePointer nodeWithUUID(const QUuid& nodeUUID, bool blockingLock = true); SharedNodePointer sendingNodeForPacket(const QByteArray& packet); SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType, From 563725e7810259e49f47a5b6cca883a14bdea5d7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 13:44:34 -0700 Subject: [PATCH 19/53] Use the "correct" shadow strategy: include only ambient term when in shadow. --- interface/resources/shaders/shadow_map.frag | 2 +- interface/resources/shaders/shadow_map.vert | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/interface/resources/shaders/shadow_map.frag b/interface/resources/shaders/shadow_map.frag index 810fc6a947..b683ed5af2 100644 --- a/interface/resources/shaders/shadow_map.frag +++ b/interface/resources/shaders/shadow_map.frag @@ -13,5 +13,5 @@ uniform sampler2DShadow shadowMap; varying vec4 shadowColor; void main(void) { - gl_FragColor = gl_Color * mix(shadowColor, vec4(1.0, 1.0, 1.0, 1.0), shadow2D(shadowMap, gl_TexCoord[0].stp)); + gl_FragColor = mix(shadowColor, gl_Color, shadow2D(shadowMap, gl_TexCoord[0].stp)); } diff --git a/interface/resources/shaders/shadow_map.vert b/interface/resources/shaders/shadow_map.vert index a15cd070b9..6809ca6e2b 100644 --- a/interface/resources/shaders/shadow_map.vert +++ b/interface/resources/shaders/shadow_map.vert @@ -11,14 +11,12 @@ varying vec4 shadowColor; void main(void) { - // the shadow color depends on the light product - vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); - float lightProduct = dot(normal, gl_LightSource[0].position); - shadowColor = mix(vec4(1.0, 1.0, 1.0, 1.0), vec4(0.8, 0.8, 0.8, 1.0), step(0.0, lightProduct)); + // the shadow color includes only the ambient terms + shadowColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient); - // standard diffuse lighting - gl_FrontColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient + - gl_LightSource[0].diffuse * max(0.0, lightProduct)); + // the normal color includes diffuse + vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); + gl_FrontColor = shadowColor + gl_Color * (gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position))); // generate the shadow texture coordinate using the eye position vec4 eyePosition = gl_ModelViewMatrix * gl_Vertex; From 9078d9a0f245a4a7218936adc0dc421d1a32a770 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 14:14:31 -0700 Subject: [PATCH 20/53] Fix for avatar LOD's getting into a degenerate state. Don't persist the avatar LOD, since we adjust it more rapidly. --- interface/interface_en.ts | 8 ++++---- interface/src/Menu.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index da8827d89d..aa7b1951ea 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 79b0a23ce5..efb5f04c43 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -393,8 +393,6 @@ void Menu::loadSettings(QSettings* settings) { _maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM); _maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS); _voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_OCTREE_SIZE_SCALE); - _avatarLODDistanceMultiplier = loadSetting(settings, "avatarLODDistanceMultiplier", - DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER); _boundaryLevelAdjust = loadSetting(settings, "boundaryLevelAdjust", 0); settings->beginGroup("View Frustum Offset Camera"); @@ -434,7 +432,6 @@ void Menu::saveSettings(QSettings* settings) { settings->setValue("maxVoxels", _maxVoxels); settings->setValue("maxVoxelsPPS", _maxVoxelPacketsPerSecond); settings->setValue("voxelSizeScale", _voxelSizeScale); - settings->setValue("avatarLODDistanceMultiplier", _avatarLODDistanceMultiplier); settings->setValue("boundaryLevelAdjust", _boundaryLevelAdjust); settings->beginGroup("View Frustum Offset Camera"); settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw); @@ -1203,7 +1200,10 @@ void Menu::autoAdjustLOD(float currentFPS) { 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; - _avatarLODDistanceMultiplier *= (targetFps / _fastFPSAverage.getAverage()); + float averageFps = _fastFPSAverage.getAverage(); + const float MAXIMUM_MULTIPLIER_SCALE = 2.0f; + _avatarLODDistanceMultiplier *= (averageFps < EPSILON) ? MAXIMUM_MULTIPLIER_SCALE : + qMin(MAXIMUM_MULTIPLIER_SCALE, targetFps / averageFps); _lastAvatarDetailDrop = now; } } else if (_fastFPSAverage.getAverage() > ADJUST_LOD_UP_FPS) { From eab4b4bc5d945ed2ef73db745970ea599971c2b0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 14:47:46 -0700 Subject: [PATCH 21/53] Make sure we process the model root nodes in alphabetic order. --- interface/src/renderer/FBXReader.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 53f4e04b0b..6846f79825 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -1259,7 +1259,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) remainingModels.insert(model.key()); } while (!remainingModels.isEmpty()) { - QString topID = getTopModelID(parentMap, models, *remainingModels.constBegin()); + QString first = *remainingModels.constBegin(); + foreach (const QString& id, remainingModels) { + if (id < first) { + first = id; + } + } + QString topID = getTopModelID(parentMap, models, first); appendModelIDs(parentMap.value(topID), childMap, models, remainingModels, modelIDs); } From 077df3906fbc491fd740e2bc99674772a2e26037 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 15:02:26 -0700 Subject: [PATCH 22/53] Make sure we reset our LOD adjustments before we switch back from throttling the renderer. --- interface/interface_en.ts | 4 ++-- interface/src/Application.cpp | 2 ++ interface/src/Menu.cpp | 6 ++++++ interface/src/Menu.h | 1 + 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index aa7b1951ea..8ca6f7d269 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index edeb454f11..5b08997b7c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1636,6 +1636,8 @@ void Application::updateLOD() { // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) { Menu::getInstance()->autoAdjustLOD(_fps); + } else { + Menu::getInstance()->resetLODAdjust(); } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index efb5f04c43..e1764374ea 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1249,6 +1249,12 @@ void Menu::autoAdjustLOD(float currentFPS) { } } +void Menu::resetLODAdjust() { + _fpsAverage.reset(); + _fastFPSAverage.reset(); + _lastAvatarDetailDrop = _lastAdjust = usecTimestampNow(); +} + void Menu::setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index cab5645304..6b41430eaf 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -85,6 +85,7 @@ public: // User Tweakable LOD Items QString getLODFeedbackText(); void autoAdjustLOD(float currentFPS); + void resetLODAdjust(); void setVoxelSizeScale(float sizeScale); float getVoxelSizeScale() const { return _voxelSizeScale; } float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; } From c540427b9e25a6aac8185455550daab7052a08f3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 15:03:46 -0700 Subject: [PATCH 23/53] tweaks to _processLock to reduce time it's locked --- .../src/octree/OctreeSendThread.cpp | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 023f3f3d2c..4105b21eb8 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -48,26 +48,28 @@ OctreeSendThread::~OctreeSendThread() { } void OctreeSendThread::setIsShuttingDown() { - QMutexLocker locker(&_processLock); // this will cause us to wait till the process loop is complete _isShuttingDown = true; OctreeServer::stopTrackingThread(this); + + // this will cause us to wait till the process loop is complete, we do this after we change _isShuttingDown + QMutexLocker locker(&_processLock); } bool OctreeSendThread::process() { + if (_isShuttingDown) { + return false; // exit early if we're shutting down + } + OctreeServer::didProcess(this); float lockWaitElapsedUsec = OctreeServer::SKIP_TIME; quint64 lockWaitStart = usecTimestampNow(); - QMutexLocker locker(&_processLock); + _processLock.lock(); quint64 lockWaitEnd = usecTimestampNow(); lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart); OctreeServer::trackProcessWaitTime(lockWaitElapsedUsec); - if (_isShuttingDown) { - return false; // exit early if we're shutting down - } - quint64 start = usecTimestampNow(); // don't do any send processing until the initial load of the octree is complete... @@ -98,6 +100,12 @@ bool OctreeSendThread::process() { } } + _processLock.unlock(); + + if (_isShuttingDown) { + return false; // exit early if we're shutting down + } + // Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap if (isStillRunning()) { // dynamically sleep until we need to fire off the next set of octree elements From 41e9017953a2261774c8ec50ee1fe12fbb355c85 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 27 Mar 2014 15:14:38 -0700 Subject: [PATCH 24/53] more audio level meter work, improved gun --- examples/gun.js | 36 ++++++++++++++++++++++++++--------- interface/src/Application.cpp | 12 ++++++++++-- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/examples/gun.js b/examples/gun.js index 94f3fd4ee3..e358e6b391 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -27,6 +27,9 @@ var BULLET_VELOCITY = 5.0; var MIN_THROWER_DELAY = 1000; var MAX_THROWER_DELAY = 1000; var LEFT_BUTTON_3 = 3; +var RELOAD_INTERVAL = 9; + +var showScore = false; // Load some sound to use for loading and firing var fireSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guns/GUN-SHOT2.raw"); @@ -38,6 +41,8 @@ var targetLaunchSound = new Sound("http://highfidelity-public.s3-us-west-1.amazo var audioOptions = new AudioInjectionOptions(); audioOptions.volume = 0.9; +var shotsFired = 0; + var shotTime = new Date(); // initialize our triggers @@ -63,7 +68,8 @@ var reticle = Overlays.addOverlay("image", { alpha: 1 }); -var text = Overlays.addOverlay("text", { +if (showScore) { + var text = Overlays.addOverlay("text", { x: screenSize.x / 2 - 100, y: screenSize.y / 2 - 50, width: 150, @@ -74,6 +80,8 @@ var text = Overlays.addOverlay("text", { leftMargin: 4, text: "Score: " + score }); +} + function printVector(string, vector) { @@ -94,6 +102,10 @@ function shootBullet(position, velocity) { // Play firing sounds audioOptions.position = position; Audio.playSound(fireSound, audioOptions); + shotsFired++; + if ((shotsFired % RELOAD_INTERVAL) == 0) { + Audio.playSound(loadSound, audioOptions); + } } function shootTarget() { @@ -147,12 +159,15 @@ function particleCollisionWithVoxel(particle, voxel, penetration) { Voxels.eraseVoxel(position.x, position.y, position.z, HOLE_SIZE); //audioOptions.position = position; audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); - Audio.playSound(targetHitSound, audioOptions); + Audio.playSound(impactSound, audioOptions); } function particleCollisionWithParticle(particle1, particle2) { score++; - Overlays.editOverlay(text, { text: "Score: " + score } ); + if (showScore) { + Overlays.editOverlay(text, { text: "Score: " + score } ); + } + // Sort out which particle is which // Record shot time @@ -171,12 +186,12 @@ function keyPressEvent(event) { if (event.text == "t") { var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; Script.setTimeout(shootTarget, time); + } if (event.text == ".") { + shootFromMouse(); } } function update(deltaTime) { - - // Check for mouseLook movement, update rotation // rotate body yaw for yaw received from mouse var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Radians( { x: 0, y: yawFromMouse, z: 0 } )); @@ -257,18 +272,21 @@ function mousePressEvent(event) { isMouseDown = true; lastX = event.x; lastY = event.y; - audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); - Audio.playSound(loadSound, audioOptions); + //audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); + //Audio.playSound(loadSound, audioOptions); } -function mouseReleaseEvent(event) { - // position +function shootFromMouse() { var DISTANCE_FROM_CAMERA = 2.0; var camera = Camera.getPosition(); var forwardVector = Quat.getFront(Camera.getOrientation()); var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY); shootBullet(newPosition, velocity); +} + +function mouseReleaseEvent(event) { + // position isMouseDown = false; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 864be460e7..c67f992536 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2503,14 +2503,22 @@ void Application::displayOverlay() { } } - const int AUDIO_METER_WIDTH = 300; + const int AUDIO_METER_WIDTH = 200; const int AUDIO_METER_INSET = 2; const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET; const int AUDIO_METER_HEIGHT = 8; const int AUDIO_METER_Y = _glWidget->height() - 40; const int AUDIO_METER_X = 25; const float CLIPPING_INDICATOR_TIME = 1.0f; - float audioLevel = log10(_audio.getLastInputLoudness() + 1.0) / log10(32767.0) * (float)AUDIO_METER_SCALE_WIDTH; + const float LOG2 = log(2.f); + float audioLevel = 0.f; + float loudness = _audio.getLastInputLoudness() + 1.f; + float log2loudness = log(loudness) / LOG2; + if (loudness < 2048) { + audioLevel = (log2loudness / 11.f) * (AUDIO_METER_SCALE_WIDTH / 5.f); + } else { + audioLevel = (log2loudness - 10.f) * (AUDIO_METER_SCALE_WIDTH / 5.f); + } bool isClipping = ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); if (isClipping) { From 796d20168e1d3105ed0a887fce3bb9b20f8395a6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 27 Mar 2014 15:30:30 -0700 Subject: [PATCH 25/53] splitting lean input from lean perturbations --- interface/src/avatar/Avatar.cpp | 19 ----------- interface/src/avatar/Avatar.h | 2 +- interface/src/avatar/Head.cpp | 21 +++++++++++- interface/src/avatar/Head.h | 17 ++++++++-- interface/src/avatar/MyAvatar.cpp | 45 ++++++++++++++++---------- interface/src/avatar/MyAvatar.h | 4 +++ interface/src/avatar/SkeletonModel.cpp | 10 ++++-- libraries/avatars/src/HeadData.cpp | 7 ---- libraries/avatars/src/HeadData.h | 5 --- 9 files changed, 75 insertions(+), 55 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index de9b33d9c7..75b0a4c66a 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -758,25 +758,6 @@ bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const { return false; } -void Avatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { - // compute lean angles - glm::vec3 leverAxis = contactPoint - getPosition(); - float leverLength = glm::length(leverAxis); - if (leverLength > EPSILON) { - glm::quat bodyRotation = getOrientation(); - glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f); - 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(); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 685705bfc4..f2ee400ba2 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -145,7 +145,7 @@ public: /// \return true if we expect the avatar would move as a result of the collision bool collisionWouldMoveAvatar(CollisionInfo& collision) const; - void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration); + virtual void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { } /// \return bounding radius of avatar virtual float getBoundingRadius() const; diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index d432830252..ffa0975ccb 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -39,6 +39,8 @@ Head::Head(Avatar* owningAvatar) : _deltaPitch(0.f), _deltaYaw(0.f), _deltaRoll(0.f), + _deltaLeanSideways(0.f), + _deltaLeanForward(0.f), _isCameraMoving(false), _faceModel(this) { @@ -56,7 +58,6 @@ void Head::reset() { } void Head::simulate(float deltaTime, bool isMine, bool billboard) { - // Update audio trailing average for rendering facial animations Faceshift* faceshift = Application::getInstance()->getFaceshift(); Visage* visage = Application::getInstance()->getVisage(); @@ -165,6 +166,19 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { _eyePosition = calculateAverageEyePosition(); } +void Head::relaxLean(float deltaTime) { + // restore rotation, lean to neutral positions + const float LEAN_RELAXATION_PERIOD = 0.25f; // seconds + float relaxationFactor = 1.f - glm::min(deltaTime / LEAN_RELAXATION_PERIOD, 1.f); + _deltaYaw *= relaxationFactor; + _deltaPitch *= relaxationFactor; + _deltaRoll *= relaxationFactor; + _leanSideways *= relaxationFactor; + _leanForward *= relaxationFactor; + _deltaLeanSideways *= relaxationFactor; + _deltaLeanForward *= relaxationFactor; +} + void Head::render(float alpha, bool forShadowMap) { if (_faceModel.render(alpha, forShadowMap) && _renderLookatVectors) { renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition); @@ -209,6 +223,11 @@ float Head::getFinalRoll() const { return glm::clamp(_baseRoll + _deltaRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } +void Head::addLeanDeltas(float sideways, float forward) { + _deltaLeanSideways += sideways; + _deltaLeanForward += forward; +} + void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { Application::getInstance()->getGlowEffect()->begin(); diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index dc96aa318f..8a03cfc7ad 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -44,6 +44,8 @@ public: void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; } void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; } + void setLeanSideways(float leanSideways) { _leanSideways = leanSideways; } + void setLeanForward(float leanForward) { _leanForward = leanForward; } /// \return orientationBody * orientationBase+Delta glm::quat getFinalOrientation() const; @@ -61,6 +63,10 @@ public: glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getUpDirection() const { return getOrientation() * IDENTITY_UP; } glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; } + float getLeanSideways() const { return _leanSideways; } + float getLeanForward() const { return _leanForward; } + float getFinalLeanSideways() const { return _leanSideways + _deltaLeanSideways; } + float getFinalLeanForward() const { return _leanForward + _deltaLeanForward; } glm::quat getEyeRotation(const glm::vec3& eyePosition) const; @@ -71,7 +77,7 @@ public: float getAverageLoudness() const { return _averageLoudness; } glm::vec3 calculateAverageEyePosition() { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * ONE_HALF; } - /// Returns the point about which scaling occurs. + /// \return the point about which scaling occurs. glm::vec3 getScalePivot() const; void setDeltaPitch(float pitch) { _deltaPitch = pitch; } @@ -86,6 +92,9 @@ public: virtual float getFinalPitch() const; virtual float getFinalYaw() const; virtual float getFinalRoll() const; + + void relaxLean(float deltaTime); + void addLeanDeltas(float sideways, float forward); private: // disallow copies of the Head, copy of owning Avatar is disallowed too @@ -110,11 +119,15 @@ private: float _rightEyeBlinkVelocity; float _timeWithoutTalking; - // delta angles for local head rotation + // delta angles for local head rotation (driven by hardware input) float _deltaPitch; float _deltaYaw; float _deltaRoll; + // delta lean angles for lean perturbations (driven by collisions) + float _deltaLeanSideways; + float _deltaLeanForward; + bool _isCameraMoving; FaceModel _faceModel; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c1c8a9a616..e18735e3e5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -93,7 +93,13 @@ void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) { } void MyAvatar::update(float deltaTime) { + Head* head = getHead(); + head->relaxLean(deltaTime); updateFromGyros(deltaTime); + if (Menu::getInstance()->isOptionChecked(MenuOption::MoveWithLean)) { + // Faceshift drive is enabled, set the avatar drive based on the head position + moveWithLean(); + } // Update head mouse from faceshift if active Faceshift* faceshift = Application::getInstance()->getFaceshift(); @@ -111,7 +117,6 @@ void MyAvatar::update(float deltaTime) { //_headMouseY = glm::clamp(_headMouseY, 0, _glWidget->height()); } - Head* head = getHead(); if (OculusManager::isConnected()) { float yaw, pitch, roll; // these angles will be in radians OculusManager::getEulerAngles(yaw, pitch, roll); @@ -360,17 +365,7 @@ void MyAvatar::updateFromGyros(float deltaTime) { } } } - } else { - // restore rotation, lean to neutral positions - const float RESTORE_PERIOD = 0.25f; // seconds - float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f); - head->setDeltaPitch(glm::mix(head->getDeltaPitch(), 0.0f, restorePercentage)); - head->setDeltaYaw(glm::mix(head->getDeltaYaw(), 0.0f, restorePercentage)); - head->setDeltaRoll(glm::mix(head->getDeltaRoll(), 0.0f, restorePercentage)); - head->setLeanSideways(glm::mix(head->getLeanSideways(), 0.0f, restorePercentage)); - head->setLeanForward(glm::mix(head->getLeanForward(), 0.0f, restorePercentage)); - return; - } + } // Set the rotation of the avatar's head (as seen by others, not affecting view frustum) // to be scaled. Pitch is greater to emphasize nodding behavior / synchrony. @@ -389,13 +384,11 @@ void MyAvatar::updateFromGyros(float deltaTime) { -MAX_LEAN, MAX_LEAN)); head->setLeanForward(glm::clamp(glm::degrees(atanf(relativePosition.z * _leanScale / TORSO_LENGTH)), -MAX_LEAN, MAX_LEAN)); +} - // if Faceshift drive is enabled, set the avatar drive based on the head position - if (!Menu::getInstance()->isOptionChecked(MenuOption::MoveWithLean)) { - return; - } - +void MyAvatar::moveWithLean() { // Move with Lean by applying thrust proportional to leaning + Head* head = getHead(); glm::quat orientation = head->getCameraOrientation(); glm::vec3 front = orientation * IDENTITY_FRONT; glm::vec3 right = orientation * IDENTITY_RIGHT; @@ -1152,3 +1145,21 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) { } } + +void MyAvatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { + glm::vec3 leverAxis = contactPoint - getPosition(); + float leverLength = glm::length(leverAxis); + if (leverLength > EPSILON) { + // compute lean perturbation angles + glm::quat bodyRotation = getOrientation(); + glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f); + glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f); + + leverAxis = leverAxis / leverLength; + glm::vec3 effectivePenetration = penetration - glm::dot(penetration, leverAxis) * leverAxis; + // use the small-angle approximation for sine + float sideways = - glm::dot(effectivePenetration, xAxis) / leverLength; + float forward = glm::dot(effectivePenetration, zAxis) / leverLength; + getHead()->addLeanDeltas(sideways, forward); + } +} diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cbb625aa2f..38edc5356e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -34,6 +34,7 @@ public: void update(float deltaTime); void simulate(float deltaTime); void updateFromGyros(float deltaTime); + void moveWithLean(); void render(const glm::vec3& cameraPosition, RenderMode renderMode = NORMAL_RENDER_MODE); void renderBody(RenderMode renderMode); @@ -87,6 +88,9 @@ public: virtual void clearJointData(int index); virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); + + void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration); + public slots: void goHome(); void increaseSize(); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index b4746a39d2..3c5f5cae6a 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -72,11 +72,15 @@ void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _jointStates.size(); i++) { const FBXJoint& joint = geometry.joints[i]; + int parentIndex = joint.parentIndex; if (i == jointIndex) { // this shape is the hand shapes.push_back(_shapes[i]); + if (parentIndex != -1) { + // also add the forearm + shapes.push_back(_shapes[parentIndex]); + } } else { - int parentIndex = joint.parentIndex; while (parentIndex != -1) { if (parentIndex == jointIndex) { // this shape is a child of the hand @@ -199,8 +203,8 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const glm::mat3 axes = glm::mat3_cast(_rotation); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) * joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation))); - state.rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getLeanSideways(), - glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getLeanForward(), + state.rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), + glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation; } diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index da1bdca23b..e74ac043fb 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -61,10 +61,3 @@ void HeadData::addRoll(float roll) { setBaseRoll(_baseRoll + roll); } - -void HeadData::addLean(float sideways, float forwards) { - // Add lean as impulse - _leanSideways += sideways; - _leanForward += forwards; -} - diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index a907c5b694..c60627e3f9 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -32,10 +32,6 @@ public: virtual ~HeadData() { }; // degrees - float getLeanSideways() const { return _leanSideways; } - void setLeanSideways(float leanSideways) { _leanSideways = leanSideways; } - float getLeanForward() const { return _leanForward; } - void setLeanForward(float leanForward) { _leanForward = leanForward; } float getBaseYaw() const { return _baseYaw; } void setBaseYaw(float yaw) { _baseYaw = glm::clamp(yaw, MIN_HEAD_YAW, MAX_HEAD_YAW); } float getBasePitch() const { return _basePitch; } @@ -64,7 +60,6 @@ public: void addYaw(float yaw); void addPitch(float pitch); void addRoll(float roll); - void addLean(float sideways, float forwards); const glm::vec3& getLookAtPosition() const { return _lookAtPosition; } void setLookAtPosition(const glm::vec3& lookAtPosition) { _lookAtPosition = lookAtPosition; } From aee15e0496863a0b638cef385814e59cf417f3bb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 15:38:21 -0700 Subject: [PATCH 26/53] Don't render display name/chat message for own avatar in first person mode. --- interface/src/avatar/Avatar.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 94e1416e68..9e9210a5b8 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -246,7 +246,8 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { const float DISPLAYNAME_DISTANCE = 10.0f; setShowDisplayName(renderMode == NORMAL_RENDER_MODE && distanceToTarget < DISPLAYNAME_DISTANCE); - if (renderMode != NORMAL_RENDER_MODE) { + if (renderMode != NORMAL_RENDER_MODE || (isMyAvatar() && + Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON)) { return; } renderDisplayName(); From 361276d13389a0227f7ed411ec71fa066eb574ae Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 16:19:21 -0700 Subject: [PATCH 27/53] Use a spherical estimate of the shadowed region to avoid growing and shrinking it as we turn around. Also, might as well render shadows for the billboards. --- interface/interface_en.ts | 4 ++-- interface/src/Application.cpp | 29 ++++++++++++++++++----------- interface/src/avatar/Avatar.cpp | 4 +--- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 8ca6f7d269..c1d16e878f 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5b08997b7c..c6fc522fca 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2189,19 +2189,26 @@ void Application::updateShadowMap() { (_viewFrustum.getFarClip() - _viewFrustum.getNearClip()); loadViewFrustum(_myCamera, _viewFrustum); glm::vec3 points[] = { - inverseRotation * (glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale)), - inverseRotation * (glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale)), - inverseRotation * (glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale)), - inverseRotation * (glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale)), - inverseRotation * (glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale)), - inverseRotation * (glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale)), - inverseRotation * (glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale)), - inverseRotation * (glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale)) }; - glm::vec3 minima(FLT_MAX, FLT_MAX, FLT_MAX), maxima(-FLT_MAX, -FLT_MAX, -FLT_MAX); + glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale), + glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale), + glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale), + glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale), + glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale), + glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale), + glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale), + glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale) }; + glm::vec3 center; for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) { - minima = glm::min(minima, points[i]); - maxima = glm::max(maxima, points[i]); + center += points[i]; } + center /= (float)(sizeof(points) / sizeof(points[0])); + float radius = 0.0f; + for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) { + radius = qMax(radius, glm::distance(points[i], center)); + } + center = inverseRotation * center; + glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius); + glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius); // stretch out our extents in z so that we get all of the avatars minima.z -= _viewFrustum.getFarClip() * 0.5f; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 9e9210a5b8..0b77d3e759 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -313,9 +313,7 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { void Avatar::renderBody(RenderMode renderMode) { if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { // render the billboard until both models are loaded - if (renderMode != SHADOW_RENDER_MODE) { - renderBillboard(); - } + renderBillboard(); return; } _skeletonModel.render(1.0f, renderMode == SHADOW_RENDER_MODE); From 5c7ac6d3a1964870675063d2a38cf494fed9c03a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Mar 2014 17:41:55 -0700 Subject: [PATCH 28/53] hack the NodeList to take the socket on which pinged as active socket --- libraries/shared/src/NodeList.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 761ea40d55..d4ede7ee66 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -294,6 +294,10 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr matchingNode->setLastHeardMicrostamp(usecTimestampNow()); QByteArray replyPacket = constructPingReplyPacket(packet); writeDatagram(replyPacket, matchingNode, senderSockAddr); + + // HACK for hacker dojo network demo + matchingNode->setPublicSocket(senderSockAddr); + matchingNode->activatePublicSocket(); } break; @@ -829,13 +833,13 @@ SharedNodePointer NodeList::updateSocketsForNode(const QUuid& uuid, // check if we need to change this node's public or local sockets if (publicSocket != matchingNode->getPublicSocket()) { - matchingNode->setPublicSocket(publicSocket); - qDebug() << "Public socket change for node" << *matchingNode; +// matchingNode->setPublicSocket(publicSocket); +// qDebug() << "Public socket change for node" << *matchingNode; } if (localSocket != matchingNode->getLocalSocket()) { - matchingNode->setLocalSocket(localSocket); - qDebug() << "Local socket change for node" << *matchingNode; +// matchingNode->setLocalSocket(localSocket); +// qDebug() << "Local socket change for node" << *matchingNode; } } From 1c47b8a3e15336a188e82e50efc5484ceb8901b3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Mar 2014 21:29:01 -0700 Subject: [PATCH 29/53] Revert "hack the NodeList to take the socket on which pinged as active socket" This reverts commit 5c7ac6d3a1964870675063d2a38cf494fed9c03a. --- libraries/shared/src/NodeList.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index d4ede7ee66..761ea40d55 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -294,10 +294,6 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr matchingNode->setLastHeardMicrostamp(usecTimestampNow()); QByteArray replyPacket = constructPingReplyPacket(packet); writeDatagram(replyPacket, matchingNode, senderSockAddr); - - // HACK for hacker dojo network demo - matchingNode->setPublicSocket(senderSockAddr); - matchingNode->activatePublicSocket(); } break; @@ -833,13 +829,13 @@ SharedNodePointer NodeList::updateSocketsForNode(const QUuid& uuid, // check if we need to change this node's public or local sockets if (publicSocket != matchingNode->getPublicSocket()) { -// matchingNode->setPublicSocket(publicSocket); -// qDebug() << "Public socket change for node" << *matchingNode; + matchingNode->setPublicSocket(publicSocket); + qDebug() << "Public socket change for node" << *matchingNode; } if (localSocket != matchingNode->getLocalSocket()) { -// matchingNode->setLocalSocket(localSocket); -// qDebug() << "Local socket change for node" << *matchingNode; + matchingNode->setLocalSocket(localSocket); + qDebug() << "Local socket change for node" << *matchingNode; } } From 18a293c020f8bda43569c47478ad2a7fd1e7f094 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Mar 2014 08:58:49 -0700 Subject: [PATCH 30/53] add a symmetric socket to the Node class --- libraries/shared/src/Node.cpp | 1 + libraries/shared/src/Node.h | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index dadf39f790..d300cd7ba0 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -51,6 +51,7 @@ Node::Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const _lastHeardMicrostamp(usecTimestampNow()), _publicSocket(publicSocket), _localSocket(localSocket), + _symmetricSocket(), _activeSocket(NULL), _connectionSecret(), _bytesReceivedMovingAverage(NULL), diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h index 43ec5baf81..1c9ee97477 100644 --- a/libraries/shared/src/Node.h +++ b/libraries/shared/src/Node.h @@ -70,7 +70,9 @@ public: void setPublicSocket(const HifiSockAddr& publicSocket); const HifiSockAddr& getLocalSocket() const { return _localSocket; } void setLocalSocket(const HifiSockAddr& localSocket); - + const HifiSockAddr& getSymmetricSocket() const { return _symmetricSocket; } + void setSymmetricSocket(const HifiSockAddr& symmetricSocket) { _symmetricSocket = symmetricSocket; } + const HifiSockAddr* getActiveSocket() const { return _activeSocket; } void activatePublicSocket(); @@ -110,6 +112,7 @@ private: quint64 _lastHeardMicrostamp; HifiSockAddr _publicSocket; HifiSockAddr _localSocket; + HifiSockAddr _symmetricSocket; HifiSockAddr* _activeSocket; QUuid _connectionSecret; SimpleMovingAverage* _bytesReceivedMovingAverage; From f80b415497e6c1402ed46217280702fae0129f1b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Mar 2014 09:07:30 -0700 Subject: [PATCH 31/53] handle activation and setting of symmetric socket --- libraries/shared/src/Node.cpp | 14 ++++++++++++++ libraries/shared/src/Node.h | 3 ++- libraries/shared/src/NodeList.cpp | 12 ++++++++++++ libraries/shared/src/NodeList.h | 1 + 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index d300cd7ba0..a4491fb707 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -85,6 +85,15 @@ void Node::setLocalSocket(const HifiSockAddr& localSocket) { _localSocket = localSocket; } +void Node::setSymmetricSocket(const HifiSockAddr& symmetricSocket) { + if (_activeSocket == &_symmetricSocket) { + // if the active socket was the symmetric socket then reset it to NULL + _activeSocket = NULL; + } + + _symmetricSocket = symmetricSocket; +} + void Node::activateLocalSocket() { qDebug() << "Activating local socket for node" << *this; _activeSocket = &_localSocket; @@ -95,6 +104,11 @@ void Node::activatePublicSocket() { _activeSocket = &_publicSocket; } +void Node::activateSymmetricSocket() { + qDebug() << "Activating symmetric socket for node" << *this; + _activeSocket = &_symmetricSocket; +} + void Node::recordBytesReceived(int bytesReceived) { if (!_bytesReceivedMovingAverage) { _bytesReceivedMovingAverage = new SimpleMovingAverage(100); diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h index 1c9ee97477..79d75629a6 100644 --- a/libraries/shared/src/Node.h +++ b/libraries/shared/src/Node.h @@ -71,12 +71,13 @@ public: const HifiSockAddr& getLocalSocket() const { return _localSocket; } void setLocalSocket(const HifiSockAddr& localSocket); const HifiSockAddr& getSymmetricSocket() const { return _symmetricSocket; } - void setSymmetricSocket(const HifiSockAddr& symmetricSocket) { _symmetricSocket = symmetricSocket; } + void setSymmetricSocket(const HifiSockAddr& symmetricSocket); const HifiSockAddr* getActiveSocket() const { return _activeSocket; } void activatePublicSocket(); void activateLocalSocket(); + void activateSymmetricSocket(); const QUuid& getConnectionSecret() const { return _connectionSecret; } void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; } diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 761ea40d55..bcd40a8de4 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -294,6 +294,15 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr matchingNode->setLastHeardMicrostamp(usecTimestampNow()); QByteArray replyPacket = constructPingReplyPacket(packet); writeDatagram(replyPacket, matchingNode, senderSockAddr); + + // If we don't have a symmetric socket for this node and this socket doesn't match + // what we have for public and local then set it as the symmetric. + // This allows a server on a reachable port to communicate with nodes on symmetric NATs + if (matchingNode->getSymmetricSocket().isNull()) { + if (senderSockAddr != matchingNode->getLocalSocket() && senderSockAddr != matchingNode->getPublicSocket()) { + matchingNode->setSymmetricSocket(senderSockAddr); + } + } } break; @@ -879,6 +888,9 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con sendingNode->activateLocalSocket(); } else if (pingType == PingType::Public && !sendingNode->getActiveSocket()) { sendingNode->activatePublicSocket(); + } else if (pingType == PingType::Symmetric && !sendingNode->getActiveSocket()) { + + } } diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index d05d6a2fbc..182c72b670 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -58,6 +58,7 @@ namespace PingType { const PingType_t Agnostic = 0; const PingType_t Local = 1; const PingType_t Public = 2; + const PingType_t Symmetric = 3; } class NodeList : public QObject { From 8b1b2d8d9901a07fa9483ef7a30fe743b70b5f91 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Mar 2014 09:11:20 -0700 Subject: [PATCH 32/53] send a symmetric ping packet if appropriate when pinging nodes --- libraries/shared/src/NodeList.cpp | 9 +++++++-- libraries/shared/src/NodeList.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index bcd40a8de4..879377a2fd 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -791,7 +791,7 @@ QByteArray NodeList::constructPingReplyPacket(const QByteArray& pingPacket) { return replyPacket; } -void NodeList::pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& node) { +void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) { // send the ping packet to the local and public sockets for this node QByteArray localPingPacket = constructPingPacket(PingType::Local); @@ -799,6 +799,11 @@ void NodeList::pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& QByteArray publicPingPacket = constructPingPacket(PingType::Public); writeDatagram(publicPingPacket, node, node->getPublicSocket()); + + if (!node->getSymmetricSocket().isNull()) { + QByteArray symmetricPingPacket = constructPingPacket(PingType::Symmetric); + writeDatagram(symmetricPingPacket, node, node->getSymmetricSocket()); + } } SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, @@ -869,7 +874,7 @@ void NodeList::pingInactiveNodes() { foreach (const SharedNodePointer& node, getNodeHash()) { if (!node->getActiveSocket()) { // we don't have an active link to this node, ping it to set that up - pingPublicAndLocalSocketsForInactiveNode(node); + pingPunchForInactiveNode(node); } } } diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 182c72b670..34078b6a94 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -102,7 +102,7 @@ public: QByteArray constructPingPacket(PingType_t pingType = PingType::Agnostic); QByteArray constructPingReplyPacket(const QByteArray& pingPacket); - void pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& node); + void pingPunchForInactiveNode(const SharedNodePointer& node); SharedNodePointer nodeWithUUID(const QUuid& nodeUUID, bool blockingLock = true); SharedNodePointer sendingNodeForPacket(const QByteArray& packet); From 65f912249db51230b6250fb814dd15da90eaefaa Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Mon, 17 Mar 2014 14:52:56 +0200 Subject: [PATCH 33/53] Moved user names in the char 1 pixel to the bottom. --- interface/src/ui/ChatWindow.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 55f32c5c7c..21f9b5285f 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -191,7 +191,10 @@ void ChatWindow::participantsChanged() { userLabel->setStyleSheet("background-color: palette(light);" "border-radius: 5px;" "color: #267077;" - "padding: 2px;" + "padding-top: 3px;" + "padding-right: 2px;" + "padding-bottom: 2px;" + "padding-left: 2px;" "border: 1px solid palette(shadow);" "font-weight: bold"); ui->usersWidget->layout()->addWidget(userLabel); From a48ca6523685007b1e5bd5ad1b9bf14335f17a43 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Mon, 17 Mar 2014 15:49:30 +0200 Subject: [PATCH 34/53] Enabled going to the clicked user in the chat. --- interface/interface_en.ts | 8 ++-- interface/src/Menu.cpp | 10 +++-- interface/src/Menu.h | 1 + interface/src/ui/ChatWindow.cpp | 70 +++++++++++++++++++-------------- 4 files changed, 52 insertions(+), 37 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index c1d16e878f..5d17756094 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -45,7 +45,7 @@ - + day %n day @@ -53,7 +53,7 @@ - + hour %n hour @@ -61,7 +61,7 @@ - + minute %n minute @@ -76,7 +76,7 @@ - + %1 online now: diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e1764374ea..47c6d040be 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -923,12 +923,16 @@ void Menu::goTo() { gotoDialog.resize(gotoDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, gotoDialog.size().height()); int dialogReturn = gotoDialog.exec(); - if (dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty()) { + goToUser(dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty(), + gotoDialog.textValue()); +} + +void Menu::goToUser(bool go, const QString& user) { + if (go) { LocationManager* manager = &LocationManager::getInstance(); - manager->goTo(gotoDialog.textValue()); + manager->goTo(user); connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision); } - sendFakeEnterEvent(); } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 6b41430eaf..41bd1b85fd 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -122,6 +122,7 @@ public slots: void importSettings(); void exportSettings(); void goTo(); + void goToUser(bool go, const QString& user); void pasteToVoxel(); void toggleLoginMenuItem(); diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 21f9b5285f..2ea2b080a0 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include "Application.h" @@ -90,27 +89,33 @@ void ChatWindow::showEvent(QShowEvent* event) { } bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { - Q_UNUSED(sender); - - if (event->type() != QEvent::KeyPress) { - return false; - } - QKeyEvent* keyEvent = static_cast(event); - if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && - (keyEvent->modifiers() & Qt::ShiftModifier) == 0) { - QString messageText = ui->messagePlainTextEdit->document()->toPlainText().trimmed(); - if (!messageText.isEmpty()) { -#ifdef HAVE_QXMPP - const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); - QXmppMessage message; - message.setTo(publicChatRoom->jid()); - message.setType(QXmppMessage::GroupChat); - message.setBody(messageText); - XmppClient::getInstance().getXMPPClient().sendPacket(message); -#endif - ui->messagePlainTextEdit->document()->clear(); + if (sender == ui->messagePlainTextEdit) { + if (event->type() != QEvent::KeyPress) { + return false; } - return true; + QKeyEvent* keyEvent = static_cast(event); + if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && + (keyEvent->modifiers() & Qt::ShiftModifier) == 0) { + QString messageText = ui->messagePlainTextEdit->document()->toPlainText().trimmed(); + if (!messageText.isEmpty()) { + #ifdef HAVE_QXMPP + const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); + QXmppMessage message; + message.setTo(publicChatRoom->jid()); + message.setType(QXmppMessage::GroupChat); + message.setBody(messageText); + XmppClient::getInstance().getXMPPClient().sendPacket(message); + #endif + ui->messagePlainTextEdit->document()->clear(); + } + return true; + } + } else { + if (event->type() != QEvent::MouseButtonRelease) { + return false; + } + QString user = sender->property("user").toString(); + Menu::getInstance()->goToUser(true, user); } return false; } @@ -187,16 +192,21 @@ void ChatWindow::participantsChanged() { delete item; } foreach (const QString& participant, participants) { - QLabel* userLabel = new QLabel(getParticipantName(participant)); + QString participantName = getParticipantName(participant); + QLabel* userLabel = new QLabel(); + userLabel->setText(participantName); userLabel->setStyleSheet("background-color: palette(light);" - "border-radius: 5px;" - "color: #267077;" - "padding-top: 3px;" - "padding-right: 2px;" - "padding-bottom: 2px;" - "padding-left: 2px;" - "border: 1px solid palette(shadow);" - "font-weight: bold"); + "border-radius: 5px;" + "color: #267077;" + "padding-top: 3px;" + "padding-right: 2px;" + "padding-bottom: 2px;" + "padding-left: 2px;" + "border: 1px solid palette(shadow);" + "font-weight: bold"); + userLabel->setProperty("user", participantName); + userLabel->setCursor(Qt::PointingHandCursor); + userLabel->installEventFilter(this); ui->usersWidget->layout()->addWidget(userLabel); } } From d764ef79ed8461cc531bccc8e285fcca3e63980b Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Mon, 17 Mar 2014 18:05:16 +0200 Subject: [PATCH 35/53] Gave more space to the user in sent messages so that resizing of the column for messages occurs for really long user names. --- interface/interface_en.ts | 14 +++++++------- interface/src/ui/ChatWindow.cpp | 15 ++++++++++----- interface/ui/chatWindow.ui | 32 ++++++++++++++++---------------- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 5d17756094..e2f9720777 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -28,24 +28,24 @@ ChatWindow - + Chat - + Connecting to XMPP... - + online now: - + day %n day @@ -53,7 +53,7 @@ - + hour %n hour @@ -61,7 +61,7 @@ - + minute %n minute @@ -76,7 +76,7 @@ - + %1 online now: diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 2ea2b080a0..ab31037f43 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -6,7 +6,7 @@ // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. // -#include +#include #include #include #include @@ -38,6 +38,9 @@ ChatWindow::ChatWindow() : FlowLayout* flowLayout = new FlowLayout(0, 4, 4); ui->usersWidget->setLayout(flowLayout); + ui->messagesGridLayout->setColumnStretch(0, 1); + ui->messagesGridLayout->setColumnStretch(1, 3); + ui->messagePlainTextEdit->installEventFilter(this); ui->closeButton->hide(); @@ -143,8 +146,9 @@ void ChatWindow::addTimeStamp() { timeLabel->setStyleSheet("color: palette(shadow);" "background-color: palette(highlight);" "padding: 4px;"); + timeLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); timeLabel->setAlignment(Qt::AlignHCenter); - ui->messagesFormLayout->addRow(timeLabel); + ui->messagesGridLayout->addWidget(timeLabel, ui->messagesGridLayout->rowCount(), 0, 1, 2); numMessagesAfterLastTimeStamp = 0; } } @@ -217,7 +221,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { } QLabel* userLabel = new QLabel(getParticipantName(message.from())); - userLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + userLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); userLabel->setStyleSheet("padding: 2px; font-weight: bold"); userLabel->setAlignment(Qt::AlignTop); @@ -228,8 +232,9 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { messageLabel->setStyleSheet("padding: 2px; margin-right: 20px"); messageLabel->setAlignment(Qt::AlignTop); - ui->messagesFormLayout->addRow(userLabel, messageLabel); - ui->messagesFormLayout->parentWidget()->updateGeometry(); + ui->messagesGridLayout->addWidget(userLabel, ui->messagesGridLayout->rowCount(), 0, Qt::AlignTop | Qt::AlignRight); + ui->messagesGridLayout->addWidget(messageLabel, ui->messagesGridLayout->rowCount() - 1, 1); + ui->messagesGridLayout->parentWidget()->updateGeometry(); Application::processEvents(); QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar(); verticalScrollBar->setSliderPosition(verticalScrollBar->maximum()); diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index 1106fca3cd..e549feaded 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -122,34 +122,34 @@ 0 0 - 358 - 464 + 382 + 16 + + + 0 + 0 + + margin-top: 0px; - - - QFormLayout::AllNonFixedFieldsGrow - - - 0 - - - 0 - + - 4 + 0 - 4 + 0 - 4 + 0 - 4 + 0 + + + 0 From 5073439007f22c3384377effef47b2866c6665a6 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Mon, 24 Mar 2014 16:55:29 +0200 Subject: [PATCH 36/53] Highlighted the user's own chat messages. --- interface/src/ui/ChatWindow.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index ab31037f43..2c2c64ac0c 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -200,14 +200,14 @@ void ChatWindow::participantsChanged() { QLabel* userLabel = new QLabel(); userLabel->setText(participantName); userLabel->setStyleSheet("background-color: palette(light);" - "border-radius: 5px;" - "color: #267077;" - "padding-top: 3px;" - "padding-right: 2px;" - "padding-bottom: 2px;" - "padding-left: 2px;" - "border: 1px solid palette(shadow);" - "font-weight: bold"); + "border-radius: 5px;" + "color: #267077;" + "padding-top: 3px;" + "padding-right: 2px;" + "padding-bottom: 2px;" + "padding-left: 2px;" + "border: 1px solid palette(shadow);" + "font-weight: bold"); userLabel->setProperty("user", participantName); userLabel->setCursor(Qt::PointingHandCursor); userLabel->installEventFilter(this); @@ -232,6 +232,11 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { messageLabel->setStyleSheet("padding: 2px; margin-right: 20px"); messageLabel->setAlignment(Qt::AlignTop); + if (getParticipantName(message.from()) == AccountManager::getInstance().getUsername()) { + userLabel->setStyleSheet(userLabel->styleSheet() + "; color: palette(highlight)"); + messageLabel->setStyleSheet(messageLabel->styleSheet() + "; color: palette(highlight)"); + } + ui->messagesGridLayout->addWidget(userLabel, ui->messagesGridLayout->rowCount(), 0, Qt::AlignTop | Qt::AlignRight); ui->messagesGridLayout->addWidget(messageLabel, ui->messagesGridLayout->rowCount() - 1, 1); ui->messagesGridLayout->parentWidget()->updateGeometry(); From e27f1914139edbb0eaffc64fd75787745379264b Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Mon, 24 Mar 2014 17:37:34 +0200 Subject: [PATCH 37/53] Changed the dock widget for the chat so that it replicates the agreed upon design. --- interface/interface_en.ts | 20 +- interface/src/ui/ChatWindow.cpp | 15 +- interface/ui/chatWindow.ui | 315 ++++++++++++++++---------------- 3 files changed, 177 insertions(+), 173 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index e2f9720777..5031083497 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -27,25 +27,25 @@ ChatWindow - - + + Chat - - + + Connecting to XMPP... - - + + online now: - + day %n day @@ -53,7 +53,7 @@ - + hour %n hour @@ -61,7 +61,7 @@ - + minute %n minute @@ -76,7 +76,7 @@ - + %1 online now: diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 2c2c64ac0c..f0da355532 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -30,10 +30,10 @@ ChatWindow::ChatWindow() : ui(new Ui::ChatWindow), numMessagesAfterLastTimeStamp(0) { - QWidget* widget = new QWidget(); - setWidget(widget); - - ui->setupUi(widget); + ui->setupUi(this); + + // remove the title bar (see the Qt docs on setTitleBarWidget) + setTitleBarWidget(new QWidget()); FlowLayout* flowLayout = new FlowLayout(0, 4, 4); ui->usersWidget->setLayout(flowLayout); @@ -42,8 +42,6 @@ ChatWindow::ChatWindow() : ui->messagesGridLayout->setColumnStretch(1, 3); ui->messagePlainTextEdit->installEventFilter(this); - - ui->closeButton->hide(); #ifdef HAVE_QXMPP const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); @@ -78,15 +76,16 @@ ChatWindow::~ChatWindow() { } void ChatWindow::keyPressEvent(QKeyEvent* event) { - QWidget::keyPressEvent(event); + QDockWidget::keyPressEvent(event); if (event->key() == Qt::Key_Escape) { hide(); } } void ChatWindow::showEvent(QShowEvent* event) { - QWidget::showEvent(event); + QDockWidget::showEvent(event); if (!event->spontaneous()) { + activateWindow(); ui->messagePlainTextEdit->setFocus(); } } diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index e549feaded..32ae426484 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -1,7 +1,7 @@ ChatWindow - + 0 @@ -13,180 +13,185 @@ 400 - 0 + 238 - - Chat - font-family: Helvetica, Arial, sans-serif; - - - 0 - - - 8 - - - 8 - - - 8 - - - 8 - - - - - - 0 - 0 - - - - Connecting to XMPP... - - - Qt::AlignCenter - - - - - - - + + QDockWidget::NoDockWidgetFeatures + + + Chat + + + + + 0 + + + 8 + + + 8 + + + 8 + + + 8 + + + + + + 0 + 0 + + + + Connecting to XMPP... + + + Qt::AlignCenter + + + + + + + + + + 0 + 0 + + + + font-weight: bold; color: palette(shadow); margin-bottom: 4px; + + + online now: + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + Qt::NoFocus + + + + + + + :/images/close.svg:/images/close.svg + + + true + + + + + + + + + + + + margin-top: 12px; + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 382 + 16 + + - + 0 0 - font-weight: bold; color: palette(shadow); margin-bottom: 4px; - - - online now: + margin-top: 0px; + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + - - - - - - 0 - 0 - - - - - 16 - 16 - - - - Qt::NoFocus - - - - - - - :/images/close.svg:/images/close.svg - - - true - - - - - - - - - - - - margin-top: 12px; - - - Qt::ScrollBarAlwaysOff - - - true - - - - - 0 - 0 - 382 - 16 - - + + + + - + 0 0 - - margin-top: 0px; + + + 0 + 60 + + + + border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px; + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + true - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - 0 - 0 - - - - - 0 - 60 - - - - border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px; - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - true - - - - + + + messagePlainTextEdit From 4374e28e9cc23258682bdc8bb6b40cfb2e215906 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Tue, 25 Mar 2014 23:41:56 +0200 Subject: [PATCH 38/53] Changed the highlighting of own messages to occupy the whole row. --- interface/src/ui/ChatWindow.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index f0da355532..ae49e82b17 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -220,23 +220,23 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { } QLabel* userLabel = new QLabel(getParticipantName(message.from())); - userLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + userLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); userLabel->setStyleSheet("padding: 2px; font-weight: bold"); - userLabel->setAlignment(Qt::AlignTop); + userLabel->setAlignment(Qt::AlignTop | Qt::AlignRight); QLabel* messageLabel = new QLabel(message.body().replace(regexLinks, "\\1")); messageLabel->setWordWrap(true); messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); messageLabel->setOpenExternalLinks(true); - messageLabel->setStyleSheet("padding: 2px; margin-right: 20px"); - messageLabel->setAlignment(Qt::AlignTop); + messageLabel->setStyleSheet("padding-bottom: 2px; padding-right: 2px; padding-top: 2px; padding-right: 20px"); + messageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); if (getParticipantName(message.from()) == AccountManager::getInstance().getUsername()) { - userLabel->setStyleSheet(userLabel->styleSheet() + "; color: palette(highlight)"); - messageLabel->setStyleSheet(messageLabel->styleSheet() + "; color: palette(highlight)"); + userLabel->setStyleSheet(userLabel->styleSheet() + "; background-color: #e1e8ea"); + messageLabel->setStyleSheet(messageLabel->styleSheet() + "; background-color: #e1e8ea"); } - ui->messagesGridLayout->addWidget(userLabel, ui->messagesGridLayout->rowCount(), 0, Qt::AlignTop | Qt::AlignRight); + ui->messagesGridLayout->addWidget(userLabel, ui->messagesGridLayout->rowCount(), 0); ui->messagesGridLayout->addWidget(messageLabel, ui->messagesGridLayout->rowCount() - 1, 1); ui->messagesGridLayout->parentWidget()->updateGeometry(); Application::processEvents(); From 3907c563df14c25c150d93dab5156f2347c622da Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Wed, 26 Mar 2014 23:44:31 +0200 Subject: [PATCH 39/53] Put the chat window above the 3D view and animated its showing. --- interface/interface_en.ts | 20 ++++++++++---------- interface/src/Menu.cpp | 24 ++++++++++++++++++------ interface/ui/chatWindow.ui | 3 +++ 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 5031083497..56fdd669ed 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -27,20 +27,20 @@ ChatWindow - - + + Chat - - + + Connecting to XMPP... - - + + online now: @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 47c6d040be..229b2e9611 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -1103,13 +1105,23 @@ void Menu::showMetavoxelEditor() { } void Menu::showChat() { + QMainWindow* mainWindow = Application::getInstance()->getWindow(); if (!_chatWindow) { - Application::getInstance()->getWindow()->addDockWidget(Qt::RightDockWidgetArea, _chatWindow = new ChatWindow()); - - } else { - if (!_chatWindow->toggleViewAction()->isChecked()) { - _chatWindow->toggleViewAction()->trigger(); - } + mainWindow->addDockWidget(Qt::NoDockWidgetArea, _chatWindow = new ChatWindow()); + } + if (!_chatWindow->toggleViewAction()->isChecked()) { + int width = _chatWindow->width(); + int y = qMax((mainWindow->height() - _chatWindow->height()) / 2, 0); + _chatWindow->move(mainWindow->width(), y); + _chatWindow->resize(0, _chatWindow->height()); + _chatWindow->toggleViewAction()->trigger(); + + QPropertyAnimation* slideAnimation = new QPropertyAnimation(_chatWindow, "geometry", _chatWindow); + slideAnimation->setStartValue(_chatWindow->geometry()); + slideAnimation->setEndValue(QRect(mainWindow->width() - width, _chatWindow->y(), + width, _chatWindow->height())); + slideAnimation->setDuration(250); + slideAnimation->start(QAbstractAnimation::DeleteWhenStopped); } } diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui index 32ae426484..60a0c6badd 100644 --- a/interface/ui/chatWindow.ui +++ b/interface/ui/chatWindow.ui @@ -22,6 +22,9 @@ QDockWidget::NoDockWidgetFeatures + + Qt::NoDockWidgetArea + Chat From 6fd1bf7edb9941a9f9a0b3ba2721a48d5fb82dbe Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 28 Mar 2014 18:44:06 +0200 Subject: [PATCH 40/53] Simplified the new goToUser function. --- interface/interface_en.ts | 8 +++---- interface/src/Menu.cpp | 41 +++++++++++---------------------- interface/src/Menu.h | 2 +- interface/src/ui/ChatWindow.cpp | 2 +- 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 56fdd669ed..519e2b61c1 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 229b2e9611..e217424cbe 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -20,8 +20,6 @@ #include #include #include -#include -#include #include #include #include @@ -925,19 +923,18 @@ void Menu::goTo() { gotoDialog.resize(gotoDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, gotoDialog.size().height()); int dialogReturn = gotoDialog.exec(); - goToUser(dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty(), - gotoDialog.textValue()); -} - -void Menu::goToUser(bool go, const QString& user) { - if (go) { - LocationManager* manager = &LocationManager::getInstance(); - manager->goTo(user); - connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision); + if (dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty()) { + goToUser(gotoDialog.textValue()); } sendFakeEnterEvent(); } +void Menu::goToUser(const QString& user) { + LocationManager* manager = &LocationManager::getInstance(); + manager->goTo(user); + connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision); +} + void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData) { QMessageBox msgBox; msgBox.setText("Both user and location exists with same name"); @@ -1105,23 +1102,13 @@ void Menu::showMetavoxelEditor() { } void Menu::showChat() { - QMainWindow* mainWindow = Application::getInstance()->getWindow(); if (!_chatWindow) { - mainWindow->addDockWidget(Qt::NoDockWidgetArea, _chatWindow = new ChatWindow()); - } - if (!_chatWindow->toggleViewAction()->isChecked()) { - int width = _chatWindow->width(); - int y = qMax((mainWindow->height() - _chatWindow->height()) / 2, 0); - _chatWindow->move(mainWindow->width(), y); - _chatWindow->resize(0, _chatWindow->height()); - _chatWindow->toggleViewAction()->trigger(); - - QPropertyAnimation* slideAnimation = new QPropertyAnimation(_chatWindow, "geometry", _chatWindow); - slideAnimation->setStartValue(_chatWindow->geometry()); - slideAnimation->setEndValue(QRect(mainWindow->width() - width, _chatWindow->y(), - width, _chatWindow->height())); - slideAnimation->setDuration(250); - slideAnimation->start(QAbstractAnimation::DeleteWhenStopped); + Application::getInstance()->getWindow()->addDockWidget(Qt::RightDockWidgetArea, _chatWindow = new ChatWindow()); + + } else { + if (!_chatWindow->toggleViewAction()->isChecked()) { + _chatWindow->toggleViewAction()->trigger(); + } } } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 41bd1b85fd..c7c4c6ecea 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -122,7 +122,7 @@ public slots: void importSettings(); void exportSettings(); void goTo(); - void goToUser(bool go, const QString& user); + void goToUser(const QString& user); void pasteToVoxel(); void toggleLoginMenuItem(); diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index ae49e82b17..76e9c4ec2d 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -117,7 +117,7 @@ bool ChatWindow::eventFilter(QObject* sender, QEvent* event) { return false; } QString user = sender->property("user").toString(); - Menu::getInstance()->goToUser(true, user); + Menu::getInstance()->goToUser(user); } return false; } From 0c289076098f4a86a4676f5b6379b53a10fe8618 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Mar 2014 10:08:00 -0700 Subject: [PATCH 41/53] fix the hydraMove script to pull you based on head orientation, not body --- examples/hydraMove.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/hydraMove.js b/examples/hydraMove.js index 92c594df9e..6268a38ba3 100644 --- a/examples/hydraMove.js +++ b/examples/hydraMove.js @@ -162,11 +162,11 @@ function flyWithHydra(deltaTime) { if (thrustMultiplier < MAX_THRUST_MULTIPLIER) { thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE); } - var currentOrientation = MyAvatar.orientation; + var headOrientation = MyAvatar.headOrientation; - var front = Quat.getFront(currentOrientation); - var right = Quat.getRight(currentOrientation); - var up = Quat.getUp(currentOrientation); + var front = Quat.getFront(headOrientation); + var right = Quat.getRight(headOrientation); + var up = Quat.getUp(headOrientation); var thrustFront = Vec3.multiply(front, MyAvatar.scale * THRUST_MAG_HAND_JETS * thrustJoystickPosition.y * thrustMultiplier * deltaTime); From 9b43508098cd68db7757f5ad7404975b1f7aaad7 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 28 Mar 2014 19:26:16 +0200 Subject: [PATCH 42/53] Restored code that Git had somehow deleted. --- interface/src/Menu.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e217424cbe..7748c466c7 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1102,13 +1102,23 @@ void Menu::showMetavoxelEditor() { } void Menu::showChat() { + QMainWindow* mainWindow = Application::getInstance()->getWindow(); if (!_chatWindow) { - Application::getInstance()->getWindow()->addDockWidget(Qt::RightDockWidgetArea, _chatWindow = new ChatWindow()); - - } else { - if (!_chatWindow->toggleViewAction()->isChecked()) { - _chatWindow->toggleViewAction()->trigger(); - } + mainWindow->addDockWidget(Qt::NoDockWidgetArea, _chatWindow = new ChatWindow()); + } + if (!_chatWindow->toggleViewAction()->isChecked()) { + int width = _chatWindow->width(); + int y = qMax((mainWindow->height() - _chatWindow->height()) / 2, 0); + _chatWindow->move(mainWindow->width(), y); + _chatWindow->resize(0, _chatWindow->height()); + _chatWindow->toggleViewAction()->trigger(); + + QPropertyAnimation* slideAnimation = new QPropertyAnimation(_chatWindow, "geometry", _chatWindow); + slideAnimation->setStartValue(_chatWindow->geometry()); + slideAnimation->setEndValue(QRect(mainWindow->width() - width, _chatWindow->y(), + width, _chatWindow->height())); + slideAnimation->setDuration(250); + slideAnimation->start(QAbstractAnimation::DeleteWhenStopped); } } From eb35b636518a72f2367a788d7c779495472a2e14 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Mar 2014 10:48:44 -0700 Subject: [PATCH 43/53] use a shared pointer for more elegant cleanup at assignment conclusion --- assignment-client/src/AssignmentClient.cpp | 26 +++++++++++---------- assignment-client/src/AssignmentClient.h | 2 +- assignment-client/src/AssignmentThread.cpp | 16 +++++++++++++ assignment-client/src/AssignmentThread.h | 23 ++++++++++++++++++ libraries/shared/src/ThreadedAssignment.cpp | 9 +++---- libraries/shared/src/ThreadedAssignment.h | 4 +++- 6 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 assignment-client/src/AssignmentThread.cpp create mode 100644 assignment-client/src/AssignmentThread.h diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 970b6518ec..aa20f2ff29 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -18,6 +18,7 @@ #include #include "AssignmentFactory.h" +#include "AssignmentThread.h" #include "AssignmentClient.h" @@ -28,7 +29,7 @@ int hifiSockAddrMeta = qRegisterMetaType("HifiSockAddr"); AssignmentClient::AssignmentClient(int &argc, char **argv) : QCoreApplication(argc, argv), - _currentAssignment(NULL) + _currentAssignment() { setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); @@ -124,7 +125,7 @@ void AssignmentClient::readPendingDatagrams() { if (nodeList->packetVersionAndHashMatch(receivedPacket)) { if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) { // construct the deployed assignment from the packet data - _currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket); + _currentAssignment = SharedAssignmentPointer(AssignmentFactory::unpackAssignment(receivedPacket)); if (_currentAssignment) { qDebug() << "Received an assignment -" << *_currentAssignment; @@ -137,14 +138,13 @@ void AssignmentClient::readPendingDatagrams() { qDebug() << "Destination IP for assignment is" << nodeList->getDomainInfo().getIP().toString(); // start the deployed assignment - QThread* workerThread = new QThread(this); + AssignmentThread* workerThread = new AssignmentThread(_currentAssignment, this); - connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); - - connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); - connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); - connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); - connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); + connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run); + connect(_currentAssignment.data(), &ThreadedAssignment::finished, workerThread, &QThread::quit); + connect(_currentAssignment.data(), &ThreadedAssignment::finished, + this, &AssignmentClient::assignmentCompleted); + connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater); _currentAssignment->moveToThread(workerThread); @@ -153,7 +153,7 @@ void AssignmentClient::readPendingDatagrams() { // let the assignment handle the incoming datagrams for its duration disconnect(&nodeList->getNodeSocket(), 0, this, 0); - connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment, + connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment.data(), &ThreadedAssignment::readPendingDatagrams); // Starts an event loop, and emits workerThread->started() @@ -202,10 +202,12 @@ void AssignmentClient::assignmentCompleted() { NodeList* nodeList = NodeList::getInstance(); // have us handle incoming NodeList datagrams again - disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment, 0); + disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment.data(), 0); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams); - _currentAssignment = NULL; + // clear our current assignment shared pointer now that we're done with it + // if the assignment thread is still around it has its own shared pointer to the assignment + _currentAssignment.clear(); // reset our NodeList by switching back to unassigned and clearing the list nodeList->setOwnerType(NodeType::Unassigned); diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 29187fa3d6..c267c6238b 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -24,7 +24,7 @@ private slots: void handleAuthenticationRequest(); private: Assignment _requestAssignment; - ThreadedAssignment* _currentAssignment; + SharedAssignmentPointer _currentAssignment; }; #endif /* defined(__hifi__AssignmentClient__) */ diff --git a/assignment-client/src/AssignmentThread.cpp b/assignment-client/src/AssignmentThread.cpp new file mode 100644 index 0000000000..dfe093aa7b --- /dev/null +++ b/assignment-client/src/AssignmentThread.cpp @@ -0,0 +1,16 @@ +// +// AssignmentThread.cpp +// hifi +// +// Created by Stephen Birarda on 2014-03-28. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include "AssignmentThread.h" + +AssignmentThread::AssignmentThread(const SharedAssignmentPointer& assignment, QObject* parent) : + QThread(parent), + _assignment(assignment) +{ + +} \ No newline at end of file diff --git a/assignment-client/src/AssignmentThread.h b/assignment-client/src/AssignmentThread.h new file mode 100644 index 0000000000..b55ac10676 --- /dev/null +++ b/assignment-client/src/AssignmentThread.h @@ -0,0 +1,23 @@ +// +// AssignmentThread.h +// hifi +// +// Created by Stephen Birarda on 2014-03-28. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__AssignmentThread__ +#define __hifi__AssignmentThread__ + +#include + +#include + +class AssignmentThread : public QThread { +public: + AssignmentThread(const SharedAssignmentPointer& assignment, QObject* parent); +private: + SharedAssignmentPointer _assignment; +}; + +#endif /* defined(__hifi__AssignmentThread__) */ diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp index be49b18055..fdf2d91c36 100644 --- a/libraries/shared/src/ThreadedAssignment.cpp +++ b/libraries/shared/src/ThreadedAssignment.cpp @@ -20,18 +20,15 @@ ThreadedAssignment::ThreadedAssignment(const QByteArray& packet) : } -void ThreadedAssignment::deleteLater() { - // move the NodeList back to the QCoreApplication instance's thread - NodeList::getInstance()->moveToThread(QCoreApplication::instance()->thread()); - QObject::deleteLater(); -} - void ThreadedAssignment::setFinished(bool isFinished) { _isFinished = isFinished; if (_isFinished) { aboutToFinish(); emit finished(); + + // move the NodeList back to the QCoreApplication instance's thread + NodeList::getInstance()->moveToThread(QCoreApplication::instance()->thread()); } } diff --git a/libraries/shared/src/ThreadedAssignment.h b/libraries/shared/src/ThreadedAssignment.h index 5b78eed56d..f9652dd98d 100644 --- a/libraries/shared/src/ThreadedAssignment.h +++ b/libraries/shared/src/ThreadedAssignment.h @@ -9,6 +9,8 @@ #ifndef __hifi__ThreadedAssignment__ #define __hifi__ThreadedAssignment__ +#include + #include "Assignment.h" class ThreadedAssignment : public Assignment { @@ -22,7 +24,6 @@ public: public slots: /// threaded run of assignment virtual void run() = 0; - virtual void deleteLater(); virtual void readPendingDatagrams() = 0; virtual void sendStatsPacket(); @@ -36,5 +37,6 @@ signals: void finished(); }; +typedef QSharedPointer SharedAssignmentPointer; #endif /* defined(__hifi__ThreadedAssignment__) */ From f9cee2ccfa3b8156564a608b7b7bbf47eeecd3b8 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 28 Mar 2014 22:21:55 +0200 Subject: [PATCH 44/53] Fixed a typo that resulted in a wrong padding of a widget. --- interface/src/ui/ChatWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index 76e9c4ec2d..6963c208c5 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -228,7 +228,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { messageLabel->setWordWrap(true); messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); messageLabel->setOpenExternalLinks(true); - messageLabel->setStyleSheet("padding-bottom: 2px; padding-right: 2px; padding-top: 2px; padding-right: 20px"); + messageLabel->setStyleSheet("padding-bottom: 2px; padding-left: 2px; padding-top: 2px; padding-right: 20px"); messageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); if (getParticipantName(message.from()) == AccountManager::getInstance().getUsername()) { From 4d16547362be06bbafbc2664b24bd1b703034b69 Mon Sep 17 00:00:00 2001 From: Grayson Stebbins Date: Fri, 28 Mar 2014 14:50:26 -0700 Subject: [PATCH 45/53] Note about shortcut for domain dialog (set your domain via Cmnd+D/Cntrl+D) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8a6725ed8..ab1212f656 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Running Interface When you launch interface, you will automatically connect to our default domain: "root.highfidelity.io". If you don't see anything, make sure your preferences are pointing to -root.highfidelity.io, if you still have no luck it's possible our servers are +root.highfidelity.io (set your domain via Cmnd+D/Cntrl+D), if you still have no luck it's possible our servers are simply down; if you're experiencing a major bug, let us know by adding an issue to this repository. Make sure to include details about your computer and how to reproduce the bug. From 27f0214032989e4e8eb32e6cfd0a5a0acb8e5d66 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 28 Mar 2014 23:09:51 -0700 Subject: [PATCH 46/53] first pass audio VU scope --- interface/src/Application.cpp | 106 ++++++++++++++++++++++++---------- interface/src/Application.h | 1 + interface/src/Menu.cpp | 2 +- 3 files changed, 78 insertions(+), 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 98f260c3bf..1974ddd60c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -151,6 +151,7 @@ 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), @@ -2504,56 +2505,101 @@ void Application::displayOverlay() { } } + // Audio Scope + const int AUDIO_SCOPE_Y_OFFSET = 135; if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { - _audio.renderMuteIcon(1, _glWidget->height() - 50); if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { - int oscilloscopeTop = _glWidget->height() - 135; - _audioScope.render(25, oscilloscopeTop); + int oscilloscopeTop = _glWidget->height() - AUDIO_SCOPE_Y_OFFSET; + _audioScope.render(MIRROR_VIEW_LEFT_PADDING, oscilloscopeTop); } } - const int AUDIO_METER_WIDTH = 200; + // Audio VU Meter and Mute Icon + const int MUTE_ICON_SIZE = 24; const int AUDIO_METER_INSET = 2; + const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - AUDIO_METER_INSET; const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET; const int AUDIO_METER_HEIGHT = 8; - const int AUDIO_METER_Y = _glWidget->height() - 40; - const int AUDIO_METER_X = 25; + const int AUDIO_METER_Y_GAP = 8; + const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET; + + int audioMeterY; + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_Y_GAP; + } else { + audioMeterY = AUDIO_METER_Y_GAP; + } + _audio.renderMuteIcon(MIRROR_VIEW_LEFT_PADDING, audioMeterY); + + + const float AUDIO_METER_BLUE[] = {0.0, 0.0, 1.0}; + const float AUDIO_METER_GREEN[] = {0.0, 1.0, 0.0}; + const float AUDIO_METER_RED[] = {1.0, 0.0, 0.0}; + const float AUDIO_GREEN_START = 0.25 * AUDIO_METER_SCALE_WIDTH; + const float AUDIO_RED_START = 0.80 * AUDIO_METER_SCALE_WIDTH; const float CLIPPING_INDICATOR_TIME = 1.0f; + const float AUDIO_METER_AVERAGING = 0.5; const float LOG2 = log(2.f); + const float MAX_LOG2_SAMPLE = 15.f; float audioLevel = 0.f; float loudness = _audio.getLastInputLoudness() + 1.f; - float log2loudness = log(loudness) / LOG2; - if (loudness < 2048) { - audioLevel = (log2loudness / 11.f) * (AUDIO_METER_SCALE_WIDTH / 5.f); - } else { - audioLevel = (log2loudness - 10.f) * (AUDIO_METER_SCALE_WIDTH / 5.f); - } + _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.f - AUDIO_METER_AVERAGING) * loudness; + + float log2loudness = log(_trailingAudioLoudness) / LOG2; + + audioLevel = log2loudness / MAX_LOG2_SAMPLE * AUDIO_METER_SCALE_WIDTH; + bool isClipping = ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); + glBegin(GL_QUADS); if (isClipping) { glColor3f(1, 0, 0); } else { glColor3f(0, 0, 0); } - glBegin(GL_QUADS); - // Draw background Quad - glVertex2i(AUDIO_METER_X, AUDIO_METER_Y); - glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, AUDIO_METER_Y); - glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, AUDIO_METER_Y + AUDIO_METER_HEIGHT); - glVertex2i(AUDIO_METER_X, AUDIO_METER_Y + AUDIO_METER_HEIGHT); - - // Draw Meter Quad - if (isClipping) { - glColor3f(1, 1, 1); - } else { - glColor3f(0, 1, 1); + // Draw audio meter background Quad + glVertex2i(AUDIO_METER_X, audioMeterY); + glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY); + glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY + AUDIO_METER_HEIGHT); + glVertex2i(AUDIO_METER_X, audioMeterY + AUDIO_METER_HEIGHT); + + if (audioLevel > AUDIO_RED_START) { + if (!isClipping) { + glColor3fv(AUDIO_METER_RED); + } else { + glColor3f(1, 1, 1); + } + // Draw Red Quad + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); + audioLevel = AUDIO_RED_START; } - // Draw Meter quad - glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, AUDIO_METER_Y + AUDIO_METER_INSET); - glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, AUDIO_METER_Y + AUDIO_METER_INSET); - glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, AUDIO_METER_Y + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); - glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, AUDIO_METER_Y + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); - + if (audioLevel > AUDIO_GREEN_START) { + if (!isClipping) { + glColor3fv(AUDIO_METER_GREEN); + } else { + glColor3f(1, 1, 1); + } + // Draw Green Quad + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); + audioLevel = AUDIO_GREEN_START; + } + // Draw Blue Quad + if (!isClipping) { + glColor3fv(AUDIO_METER_BLUE); + } else { + glColor3f(1, 1, 1); + } + // Draw Blue (low level) quad + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); glEnd(); if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 15778f2a17..af77adb8fc 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -397,6 +397,7 @@ private: quint64 _lastQueriedTime; Oscilloscope _audioScope; + float _trailingAudioLoudness; OctreeQuery _octreeQuery; // NodeData derived class for querying voxels from voxel server diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7748c466c7..4d93016d4e 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -241,7 +241,7 @@ Menu::Menu() : addDisabledActionAndSeparator(viewMenu, "Stats"); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash); addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, appInstance, SLOT(toggleLogDialog())); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Oscilloscope, 0, true); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Oscilloscope, 0, false); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true); addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails())); addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, this, SLOT(octreeStatsDetails())); From 6880160bc00492ed907ebce06af713ead53ba6b2 Mon Sep 17 00:00:00 2001 From: James Brotchie Date: Mon, 31 Mar 2014 22:08:38 +1000 Subject: [PATCH 47/53] Remember most recently opened script location. Previously when running a script from a file you were always taken back to your desktop directory. Now you will be taken to the location of your most recently opened script. --- interface/interface_en.ts | 8 ++++---- interface/src/Application.cpp | 15 ++++++++++++--- interface/src/Application.h | 2 ++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 519e2b61c1..7c5d1ecbcf 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c6fc522fca..d91e686844 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -169,6 +169,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _bytesPerSecond(0), _recentMaxPackets(0), _resetRecentMaxPacketsSoon(true), + _previousScriptLocation(), _logger(new FileLogger(this)) { // read the ApplicationInfo.ini file for Name/Version/Domain information @@ -3610,12 +3611,20 @@ void Application::loadScript(const QString& scriptName) { } void Application::loadDialog() { - // shut down and stop any existing script - QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); - QString suggestedName = desktopLocation.append("/script.js"); + QString suggestedName; + + if (_previousScriptLocation.isEmpty()) { + QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); + suggestedName = desktopLocation.append("/script.js"); + } else { + suggestedName = _previousScriptLocation; + } QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName, tr("JavaScript Files (*.js)")); + if (!fileNameString.isEmpty()) { + _previousScriptLocation = fileNameString; + } loadScript(fileNameString); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 15778f2a17..bab7578ca4 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -483,6 +483,8 @@ private: ControllerScriptingInterface _controllerScriptingInterface; QPointer _logDialog; + QString _previousScriptLocation; + FileLogger* _logger; void checkVersion(); From 59bc0ffb9dc652a87d191946f736fd6ff65a79da Mon Sep 17 00:00:00 2001 From: James Brotchie Date: Mon, 31 Mar 2014 23:33:17 +1000 Subject: [PATCH 48/53] Update BUILD.md Not sure about anybody else but I needed glm 0.9.5.2 the compile without errors. 0.9.5.0 (as suggested in the current BUILD.md) failed because the precision template parameter was undefined. --- BUILD.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BUILD.md b/BUILD.md index 8f871c3cea..6464f14d08 100644 --- a/BUILD.md +++ b/BUILD.md @@ -3,7 +3,7 @@ Dependencies * [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.11 * [Qt](http://qt-project.org/downloads) ~> 5.2.0 * [zLib](http://www.zlib.net/) ~> 1.2.8 -* [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.0 +* [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.2 * [qxmpp](https://code.google.com/p/qxmpp/) ~> 0.7.6 #####Linux only @@ -142,4 +142,4 @@ If you need to debug Interface, you can run interface from within Visual Studio ####Debugging Interface * In the Solution Explorer, right click interface and click Set as StartUp Project * Set the "Working Directory" for the Interface debugging sessions to the Debug output directory so that your application can load resources. Do this: right click interface and click Properties, choose Debugging from Configuration Properties, set Working Directory to .\Debug -* Now you can run and debug interface through Visual Studio \ No newline at end of file +* Now you can run and debug interface through Visual Studio From da116608a036ca8bb5792a8537a18552d744c860 Mon Sep 17 00:00:00 2001 From: James Brotchie Date: Tue, 1 Apr 2014 07:38:22 +1000 Subject: [PATCH 49/53] Hitting enter no longer shows chat window when chat is disabled. When chat is disabled (QXMPP library not available or XMPP disconnected) hitting enter would show the chat window, even when the Tools -> Chat menu item is grayed out! --- interface/src/Menu.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7748c466c7..ec20401fef 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1102,6 +1102,11 @@ void Menu::showMetavoxelEditor() { } void Menu::showChat() { + if (!_chatAction->isEnabled()) { + // Don't do anything if chat is disabled (No + // QXMPP library or xmpp is disconnected). + return; + } QMainWindow* mainWindow = Application::getInstance()->getWindow(); if (!_chatWindow) { mainWindow->addDockWidget(Qt::NoDockWidgetArea, _chatWindow = new ChatWindow()); From 135d97d0a7500134e39e1b283ed274a0eafb6f74 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 31 Mar 2014 15:59:54 -0700 Subject: [PATCH 50/53] add a pretty important missing line for symmetric socket activation --- libraries/shared/src/NodeList.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 879377a2fd..b725914bfd 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -894,8 +894,7 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con } else if (pingType == PingType::Public && !sendingNode->getActiveSocket()) { sendingNode->activatePublicSocket(); } else if (pingType == PingType::Symmetric && !sendingNode->getActiveSocket()) { - - + sendingNode->activateSymmetricSocket(); } } From f027aac76baa4b37ea178e864c8c9f5c230f78f2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 31 Mar 2014 17:19:04 -0700 Subject: [PATCH 51/53] Fix to model upload in account manager --- libraries/shared/src/AccountManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/AccountManager.cpp b/libraries/shared/src/AccountManager.cpp index 2fe5007ad1..955ba779d1 100644 --- a/libraries/shared/src/AccountManager.cpp +++ b/libraries/shared/src/AccountManager.cpp @@ -144,15 +144,15 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager:: break; case QNetworkAccessManager::PostOperation: case QNetworkAccessManager::PutOperation: - authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); if (dataMultiPart) { if (operation == QNetworkAccessManager::PostOperation) { networkReply = _networkAccessManager->post(authenticatedRequest, dataMultiPart); - dataMultiPart->setParent(networkReply); } else { networkReply = _networkAccessManager->put(authenticatedRequest, dataMultiPart); } + dataMultiPart->setParent(networkReply); } else { + authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); if (operation == QNetworkAccessManager::PostOperation) { networkReply = _networkAccessManager->post(authenticatedRequest, dataByteArray); } else { From 81d1eb7ed264540d670d23cbca081933fc5a873c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 31 Mar 2014 18:24:58 -0700 Subject: [PATCH 52/53] removed unused procedural movement code --- interface/interface_en.ts | 16 ++++++++-------- interface/src/Application.cpp | 10 ---------- interface/src/Application.h | 1 - interface/src/Audio.cpp | 2 -- interface/src/Audio.h | 7 +------ 5 files changed, 9 insertions(+), 27 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 7c5d1ecbcf..a1abb57b52 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dc1a843e47..fa1f4ae0a1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1846,15 +1846,6 @@ void Application::updateDialogs(float deltaTime) { } } -void Application::updateAudio(float deltaTime) { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateAudio()"); - - // Update audio stats for procedural sounds - _audio.setLastAcceleration(_myAvatar->getThrust()); - _audio.setLastVelocity(_myAvatar->getVelocity()); -} - void Application::updateCursor(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateCursor()"); @@ -1903,7 +1894,6 @@ void Application::update(float deltaTime) { updateMetavoxels(deltaTime); // update metavoxels updateCamera(deltaTime); // handle various camera tweaks like off axis projection updateDialogs(deltaTime); // update various stats dialogs if present - updateAudio(deltaTime); // Update audio stats for procedural sounds updateCursor(deltaTime); // Handle cursor updates _particles.update(); // update the particles... diff --git a/interface/src/Application.h b/interface/src/Application.h index fcf8810378..f638d947d9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -312,7 +312,6 @@ private: void updateMetavoxels(float deltaTime); void updateCamera(float deltaTime); void updateDialogs(float deltaTime); - void updateAudio(float deltaTime); void updateCursor(float deltaTime); Avatar* findLookatTargetAvatar(glm::vec3& eyePosition, QUuid &nodeUUID); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index dc34fb2e2d..312a6c5699 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -67,8 +67,6 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p _noiseGateOpen(false), _noiseGateEnabled(true), _noiseGateFramesToClose(0), - _lastVelocity(0), - _lastAcceleration(0), _totalPacketsReceived(0), _collisionSoundMagnitude(0.0f), _collisionSoundFrequency(0.0f), diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 0ffcb77baf..f98e18eb0b 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -51,10 +51,7 @@ public: float getAudioAverageInputLoudness() const { return _lastInputLoudness; } void setNoiseGateEnabled(bool noiseGateEnabled) { _noiseGateEnabled = noiseGateEnabled; } - - void setLastAcceleration(const glm::vec3 lastAcceleration) { _lastAcceleration = lastAcceleration; } - void setLastVelocity(const glm::vec3 lastVelocity) { _lastVelocity = lastVelocity; } - + void setJitterBufferSamples(int samples) { _jitterBufferSamples = samples; } int getJitterBufferSamples() { return _jitterBufferSamples; } @@ -139,8 +136,6 @@ private: bool _noiseGateOpen; bool _noiseGateEnabled; int _noiseGateFramesToClose; - glm::vec3 _lastVelocity; - glm::vec3 _lastAcceleration; int _totalPacketsReceived; float _collisionSoundMagnitude; From 7d12fcee9db07ca8900619c12b22e817b5e457f3 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 31 Mar 2014 21:55:29 -0700 Subject: [PATCH 53/53] 220Hz tone injection option for easier audio quality checks --- interface/src/Audio.cpp | 24 ++++++++++++++++++++---- interface/src/Audio.h | 3 +++ interface/src/Menu.cpp | 6 ++++++ interface/src/Menu.h | 1 + 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 312a6c5699..9f993e653d 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -66,8 +66,10 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p _noiseGateSampleCounter(0), _noiseGateOpen(false), _noiseGateEnabled(true), + _toneInjectionEnabled(false), _noiseGateFramesToClose(0), _totalPacketsReceived(0), + _totalInputAudioSamples(0), _collisionSoundMagnitude(0.0f), _collisionSoundFrequency(0.0f), _collisionSoundNoise(0.0f), @@ -390,7 +392,7 @@ void Audio::handleAudioInput() { inputSamplesRequired, NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL, _inputFormat, _desiredInputFormat); - + // // Impose Noise Gate // @@ -421,13 +423,15 @@ void Audio::handleAudioInput() { const float DC_OFFSET_AVERAGING = 0.99f; const float CLIPPING_THRESHOLD = 0.90f; + // + // Check clipping, adjust DC offset, and check if should open noise gate + // float measuredDcOffset = 0.f; - // Increment the time since the last clip if (_timeSinceLastClip >= 0.0f) { _timeSinceLastClip += (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float) SAMPLE_RATE; } - + for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { measuredDcOffset += monoAudioSamples[i]; monoAudioSamples[i] -= (int16_t) _dcOffset; @@ -489,6 +493,16 @@ void Audio::handleAudioInput() { _lastInputLoudness = 0; } } + // + // Add tone injection if enabled + // + const float TONE_FREQ = 220.f / SAMPLE_RATE * TWO_PI; + const float QUARTER_VOLUME = 8192.f; + if (_toneInjectionEnabled) { + for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { + monoAudioSamples[i] = QUARTER_VOLUME * sinf(TONE_FREQ * (float)(i + _proceduralEffectSample)); + } + } // add input data just written to the scope QMetaObject::invokeMethod(_scope, "addSamples", Qt::QueuedConnection, @@ -683,7 +697,9 @@ void Audio::toggleAudioNoiseReduction() { _noiseGateEnabled = !_noiseGateEnabled; } - +void Audio::toggleToneInjection() { + _toneInjectionEnabled = !_toneInjectionEnabled; +} // Take a pointer to the acquired microphone input samples and add procedural sounds void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) { diff --git a/interface/src/Audio.h b/interface/src/Audio.h index f98e18eb0b..170572a4d7 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -81,6 +81,7 @@ public slots: void reset(); void toggleMute(); void toggleAudioNoiseReduction(); + void toggleToneInjection(); virtual void handleAudioByteArray(const QByteArray& audioByteArray); @@ -135,8 +136,10 @@ private: int _noiseGateSampleCounter; bool _noiseGateOpen; bool _noiseGateEnabled; + bool _toneInjectionEnabled; int _noiseGateFramesToClose; int _totalPacketsReceived; + int _totalInputAudioSamples; float _collisionSoundMagnitude; float _collisionSoundFrequency; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 4d93016d4e..122d4c5654 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -360,6 +360,12 @@ Menu::Menu() : false, appInstance->getAudio(), SLOT(toggleMute())); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioToneInjection, + 0, + false, + appInstance->getAudio(), + SLOT(toggleToneInjection())); + addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel, Qt::CTRL | Qt::SHIFT | Qt::Key_V, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index c7c4c6ecea..206593ce80 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -242,6 +242,7 @@ namespace MenuOption { const QString FilterSixense = "Smooth Sixense Movement"; const QString Enable3DTVMode = "Enable 3DTV Mode"; const QString AudioNoiseReduction = "Audio Noise Reduction"; + const QString AudioToneInjection = "Inject Test Tone"; const QString EchoServerAudio = "Echo Server Audio"; const QString EchoLocalAudio = "Echo Local Audio"; const QString MuteAudio = "Mute Microphone";