From d003ec9c584433a0ecddc5f7ee7890efbe66eec8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 31 Aug 2015 15:49:17 -0700 Subject: [PATCH 01/10] Delete collision sounds (and their threads) after use. This has been broken ever since I added avatar collision sounds around https://github.com/highfidelity/hifi/pull/5159 or so. --- libraries/audio/src/AudioInjector.cpp | 11 +++++++++-- libraries/audio/src/AudioInjector.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 8fd7cb9ce5..716ed5d43e 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -313,7 +313,7 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol QByteArray samples = sound->getByteArray(); if (stretchFactor == 1.0f) { - return playSound(samples, options, NULL); + return playSoundAndDelete(samples, options, NULL); } soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I); @@ -333,9 +333,16 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol qCDebug(audio) << "Unable to resample" << soundUrl << "from" << nInputSamples << "@" << standardRate << "to" << nOutputSamples << "@" << resampledRate; resampled = samples; } - return playSound(resampled, options, NULL); + return playSoundAndDelete(resampled, options, NULL); } +AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) { + AudioInjector* sound = playSound(buffer, options, localInterface); + sound->triggerDeleteAfterFinish(); + return sound; +} + + AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) { QThread* injectorThread = new QThread(); injectorThread->setObjectName("Audio Injector Thread"); diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index d65925b865..0e98fe1682 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -46,6 +46,7 @@ public: void setLocalAudioInterface(AbstractAudioInterface* localAudioInterface) { _localAudioInterface = localAudioInterface; } + static AudioInjector* playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface); static AudioInjector* playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface); static AudioInjector* playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position); From 70ffe10057d5a410b74e8c37eb5f148596bc443e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 31 Aug 2015 14:44:12 -0700 Subject: [PATCH 02/10] Use a constant log file and rollover accumulated logs to a new file based on startup, time or size --- interface/src/FileLogger.cpp | 62 ++++++++++++++++++++++++++++-------- interface/src/FileLogger.h | 2 +- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp index 00da80814b..0b2128cf17 100644 --- a/interface/src/FileLogger.cpp +++ b/interface/src/FileLogger.cpp @@ -10,16 +10,39 @@ // #include "FileLogger.h" -#include "HifiSockAddr.h" -#include -#include -#include -#include -#include -const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt"; -const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; -const QString LOGS_DIRECTORY = "Logs"; +#include +#include +#include +#include + +#include +#include +#include + +#include "HifiSockAddr.h" + +static const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt"; +static const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss"; +static const QString LOGS_DIRECTORY = "Logs"; +// Max log size is 1 MB +static const uint64_t MAX_LOG_SIZE = 1024 * 1024; +// Max log age is 1 hour +static const uint64_t MAX_LOG_AGE_USECS = USECS_PER_SECOND * 3600; + +QString getLogRollerFilename() { + QString result = FileUtils::standardPath(LOGS_DIRECTORY); + QHostAddress clientAddress = getLocalAddress(); + QDateTime now = QDateTime::currentDateTime(); + result.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT))); + return result; +} + +const QString& getLogFilename() { + static QString fileName = FileUtils::standardPath(LOGS_DIRECTORY) + "hifi-log.txt"; + return fileName; +} + class FilePersistThread : public GenericQueueThread < QString > { public: @@ -28,8 +51,22 @@ public: } protected: + void rollFileIfNecessary(QFile& file) { + uint64_t now = usecTimestampNow(); + if ((file.size() > MAX_LOG_SIZE) || (now - _lastRollTime) > MAX_LOG_AGE_USECS) { + QString newFileName = getLogRollerFilename(); + if (file.copy(newFileName)) { + _lastRollTime = now; + file.open(QIODevice::WriteOnly | QIODevice::Truncate); + file.close(); + qDebug() << "Rolled log file: " << newFileName; + } + } + } + virtual bool processQueueItems(const Queue& messages) { QFile file(_logger._fileName); + rollFileIfNecessary(file); if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { QTextStream out(&file); foreach(const QString& message, messages) { @@ -40,20 +77,17 @@ protected: } private: const FileLogger& _logger; + uint64_t _lastRollTime = 0; }; static FilePersistThread* _persistThreadInstance; FileLogger::FileLogger(QObject* parent) : - AbstractLoggerInterface(parent) + AbstractLoggerInterface(parent), _fileName(getLogFilename()) { _persistThreadInstance = new FilePersistThread(*this); _persistThreadInstance->initialize(true, QThread::LowestPriority); - _fileName = FileUtils::standardPath(LOGS_DIRECTORY); - QHostAddress clientAddress = getLocalAddress(); - QDateTime now = QDateTime::currentDateTime(); - _fileName.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT))); } FileLogger::~FileLogger() { diff --git a/interface/src/FileLogger.h b/interface/src/FileLogger.h index 549654ca5c..122da20ae7 100644 --- a/interface/src/FileLogger.h +++ b/interface/src/FileLogger.h @@ -27,7 +27,7 @@ public: virtual void locateLog() override; private: - QString _fileName; + const QString _fileName; friend class FilePersistThread; }; From fa423344305801f7bb1339f5ba02faf2cebcd2d9 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 3 Sep 2015 11:30:44 -0700 Subject: [PATCH 03/10] added userData manipulations to grab script --- examples/controllers/hydra/hydraGrab.js | 35 ++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 71e4d2a07e..1c0a46f661 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -11,9 +11,13 @@ // +Script.include("https://hifi-public.s3.amazonaws.com/scripts/libraries/utils.js"); + var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK"); var rightTriggerAction = RIGHT_HAND_CLICK; +var GRAB_USER_DATA_KEY = "grabKey"; + var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK"); var leftTriggerAction = LEFT_HAND_CLICK; @@ -37,7 +41,7 @@ var INTERSECT_COLOR = { blue: 10 }; -var GRAB_RADIUS = 2; +var GRAB_RADIUS = 0.5; var GRAB_COLOR = { red: 250, @@ -154,7 +158,6 @@ controller.prototype.attemptMove = function() { var newPosition = Vec3.sum(handPosition, Vec3.multiply(direction, this.distanceToEntity)) this.distanceHolding = true; - //TO DO : USE SPRING ACTION UPDATE FOR MOVING if (this.actionID === null) { this.actionID = Entities.addAction("spring", this.grabbedEntity, { targetPosition: newPosition, @@ -184,7 +187,10 @@ controller.prototype.hidePointer = function() { controller.prototype.letGo = function() { - Entities.deleteAction(this.grabbedEntity, this.actionID); + if (this.grabbedEntity && this.actionID) { + this.deactivateEntity(this.grabbedEntity); + Entities.deleteAction(this.grabbedEntity, this.actionID); + } this.grabbedEntity = null; this.actionID = null; this.distanceHolding = false; @@ -264,11 +270,29 @@ controller.prototype.checkForInRangeObject = function() { if (grabbedEntity === null) { return false; } else { + //We are grabbing an entity, so let it know we've grabbed it this.grabbedEntity = grabbedEntity; + this.activateEntity(this.grabbedEntity); + return true; } } +controller.prototype.activateEntity = function(entity) { + var data = { + activated: true, + avatarId: MyAvatar.sessionUUID + }; + setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); +} + +controller.prototype.deactivateEntity = function(entity) { + var data = { + activated: false, + avatarId: null + }; + setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); +} controller.prototype.onActionEvent = function(action, state) { if (this.pullAction === action && state === 1) { @@ -293,7 +317,9 @@ controller.prototype.onActionEvent = function(action, state) { controller.prototype.cleanup = function() { Entities.deleteEntity(this.pointer); - Entities.deleteAction(this.grabbedEntity, this.actionID); + if (this.grabbedEntity) { + Entities.deleteAction(this.grabbedEntity, this.actionID); + } } function update() { @@ -314,6 +340,7 @@ function cleanup() { } + Script.scriptEnding.connect(cleanup); Script.update.connect(update) Controller.actionEvent.connect(onActionEvent); \ No newline at end of file From 8dc390576710f11d59b9ba6e9d5da70c2da8b73f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 27 Aug 2015 17:15:02 -0700 Subject: [PATCH 04/10] Add common vector constants to JS, add some missing quaternion functionality --- libraries/script-engine/src/Quat.cpp | 20 ++++++++++ libraries/script-engine/src/Quat.h | 4 ++ libraries/script-engine/src/Vec3.cpp | 54 ++++--------------------- libraries/script-engine/src/Vec3.h | 59 ++++++++++++++++++---------- libraries/shared/src/GLMHelpers.cpp | 2 + libraries/shared/src/GLMHelpers.h | 3 +- 6 files changed, 74 insertions(+), 68 deletions(-) diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 9cd659dd5d..14a6573415 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -18,6 +18,26 @@ #include "ScriptEngineLogging.h" #include "Quat.h" +quat Quat::normalize(const glm::quat& q) { + return glm::normalize(q); +} + +glm::quat Quat::rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { + return ::rotationBetween(v1, v2); +} + +glm::quat Quat::lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up) { + return glm::quat_cast(glm::lookAt(eye, center, up)); +} + +glm::quat Quat::lookAtSimple(const glm::vec3& eye, const glm::vec3& center) { + auto dir = glm::normalize(center - eye); + // if the direction is nearly aligned with the Y axis, then use the X axis for 'up' + if (dir.x < 0.001f && dir.z < 0.001f) { + return lookAt(eye, center, Vectors::UNIT_X); + } + return lookAt(eye, center, Vectors::UNIT_Y); +} glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) { return q1 * q2; diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 1f130c57c7..543c93401f 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -25,6 +25,10 @@ class Quat : public QObject { public slots: glm::quat multiply(const glm::quat& q1, const glm::quat& q2); + glm::quat normalize(const glm::quat& q); + glm::quat lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up); + glm::quat lookAtSimple(const glm::vec3& eye, const glm::vec3& center); + glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); glm::quat fromVec3Degrees(const glm::vec3& vec3); // degrees glm::quat fromVec3Radians(const glm::vec3& vec3); // radians glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll); // degrees diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index 4ed16b2ef0..69961bfd8e 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -13,66 +13,26 @@ #include +#include + #include "ScriptEngineLogging.h" #include "NumericalConstants.h" #include "Vec3.h" -glm::vec3 Vec3::reflect(const glm::vec3& v1, const glm::vec3& v2) { - return glm::reflect(v1, v2); -} -glm::vec3 Vec3::cross(const glm::vec3& v1, const glm::vec3& v2) { - return glm::cross(v1,v2); -} - -float Vec3::dot(const glm::vec3& v1, const glm::vec3& v2) { - return glm::dot(v1,v2); -} - -glm::vec3 Vec3::multiply(const glm::vec3& v1, float f) { - return v1 * f; -} - -glm::vec3 Vec3::multiply(float f, const glm::vec3& v1) { - return v1 * f; -} - -glm::vec3 Vec3::multiplyQbyV(const glm::quat& q, const glm::vec3& v) { - return q * v; -} - -glm::vec3 Vec3::sum(const glm::vec3& v1, const glm::vec3& v2) { - return v1 + v2; -} -glm::vec3 Vec3::subtract(const glm::vec3& v1, const glm::vec3& v2) { - return v1 - v2; -} - -float Vec3::length(const glm::vec3& v) { - return glm::length(v); -} - -float Vec3::distance(const glm::vec3& v1, const glm::vec3& v2) { - return glm::distance(v1, v2); -} float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3) { - return glm::degrees(glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3))); + float radians = glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3)); + return glm::degrees(radians); } -glm::vec3 Vec3::normalize(const glm::vec3& v) { - return glm::normalize(v); -} - -glm::vec3 Vec3::mix(const glm::vec3& v1, const glm::vec3& v2, float m) { - return glm::mix(v1, v2, m); -} void Vec3::print(const QString& lable, const glm::vec3& v) { qCDebug(scriptengine) << qPrintable(lable) << v.x << "," << v.y << "," << v.z; } -bool Vec3::equal(const glm::vec3& v1, const glm::vec3& v2) { - return v1 == v2; +bool Vec3::withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon) { + float distanceSquared = glm::length2(v1 - v2); + return (epsilon*epsilon) >= distanceSquared; } glm::vec3 Vec3::toPolar(const glm::vec3& v) { diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 82062ca80d..b05e729a49 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -11,40 +11,59 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#pragma once #ifndef hifi_Vec3_h #define hifi_Vec3_h -#include -#include +#include +#include -#include -#include +#include "GLMHelpers.h" /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API class Vec3 : public QObject { Q_OBJECT public slots: - glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2); - glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2); - float dot(const glm::vec3& v1, const glm::vec3& v2); - glm::vec3 multiply(const glm::vec3& v1, float f); - glm::vec3 multiply(float, const glm::vec3& v1); - glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v); - glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2); - glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2); - float length(const glm::vec3& v); - float distance(const glm::vec3& v1, const glm::vec3& v2); + glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2) { return glm::reflect(v1, v2); } + glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2) { return glm::cross(v1, v2); } + float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); } + glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; } + glm::vec3 multiply(float f, const glm::vec3& v1) { return v1 * f; } + glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; } + glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; } + glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2) { return v1 - v2; } + float length(const glm::vec3& v) { return glm::length(v); } + float distance(const glm::vec3& v1, const glm::vec3& v2) { return glm::distance(v1, v2); } float orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3); - glm::vec3 normalize(const glm::vec3& v); - glm::vec3 mix(const glm::vec3& v1, const glm::vec3& v2, float m); - void print(const QString& lable, const glm::vec3& v); - bool equal(const glm::vec3& v1, const glm::vec3& v2); + glm::vec3 normalize(const glm::vec3& v) { return glm::normalize(v); }; + glm::vec3 mix(const glm::vec3& v1, const glm::vec3& v2, float m) { return glm::mix(v1, v2, m); } + void print(const QString& label, const glm::vec3& v); + bool equal(const glm::vec3& v1, const glm::vec3& v2) { return v1 == v2; } + bool withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon); + // FIXME misnamed, should be 'spherical' or 'euler' depending on the implementation glm::vec3 toPolar(const glm::vec3& v); glm::vec3 fromPolar(const glm::vec3& polar); glm::vec3 fromPolar(float elevation, float azimuth); + const glm::vec3& UNIT_X() { return Vectors::UNIT_X; } + const glm::vec3& UNIT_Y() { return Vectors::UNIT_Y; } + const glm::vec3& UNIT_Z() { return Vectors::UNIT_Z; } + const glm::vec3& UNIT_NEG_X() { return Vectors::UNIT_NEG_X; } + const glm::vec3& UNIT_NEG_Y() { return Vectors::UNIT_NEG_Y; } + const glm::vec3& UNIT_NEG_Z() { return Vectors::UNIT_NEG_Z; } + const glm::vec3& UNIT_XY() { return Vectors::UNIT_XY; } + const glm::vec3& UNIT_XZ() { return Vectors::UNIT_XZ; } + const glm::vec3& UNIT_YZ() { return Vectors::UNIT_YZ; } + const glm::vec3& UNIT_XYZ() { return Vectors::UNIT_XYZ; } + const glm::vec3& FLOAT_MAX() { return Vectors::MAX; } + const glm::vec3& FLOAT_MIN() { return Vectors::MIN; } + const glm::vec3& ZERO() { return Vectors::ZERO; } + const glm::vec3& ONE() { return Vectors::ONE; } + const glm::vec3& TWO() { return Vectors::TWO; } + const glm::vec3& HALF() { return Vectors::HALF; } + const glm::vec3& RIGHT() { return Vectors::RIGHT; } + const glm::vec3& UP() { return Vectors::UNIT_X; } + const glm::vec3& FRONT() { return Vectors::FRONT; } }; - - #endif // hifi_Vec3_h diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 4ca8ed330b..4f2124a343 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -27,6 +27,8 @@ const vec3 Vectors::MAX{ FLT_MAX }; const vec3 Vectors::MIN{ -FLT_MAX }; const vec3 Vectors::ZERO{ 0.0f }; const vec3 Vectors::ONE{ 1.0f }; +const vec3 Vectors::TWO{ 2.0f }; +const vec3 Vectors::HALF{ 0.5f }; const vec3& Vectors::RIGHT = Vectors::UNIT_X; const vec3& Vectors::UP = Vectors::UNIT_Y; const vec3& Vectors::FRONT = Vectors::UNIT_NEG_Z; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 4b03ed2525..a35cdfbfb3 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -64,12 +64,13 @@ public: static const vec3 UNIT_XY; static const vec3 UNIT_XZ; static const vec3 UNIT_YZ; - static const vec3 UNIT_ZX; static const vec3 UNIT_XYZ; static const vec3 MAX; static const vec3 MIN; static const vec3 ZERO; static const vec3 ONE; + static const vec3 TWO; + static const vec3 HALF; static const vec3& RIGHT; static const vec3& UP; static const vec3& FRONT; From 36ca789d9243291061ac5bda5c7a823c36a874d4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 3 Sep 2015 14:08:04 -0700 Subject: [PATCH 05/10] fix differential avatar-joint sending to work if more than 2 avatars are in the domain --- assignment-client/src/avatars/AvatarMixer.cpp | 26 ++++++++++++++++++- interface/src/avatar/MyAvatar.cpp | 6 ++--- interface/src/avatar/MyAvatar.h | 2 +- libraries/avatars/src/AvatarData.cpp | 23 +++++++++++----- libraries/avatars/src/AvatarData.h | 4 ++- libraries/script-engine/src/ScriptEngine.cpp | 3 ++- 6 files changed, 51 insertions(+), 13 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index eefb654737..685e95bbd7 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -295,7 +295,8 @@ void AvatarMixer::broadcastAvatarData() { avatarPacketList.startSegment(); numAvatarDataBytes += avatarPacketList.write(otherNode->getUUID().toRfc4122()); - numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray(false)); + numAvatarDataBytes += + avatarPacketList.write(otherAvatar.toByteArray(false, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO)); avatarPacketList.endSegment(); @@ -361,6 +362,29 @@ void AvatarMixer::broadcastAvatarData() { } else { nodeData->setMaxAvatarDistance(maxAvatarDistanceThisFrame); } + + // We're done encoding this version of the otherAvatars. Update their "lastSent" joint-states so + // that we can notice differences, next time around. + nodeList->eachMatchingNode( + [&](const SharedNodePointer& otherNode)->bool { + if (!otherNode->getLinkedData()) { + return false; + } + if (otherNode->getUUID() == node->getUUID()) { + return false; + } + + return true; + }, + [&](const SharedNodePointer& otherNode) { + AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + MutexTryLocker lock(otherNodeData->getMutex()); + if (!lock.isLocked()) { + return; + } + AvatarData& otherAvatar = otherNodeData->getAvatar(); + otherAvatar.doneEncoding(); + }); } ); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6939720f32..73ff728747 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -123,18 +123,18 @@ MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset(); } -QByteArray MyAvatar::toByteArray(bool cullSmallChanges) { +QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { CameraMode mode = Application::getInstance()->getCamera()->getMode(); if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { // fake the avatar position that is sent up to the AvatarMixer glm::vec3 oldPosition = _position; _position = getSkeletonPosition(); - QByteArray array = AvatarData::toByteArray(cullSmallChanges); + QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll); // copy the correct position back _position = oldPosition; return array; } - return AvatarData::toByteArray(cullSmallChanges); + return AvatarData::toByteArray(cullSmallChanges, sendAll); } void MyAvatar::reset() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 89f3b236f4..34e045076d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -200,7 +200,7 @@ private: glm::vec3 getWorldBodyPosition() const; glm::quat getWorldBodyOrientation() const; - QByteArray toByteArray(bool cullSmallChanges); + QByteArray toByteArray(bool cullSmallChanges, bool sendAll); void simulate(float deltaTime); void updateFromTrackers(float deltaTime); virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 123c9707ba..853da6dbe5 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -145,7 +145,7 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) { _handPosition = glm::inverse(getOrientation()) * (handPosition - _position); } -QByteArray AvatarData::toByteArray(bool cullSmallChanges) { +QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { // TODO: DRY this up to a shared method // that can pack any type given the number of bytes // and return the number of bytes to push the pointer @@ -244,11 +244,12 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges) { _lastSentJointData.resize(_jointData.size()); - // foreach (const JointData& data, _jointData) { for (int i=0; i < _jointData.size(); i++) { const JointData& data = _jointData.at(i); - if (_lastSentJointData[i].rotation != data.rotation) { - if (!cullSmallChanges || fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= MIN_ROTATION_DOT) { + if (sendAll || _lastSentJointData[i].rotation != data.rotation) { + if (sendAll || + !cullSmallChanges || + fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= MIN_ROTATION_DOT) { validity |= (1 << validityBit); } } @@ -267,7 +268,6 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges) { const JointData& data = _jointData[ i ]; if (validity & (1 << validityBit)) { destinationBuffer += packOrientationQuatToBytes(destinationBuffer, data.rotation); - _lastSentJointData[i].rotation = data.rotation; } if (++validityBit == BITS_IN_BYTE) { validityBit = 0; @@ -278,6 +278,16 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges) { return avatarDataByteArray.left(destinationBuffer - startPosition); } +void AvatarData::doneEncoding() { + // The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData. + _lastSentJointData.resize(_jointData.size()); + for (int i = 0; i < _jointData.size(); i ++) { + const JointData& data = _jointData[ i ]; + _lastSentJointData[i].rotation = data.rotation; + } +} + + bool AvatarData::shouldLogError(const quint64& now) { if (now > _errorLogExpiry) { _errorLogExpiry = now + DEFAULT_FILTERED_LOG_EXPIRY; @@ -1083,7 +1093,8 @@ void AvatarData::setJointMappingsFromNetworkReply() { void AvatarData::sendAvatarDataPacket() { auto nodeList = DependencyManager::get(); - QByteArray avatarByteArray = toByteArray(true); + QByteArray avatarByteArray = toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); + doneEncoding(); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 278ec2047c..fe6c737a6e 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -111,6 +111,7 @@ const int AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS = 5000; // See also static AvatarData::defaultFullAvatarModelUrl(). const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default"); +const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02; // Where one's own Avatar begins in the world (will be overwritten if avatar data file is found). // This is the start location in the Sandbox (xyz: 6270, 211, 6000). @@ -171,7 +172,8 @@ public: glm::vec3 getHandPosition() const; void setHandPosition(const glm::vec3& handPosition); - virtual QByteArray toByteArray(bool cullSmallChanges); + virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll); + virtual void doneEncoding(); /// \return true if an error should be logged bool shouldLogError(const quint64& now); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index aa395b1b06..ced0742a51 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -599,7 +599,8 @@ void ScriptEngine::run() { / (1000 * 1000)) + 0.5); const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t); - QByteArray avatarByteArray = _avatarData->toByteArray(true); + QByteArray avatarByteArray = _avatarData->toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); + _avatarData->doneEncoding(); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); From ebe5818c84080abbdc1b5b8edded34710135c9e3 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 3 Sep 2015 14:09:01 -0700 Subject: [PATCH 06/10] relative path --- examples/controllers/hydra/hydraGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 1c0a46f661..bd060ecc14 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -11,7 +11,7 @@ // -Script.include("https://hifi-public.s3.amazonaws.com/scripts/libraries/utils.js"); +Script.include("../../libraries/utils.js"); var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK"); var rightTriggerAction = RIGHT_HAND_CLICK; From 44c554895d3bb8788d4906a7c09733916b030b35 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 3 Sep 2015 14:33:25 -0700 Subject: [PATCH 07/10] next wave of breakdance work --- examples/libraries/omniTool.js | 4 + .../modules/breakdanceOmniToolModule.js | 35 ++ examples/libraries/utils.js | 27 +- .../breakdanceCore.js} | 378 ++++++++---------- examples/toys/breakdanceToy.js | 17 + 5 files changed, 258 insertions(+), 203 deletions(-) create mode 100644 examples/libraries/omniTool/modules/breakdanceOmniToolModule.js rename examples/{controllers/breakdanceToy.js => toys/breakdanceCore.js} (73%) create mode 100644 examples/toys/breakdanceToy.js diff --git a/examples/libraries/omniTool.js b/examples/libraries/omniTool.js index c9f041d672..724f30c548 100644 --- a/examples/libraries/omniTool.js +++ b/examples/libraries/omniTool.js @@ -65,6 +65,10 @@ OmniTool = function(side) { }); } +OmniTool.prototype.onCleanup = function(action) { + this.unloadModule(); +} + OmniTool.prototype.onActionEvent = function(action, state) { // FIXME figure out the issues when only one spatial controller is active // logDebug("Action: " + action + " " + state); diff --git a/examples/libraries/omniTool/modules/breakdanceOmniToolModule.js b/examples/libraries/omniTool/modules/breakdanceOmniToolModule.js new file mode 100644 index 0000000000..36ee6b1fee --- /dev/null +++ b/examples/libraries/omniTool/modules/breakdanceOmniToolModule.js @@ -0,0 +1,35 @@ +// +// breakdanceOmniToolModule.js +// examples/libraries/omniTool/modules +// +// This is an omniTool module version of the breakdance game +// +// Created by Brad Hefta-Gaub on Sept 3, 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../toys/breakdanceCore.js"); + +OmniToolModules.Breakdance = function() { + print("OmniToolModules.Breakdance..."); +} + +OmniToolModules.Breakdance.prototype.onLoad = function(deltaTime) { + print("OmniToolModules.Breakdance.prototype.onLoad()..."); + breakdanceStart(); + } + +OmniToolModules.Breakdance.prototype.onUpdate = function(deltaTime) { + print("OmniToolModules.Breakdance.prototype.onUpdate()..."); + breakdanceUpdate(); +} + + OmniToolModules.Breakdance.prototype.onUnload = function() { + print("OmniToolModules.Breakdance.prototype.onUnload()..."); + breakdanceEnd(); +} + +OmniToolModuleType = "Breakdance"; \ No newline at end of file diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index 6e6012cfe3..1c2816e7a0 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -101,4 +101,29 @@ logInfo = function(str) { logDebug = function(str) { print(str); -} \ No newline at end of file +} + +// Computes the penetration between a point and a sphere (centered at the origin) +// if point is inside sphere: returns true and stores the result in 'penetration' +// (the vector that would move the point outside the sphere) +// otherwise returns false +findSphereHit = function(point, sphereRadius) { + var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations + var vectorLength = Vec3.length(point); + if (vectorLength < EPSILON) { + return true; + } + var distance = vectorLength - sphereRadius; + if (distance < 0.0) { + return true; + } + return false; +} + +findSpherePointHit = function(sphereCenter, sphereRadius, point) { + return findSphereHit(Vec3.subtract(point,sphereCenter), sphereRadius); +} + +findSphereSphereHit = function(firstCenter, firstRadius, secondCenter, secondRadius) { + return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter); +} diff --git a/examples/controllers/breakdanceToy.js b/examples/toys/breakdanceCore.js similarity index 73% rename from examples/controllers/breakdanceToy.js rename to examples/toys/breakdanceCore.js index db3b418c01..0f346537eb 100644 --- a/examples/controllers/breakdanceToy.js +++ b/examples/toys/breakdanceCore.js @@ -1,7 +1,8 @@ // -// breakdanceToy.js -// examples +// breakdanceCore.js +// examples/toys // +// This is the core breakdance game library, it can be used as part of an entity script, or an omniTool module, or bootstapped on it's own // Created by Brad Hefta-Gaub on August 24, 2015 // Copyright 2015 High Fidelity, Inc. // @@ -9,34 +10,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; - -// helpers -// Computes the penetration between a point and a sphere (centered at the origin) -// if point is inside sphere: returns true and stores the result in 'penetration' -// (the vector that would move the point outside the sphere) -// otherwise returns false -function findSphereHit(point, sphereRadius) { - var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations - var vectorLength = Vec3.length(point); - if (vectorLength < EPSILON) { - return true; - } - var distance = vectorLength - sphereRadius; - if (distance < 0.0) { - return true; - } - return false; -} - -function findSpherePointHit(sphereCenter, sphereRadius, point) { - return findSphereHit(Vec3.subtract(point,sphereCenter), sphereRadius); -} - -function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadius) { - return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter); -} +Script.include("../libraries/utils.js"); function getPositionPuppet() { var DISTANCE_IN_FRONT = 2; @@ -245,13 +220,18 @@ function getPositionRightOnBase() { } -// We will also demonstrate some 3D overlays. We will create a couple of cubes, spheres, and lines -// our 3D cube that moves around... -var handSize = 0.25; -var leftCubePosition = MyAvatar.getLeftPalmPosition(); -var rightCubePosition = MyAvatar.getRightPalmPosition(); +// some globals we will need access to +var HAND_SIZE = 0.25; +var TARGET_SIZE = 0.3; +var TARGET_COLOR = { red: 128, green: 128, blue: 128}; +var TARGET_COLOR_HIT = { red: 0, green: 255, blue: 0}; -var text = Overlays.addOverlay("text", { +var textOverlay, leftHandOverlay, rightHandOverlay, + leftOnBaseOverlay, leftLoweredOverlay, leftOverheadOverlay, leftSideOverlay, leftFrontOverlay, + rightOnBaseOverlay, rightLoweredOverlay, rightOverheadOverlay, rightSideOverlay, rightFrontOverlay; + +function createOverlays() { + textOverlay = Overlays.addOverlay("text", { x: 100, y: 300, width: 900, @@ -265,31 +245,110 @@ var text = Overlays.addOverlay("text", { backgroundAlpha: 0.5 }); -var leftHand= Overlays.addOverlay("cube", { - position: leftCubePosition, - size: handSize, + leftHandOverlay = Overlays.addOverlay("cube", { + position: MyAvatar.getLeftPalmPosition(), + size: HAND_SIZE, color: { red: 0, green: 0, blue: 255}, alpha: 1, solid: false }); -var rightHand= Overlays.addOverlay("cube", { - position: rightCubePosition, - size: handSize, + rightHandOverlay = Overlays.addOverlay("cube", { + position: MyAvatar.getRightPalmPosition(), + size: HAND_SIZE, color: { red: 255, green: 0, blue: 0}, alpha: 1, solid: false }); + leftOnBaseOverlay = Overlays.addOverlay("cube", { + position: getPositionLeftOnBase(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); -var targetSize = 0.3; -var targetColor = { red: 128, green: 128, blue: 128}; -var targetColorHit = { red: 0, green: 255, blue: 0}; -var moveCycleColor = { red: 255, green: 255, blue: 0}; + + leftLoweredOverlay = Overlays.addOverlay("cube", { + position: getPositionLeftLowered(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + + leftOverheadOverlay = Overlays.addOverlay("cube", { + position: getPositionLeftOverhead(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + leftSideOverlay = Overlays.addOverlay("cube", { + position: getPositionLeftSide(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + + leftFrontOverlay = Overlays.addOverlay("cube", { + position: getPositionLeftFront(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + rightOnBaseOverlay = Overlays.addOverlay("cube", { + position: getPositionRightOnBase(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + rightLoweredOverlay = Overlays.addOverlay("cube", { + position: getPositionRightLowered(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + + rightOverheadOverlay = Overlays.addOverlay("cube", { + position: getPositionRightOverhead(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + rightSideOverlay = Overlays.addOverlay("cube", { + position: getPositionRightSide(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); + + + rightFrontOverlay = Overlays.addOverlay("cube", { + position: getPositionRightFront(), + size: TARGET_SIZE, + color: TARGET_COLOR, + alpha: 1, + solid: false + }); +} var TEMPORARY_LIFETIME = 60; - -var animationSettings = JSON.stringify({ +var ANIMATION_SETTINGS = JSON.stringify({ fps: 30, running: true, loop: true, @@ -297,107 +356,22 @@ var animationSettings = JSON.stringify({ lastFrame: 10000 }); -var naturalDimensions = { x: 1.63, y: 1.67, z: 0.31 }; -var dimensions = Vec3.multiply(naturalDimensions, 0.3); +var NATURAL_DIMENSIONS = { x: 1.63, y: 1.67, z: 0.31 }; +var DIMENSIONS = Vec3.multiply(NATURAL_DIMENSIONS, 0.3); +var puppetEntityID; -var puppetEntityID = Entities.addEntity({ +function createPuppet() { + puppetEntityID = Entities.addEntity({ type: "Model", modelURL: "https://hifi-public.s3.amazonaws.com/models/Bboys/bboy1/bboy1.fbx", animationURL: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx", - animationSettings: animationSettings, + animationSettings: ANIMATION_SETTINGS, position: getPositionPuppet(), ignoreForCollisions: true, - dimensions: dimensions, + dimensions: DIMENSIONS, lifetime: TEMPORARY_LIFETIME }); - -var leftOnBase = Overlays.addOverlay("cube", { - position: getPositionLeftOnBase(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - - -var leftLowered = Overlays.addOverlay("cube", { - position: getPositionLeftLowered(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - - -var leftOverhead = Overlays.addOverlay("cube", { - position: getPositionLeftOverhead(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - -var leftSide= Overlays.addOverlay("cube", { - position: getPositionLeftSide(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - - -var leftFront= Overlays.addOverlay("cube", { - position: getPositionLeftFront(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - -var rightOnBase = Overlays.addOverlay("cube", { - position: getPositionRightOnBase(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - -var rightLowered = Overlays.addOverlay("cube", { - position: getPositionRightLowered(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - - -var rightOverhead = Overlays.addOverlay("cube", { - position: getPositionRightOverhead(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - -var rightSide= Overlays.addOverlay("cube", { - position: getPositionRightSide(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - - -var rightFront= Overlays.addOverlay("cube", { - position: getPositionRightFront(), - size: targetSize, - color: targetColor, - alpha: 1, - solid: false - }); - -var startDate = new Date(); -var lastTime = startDate.getTime(); +} var NO_POSE = 0; var LEFT_ON_BASE = 1; @@ -411,8 +385,6 @@ var RIGHT_LOWERED = 128; var RIGHT_SIDE = 256; var RIGHT_FRONT = 512; -var lastPoseValue = NO_POSE; - //http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx //http://s3.amazonaws.com/hifi-public/animations/Breakdancing/bboy_pose_to_idle.fbx //http://s3.amazonaws.com/hifi-public/animations/Breakdancing/bboy_uprock.fbx @@ -461,8 +433,6 @@ poses[LEFT_ON_BASE + RIGHT_LOWERED ] = { name: "Left On Base + Right Lowered" poses[LEFT_ON_BASE + RIGHT_SIDE ] = { name: "Left On Base + Right Side", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" }; poses[LEFT_ON_BASE + RIGHT_FRONT ] = { name: "Left On Base + Right Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" }; - - poses[LEFT_OVERHEAD + RIGHT_OVERHEAD ] = { name: "Left Overhead + Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/bboy_uprock.fbx" }; poses[LEFT_LOWERED + RIGHT_OVERHEAD ] = { name: "Left Lowered + Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_1.fbx" }; poses[LEFT_SIDE + RIGHT_OVERHEAD ] = { name: "Left Side + Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_2.fbx" }; @@ -484,43 +454,46 @@ poses[LEFT_SIDE + RIGHT_FRONT ] = { name: "Left Side + Right Front", poses[LEFT_FRONT + RIGHT_FRONT ] = { name: "Left Front + Right Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock_var_1_end.fbx" }; -Script.update.connect(function(deltaTime) { - var date= new Date(); - var now= date.getTime(); - var elapsed = now - lastTime; - var inMoveCycle = false; +breakdanceStart = function() { + print("breakdanceStart..."); + createOverlays(); + createPuppet(); +} + +breakdanceUpdate = function(deltaTime) { + //print("breakdanceUpdate..."); var leftHandPos = MyAvatar.getLeftPalmPosition(); var rightHandPos = MyAvatar.getRightPalmPosition(); - Overlays.editOverlay(leftHand, { position: leftHandPos } ); - Overlays.editOverlay(rightHand, { position: rightHandPos } ); + Overlays.editOverlay(leftHandOverlay, { position: leftHandPos } ); + Overlays.editOverlay(rightHandOverlay, { position: rightHandPos } ); - var hitTargetLeftOnBase = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftOnBase(), targetSize/2); - var hitTargetLeftOverhead = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftOverhead(), targetSize/2); - var hitTargetLeftLowered = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftLowered(), targetSize/2); - var hitTargetLeftSide = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftSide(), targetSize/2); - var hitTargetLeftFront = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftFront(), targetSize/2); + var hitTargetLeftOnBase = findSphereSphereHit(leftHandPos, HAND_SIZE/2, getPositionLeftOnBase(), TARGET_SIZE/2); + var hitTargetLeftOverhead = findSphereSphereHit(leftHandPos, HAND_SIZE/2, getPositionLeftOverhead(), TARGET_SIZE/2); + var hitTargetLeftLowered = findSphereSphereHit(leftHandPos, HAND_SIZE/2, getPositionLeftLowered(), TARGET_SIZE/2); + var hitTargetLeftSide = findSphereSphereHit(leftHandPos, HAND_SIZE/2, getPositionLeftSide(), TARGET_SIZE/2); + var hitTargetLeftFront = findSphereSphereHit(leftHandPos, HAND_SIZE/2, getPositionLeftFront(), TARGET_SIZE/2); - var hitTargetRightOnBase = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightOnBase(), targetSize/2); - var hitTargetRightOverhead = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightOverhead(), targetSize/2); - var hitTargetRightLowered = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightLowered(), targetSize/2); - var hitTargetRightSide = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightSide(), targetSize/2); - var hitTargetRightFront = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightFront(), targetSize/2); + var hitTargetRightOnBase = findSphereSphereHit(rightHandPos, HAND_SIZE/2, getPositionRightOnBase(), TARGET_SIZE/2); + var hitTargetRightOverhead = findSphereSphereHit(rightHandPos, HAND_SIZE/2, getPositionRightOverhead(), TARGET_SIZE/2); + var hitTargetRightLowered = findSphereSphereHit(rightHandPos, HAND_SIZE/2, getPositionRightLowered(), TARGET_SIZE/2); + var hitTargetRightSide = findSphereSphereHit(rightHandPos, HAND_SIZE/2, getPositionRightSide(), TARGET_SIZE/2); + var hitTargetRightFront = findSphereSphereHit(rightHandPos, HAND_SIZE/2, getPositionRightFront(), TARGET_SIZE/2); // determine target colors - var targetColorLeftOnBase = hitTargetLeftOnBase ? targetColorHit : targetColor; - var targetColorLeftOverhead = hitTargetLeftOverhead ? targetColorHit : targetColor; - var targetColorLeftLowered = hitTargetLeftLowered ? targetColorHit : targetColor; - var targetColorLeftSide = hitTargetLeftSide ? targetColorHit : targetColor; - var targetColorLeftFront = hitTargetLeftFront ? targetColorHit : targetColor; + var targetColorLeftOnBase = hitTargetLeftOnBase ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorLeftOverhead = hitTargetLeftOverhead ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorLeftLowered = hitTargetLeftLowered ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorLeftSide = hitTargetLeftSide ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorLeftFront = hitTargetLeftFront ? TARGET_COLOR_HIT : TARGET_COLOR; - var targetColorRightOnBase = hitTargetRightOnBase ? targetColorHit : targetColor; - var targetColorRightOverhead = hitTargetRightOverhead ? targetColorHit : targetColor; - var targetColorRightLowered = hitTargetRightLowered ? targetColorHit : targetColor; - var targetColorRightSide = hitTargetRightSide ? targetColorHit : targetColor; - var targetColorRightFront = hitTargetRightFront ? targetColorHit : targetColor; + var targetColorRightOnBase = hitTargetRightOnBase ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorRightOverhead = hitTargetRightOverhead ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorRightLowered = hitTargetRightLowered ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorRightSide = hitTargetRightSide ? TARGET_COLOR_HIT : TARGET_COLOR; + var targetColorRightFront = hitTargetRightFront ? TARGET_COLOR_HIT : TARGET_COLOR; // calculate a combined arm pose based on left and right hits var poseValue = NO_POSE; @@ -536,47 +509,48 @@ Script.update.connect(function(deltaTime) { poseValue += hitTargetRightFront ? RIGHT_FRONT : 0; if (poses[poseValue] == undefined) { - Overlays.editOverlay(text, { text: "no pose -- value:" + poseValue }); + Overlays.editOverlay(textOverlay, { text: "no pose -- value:" + poseValue }); } else { - Overlays.editOverlay(text, { text: "pose:" + poses[poseValue].name + "\n" + "animation:" + poses[poseValue].animation }); + Overlays.editOverlay(textOverlay, { text: "pose:" + poses[poseValue].name + "\n" + "animation:" + poses[poseValue].animation }); var props = Entities.getEntityProperties(puppetEntityID); + print("puppetEntityID:" + puppetEntityID + "age:"+props.age); Entities.editEntity(puppetEntityID, { animationURL: poses[poseValue].animation, lifetime: TEMPORARY_LIFETIME + props.age // renew lifetime }); } - lastPoseValue = poseValue; - - Overlays.editOverlay(leftOnBase, { position: getPositionLeftOnBase(), color: targetColorLeftOnBase } ); - Overlays.editOverlay(leftOverhead, { position: getPositionLeftOverhead(), color: targetColorLeftOverhead } ); - Overlays.editOverlay(leftLowered, { position: getPositionLeftLowered(), color: targetColorLeftLowered } ); - Overlays.editOverlay(leftSide, { position: getPositionLeftSide() , color: targetColorLeftSide } ); - Overlays.editOverlay(leftFront, { position: getPositionLeftFront() , color: targetColorLeftFront } ); + Overlays.editOverlay(leftOnBaseOverlay, { position: getPositionLeftOnBase(), color: targetColorLeftOnBase } ); + Overlays.editOverlay(leftOverheadOverlay, { position: getPositionLeftOverhead(), color: targetColorLeftOverhead } ); + Overlays.editOverlay(leftLoweredOverlay, { position: getPositionLeftLowered(), color: targetColorLeftLowered } ); + Overlays.editOverlay(leftSideOverlay, { position: getPositionLeftSide() , color: targetColorLeftSide } ); + Overlays.editOverlay(leftFrontOverlay, { position: getPositionLeftFront() , color: targetColorLeftFront } ); - Overlays.editOverlay(rightOnBase, { position: getPositionRightOnBase(), color: targetColorRightOnBase } ); - Overlays.editOverlay(rightOverhead, { position: getPositionRightOverhead(), color: targetColorRightOverhead } ); - Overlays.editOverlay(rightLowered, { position: getPositionRightLowered(), color: targetColorRightLowered } ); - Overlays.editOverlay(rightSide, { position: getPositionRightSide() , color: targetColorRightSide } ); - Overlays.editOverlay(rightFront, { position: getPositionRightFront() , color: targetColorRightFront } ); - }); + Overlays.editOverlay(rightOnBaseOverlay, { position: getPositionRightOnBase(), color: targetColorRightOnBase } ); + Overlays.editOverlay(rightOverheadOverlay, { position: getPositionRightOverhead(), color: targetColorRightOverhead } ); + Overlays.editOverlay(rightLoweredOverlay, { position: getPositionRightLowered(), color: targetColorRightLowered } ); + Overlays.editOverlay(rightSideOverlay, { position: getPositionRightSide() , color: targetColorRightSide } ); + Overlays.editOverlay(rightFrontOverlay, { position: getPositionRightFront() , color: targetColorRightFront } ); + } -Script.scriptEnding.connect(function() { - Overlays.deleteOverlay(leftHand); - Overlays.deleteOverlay(rightHand); - Overlays.deleteOverlay(text); - Overlays.deleteOverlay(leftOnBase); - Overlays.deleteOverlay(leftOverhead); - Overlays.deleteOverlay(leftLowered); - Overlays.deleteOverlay(leftSide); - Overlays.deleteOverlay(leftFront); - Overlays.deleteOverlay(rightOnBase); - Overlays.deleteOverlay(rightOverhead); - Overlays.deleteOverlay(rightLowered); - Overlays.deleteOverlay(rightSide); - Overlays.deleteOverlay(rightFront); +breakdanceEnd= function() { + print("breakdanceEnd..."); + + Overlays.deleteOverlay(leftHandOverlay); + Overlays.deleteOverlay(rightHandOverlay); + + Overlays.deleteOverlay(textOverlay); + Overlays.deleteOverlay(leftOnBaseOverlay); + Overlays.deleteOverlay(leftOverheadOverlay); + Overlays.deleteOverlay(leftLoweredOverlay); + Overlays.deleteOverlay(leftSideOverlay); + Overlays.deleteOverlay(leftFrontOverlay); + Overlays.deleteOverlay(rightOnBaseOverlay); + Overlays.deleteOverlay(rightOverheadOverlay); + Overlays.deleteOverlay(rightLoweredOverlay); + Overlays.deleteOverlay(rightSideOverlay); + Overlays.deleteOverlay(rightFrontOverlay); - print("puppetEntityID:"+puppetEntityID); Entities.deleteEntity(puppetEntityID); -}); \ No newline at end of file +} \ No newline at end of file diff --git a/examples/toys/breakdanceToy.js b/examples/toys/breakdanceToy.js new file mode 100644 index 0000000000..841d5ad111 --- /dev/null +++ b/examples/toys/breakdanceToy.js @@ -0,0 +1,17 @@ +// +// breakdanceToy.js +// examples/toys +// +// This is an local script version of the breakdance game +// +// Created by Brad Hefta-Gaub on Sept 3, 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("breakdanceCore.js"); +breakdanceStart(); +Script.update.connect(breakdanceUpdate); +Script.scriptEnding.connect(breakdanceEnd); From 1071b98260354ce836d5c9fea85502d0f1f8dde6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 3 Sep 2015 14:38:46 -0700 Subject: [PATCH 08/10] fix differential avatar-joint sending to work if more than 2 avatars are in the domain --- assignment-client/src/avatars/AvatarMixer.cpp | 48 ++++++++++--------- libraries/avatars/src/AvatarData.cpp | 6 +-- libraries/avatars/src/AvatarData.h | 4 ++ 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 685e95bbd7..64969b55ce 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -362,32 +362,34 @@ void AvatarMixer::broadcastAvatarData() { } else { nodeData->setMaxAvatarDistance(maxAvatarDistanceThisFrame); } - - // We're done encoding this version of the otherAvatars. Update their "lastSent" joint-states so - // that we can notice differences, next time around. - nodeList->eachMatchingNode( - [&](const SharedNodePointer& otherNode)->bool { - if (!otherNode->getLinkedData()) { - return false; - } - if (otherNode->getUUID() == node->getUUID()) { - return false; - } - - return true; - }, - [&](const SharedNodePointer& otherNode) { - AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); - MutexTryLocker lock(otherNodeData->getMutex()); - if (!lock.isLocked()) { - return; - } - AvatarData& otherAvatar = otherNodeData->getAvatar(); - otherAvatar.doneEncoding(); - }); } ); + // We're done encoding this version of the otherAvatars. Update their "lastSent" joint-states so + // that we can notice differences, next time around. + nodeList->eachMatchingNode( + [&](const SharedNodePointer& otherNode)->bool { + if (!otherNode->getLinkedData()) { + return false; + } + if (otherNode->getType() != NodeType::Agent) { + return false; + } + if (!otherNode->getActiveSocket()) { + return false; + } + return true; + }, + [&](const SharedNodePointer& otherNode) { + AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + MutexTryLocker lock(otherNodeData->getMutex()); + if (!lock.isLocked()) { + return; + } + AvatarData& otherAvatar = otherNodeData->getAvatar(); + otherAvatar.doneEncoding(); + }); + _lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch(); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 853da6dbe5..555ca42d60 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -31,10 +31,6 @@ quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND; -// this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer -const float MIN_ROTATION_DOT = 0.9999999f; - - using namespace std; const glm::vec3 DEFAULT_LOCAL_AABOX_CORNER(-0.5f); @@ -249,7 +245,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { if (sendAll || _lastSentJointData[i].rotation != data.rotation) { if (sendAll || !cullSmallChanges || - fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= MIN_ROTATION_DOT) { + fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) { validity |= (1 << validityBit); } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index fe6c737a6e..8976074ec0 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -111,7 +111,11 @@ const int AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS = 5000; // See also static AvatarData::defaultFullAvatarModelUrl(). const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default"); +// how often should we send a full report about joint rotations, even if they haven't changed? const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02; +// this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer +const float AVATAR_MIN_ROTATION_DOT = 0.99999999f; + // Where one's own Avatar begins in the world (will be overwritten if avatar data file is found). // This is the start location in the Sandbox (xyz: 6270, 211, 6000). From 0c8848a463428e80e949a7f475141ddafb67c27d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 3 Sep 2015 15:41:40 -0700 Subject: [PATCH 09/10] don't update _lastSendJointData if we didn't send --- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 17 ++++++++++++----- libraries/avatars/src/AvatarData.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 64969b55ce..1ab637b57f 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -387,7 +387,7 @@ void AvatarMixer::broadcastAvatarData() { return; } AvatarData& otherAvatar = otherNodeData->getAvatar(); - otherAvatar.doneEncoding(); + otherAvatar.doneEncoding(false); }); _lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 555ca42d60..46a323733a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -274,16 +274,20 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { return avatarDataByteArray.left(destinationBuffer - startPosition); } -void AvatarData::doneEncoding() { +void AvatarData::doneEncoding(bool cullSmallChanges) { // The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData. _lastSentJointData.resize(_jointData.size()); for (int i = 0; i < _jointData.size(); i ++) { const JointData& data = _jointData[ i ]; - _lastSentJointData[i].rotation = data.rotation; + if (_lastSentJointData[i].rotation != data.rotation) { + if (!cullSmallChanges || + fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) { + _lastSentJointData[i].rotation = data.rotation; + } + } } } - bool AvatarData::shouldLogError(const quint64& now) { if (now > _errorLogExpiry) { _errorLogExpiry = now + DEFAULT_FILTERED_LOG_EXPIRY; @@ -1089,8 +1093,11 @@ void AvatarData::setJointMappingsFromNetworkReply() { void AvatarData::sendAvatarDataPacket() { auto nodeList = DependencyManager::get(); - QByteArray avatarByteArray = toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); - doneEncoding(); + // about 2% of the time, we send a full update (meaning, we transmit all the joint data), even if nothing has changed. + // this is to guard against a joint moving once, the packet getting lost, and the joint never moving again. + bool sendFullUpdate = randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO; + QByteArray avatarByteArray = toByteArray(true, sendFullUpdate); + doneEncoding(true); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 8976074ec0..c4cc3fcaac 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -177,7 +177,7 @@ public: void setHandPosition(const glm::vec3& handPosition); virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll); - virtual void doneEncoding(); + virtual void doneEncoding(bool cullSmallChanges); /// \return true if an error should be logged bool shouldLogError(const quint64& now); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index ced0742a51..eeff6c15f0 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -600,7 +600,7 @@ void ScriptEngine::run() { const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t); QByteArray avatarByteArray = _avatarData->toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); - _avatarData->doneEncoding(); + _avatarData->doneEncoding(true); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); From 218521fc021c4e3565ef35cf56926de89a004525 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 3 Sep 2015 16:08:46 -0700 Subject: [PATCH 10/10] put AVATAR_MIN_ROTATION_DOT back to previous value --- libraries/avatars/src/AvatarData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index c4cc3fcaac..af97180cbd 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -114,7 +114,7 @@ const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default"); // how often should we send a full report about joint rotations, even if they haven't changed? const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02; // this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer -const float AVATAR_MIN_ROTATION_DOT = 0.99999999f; +const float AVATAR_MIN_ROTATION_DOT = 0.9999999f; // Where one's own Avatar begins in the world (will be overwritten if avatar data file is found).