From 76923583ee8fbb35f24e965616dd26905413f2de Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 1 Sep 2015 13:31:34 -0700 Subject: [PATCH 1/3] Interface only sends joint data to avatar-mixer if they have changed enough since the last send --- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- .../src/avatars/ScriptableAvatar.cpp | 3 -- interface/src/avatar/Avatar.cpp | 8 ++-- interface/src/avatar/MyAvatar.cpp | 8 ++-- interface/src/avatar/MyAvatar.h | 2 +- libraries/avatars/src/AvatarData.cpp | 47 ++++++++++++++----- libraries/avatars/src/AvatarData.h | 4 +- libraries/script-engine/src/ScriptEngine.cpp | 2 +- 8 files changed, 47 insertions(+), 29 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 3a3176b78d..eefb654737 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -295,7 +295,7 @@ void AvatarMixer::broadcastAvatarData() { avatarPacketList.startSegment(); numAvatarDataBytes += avatarPacketList.write(otherNode->getUUID().toRfc4122()); - numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray()); + numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray(false)); avatarPacketList.endSegment(); diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 3d243d78ec..0b7af01c94 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -77,10 +77,7 @@ void ScriptableAvatar::update(float deltatime) { int mapping = animationJoints.indexOf(modelJoints[i]); if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) { JointData& data = _jointData[i]; - data.valid = true; data.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); - } else { - _jointData[i].valid = false; } } } else { diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e7423336b1..6d1505ced9 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -200,11 +200,9 @@ void Avatar::simulate(float deltaTime) { if (!_shouldRenderBillboard && inViewFrustum) { { PerformanceTimer perfTimer("skeleton"); - if (_hasNewJointRotations) { - for (int i = 0; i < _jointData.size(); i++) { - const JointData& data = _jointData.at(i); - _skeletonModel.setJointState(i, data.valid, data.rotation); - } + for (int i = 0; i < _jointData.size(); i++) { + const JointData& data = _jointData.at(i); + _skeletonModel.setJointState(i, true, data.rotation); } _skeletonModel.simulate(deltaTime, _hasNewJointRotations); simulateAttachments(deltaTime); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 39d2637b9e..6939720f32 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -123,18 +123,18 @@ MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset(); } -QByteArray MyAvatar::toByteArray() { +QByteArray MyAvatar::toByteArray(bool cullSmallChanges) { 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(); + QByteArray array = AvatarData::toByteArray(cullSmallChanges); // copy the correct position back _position = oldPosition; return array; } - return AvatarData::toByteArray(); + return AvatarData::toByteArray(cullSmallChanges); } void MyAvatar::reset() { @@ -220,7 +220,7 @@ void MyAvatar::simulate(float deltaTime) { _jointData.resize(_rig->getJointStateCount()); for (int i = 0; i < _jointData.size(); i++) { JointData& data = _jointData[i]; - data.valid = _rig->getJointStateRotation(i, data.rotation); + _rig->getJointStateRotation(i, data.rotation); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 9e7ab11aa6..89f3b236f4 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(); + QByteArray toByteArray(bool cullSmallChanges); 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 9ef1f94c55..d761d861c3 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -31,6 +31,10 @@ 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); @@ -141,7 +145,7 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) { _handPosition = glm::inverse(getOrientation()) * (handPosition - _position); } -QByteArray AvatarData::toByteArray() { +QByteArray AvatarData::toByteArray(bool cullSmallChanges) { // 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 @@ -234,11 +238,19 @@ QByteArray AvatarData::toByteArray() { // joint data *destinationBuffer++ = _jointData.size(); + unsigned char* validityPosition = destinationBuffer; unsigned char validity = 0; int validityBit = 0; - foreach (const JointData& data, _jointData) { - if (data.valid) { - validity |= (1 << validityBit); + + _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) { + validity |= (1 << validityBit); + } } if (++validityBit == BITS_IN_BYTE) { *destinationBuffer++ = validity; @@ -248,9 +260,18 @@ QByteArray AvatarData::toByteArray() { if (validityBit != 0) { *destinationBuffer++ = validity; } - foreach (const JointData& data, _jointData) { - if (data.valid) { + + validityBit = 0; + validity = *validityPosition++; + for (int i = 0; i < _jointData.size(); i ++) { + 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; + validity = *validityPosition++; } } @@ -494,6 +515,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } int numValidJoints = 0; _jointData.resize(numJoints); + + QVector valids; + valids.resize(numJoints); + { // validity bits unsigned char validity = 0; int validityBit = 0; @@ -505,7 +530,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (valid) { ++numValidJoints; } - _jointData[i].valid = valid; + valids[i] = valid; validityBit = (validityBit + 1) % BITS_IN_BYTE; } } @@ -527,7 +552,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { { // joint data for (int i = 0; i < numJoints; i++) { JointData& data = _jointData[i]; - if (data.valid) { + if (valids[i]) { _hasNewJointRotations = true; sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation); } @@ -731,7 +756,6 @@ void AvatarData::setJointData(int index, const glm::quat& rotation) { _jointData.resize(index + 1); } JointData& data = _jointData[index]; - data.valid = true; data.rotation = rotation; } @@ -746,7 +770,6 @@ void AvatarData::clearJointData(int index) { if (_jointData.size() <= index) { _jointData.resize(index + 1); } - _jointData[index].valid = false; } bool AvatarData::isJointDataValid(int index) const { @@ -759,7 +782,7 @@ bool AvatarData::isJointDataValid(int index) const { Q_RETURN_ARG(bool, result), Q_ARG(int, index)); return result; } - return index < _jointData.size() && _jointData.at(index).valid; + return index < _jointData.size() /* && _jointData.at(index).valid */; } glm::quat AvatarData::getJointRotation(int index) const { @@ -1060,7 +1083,7 @@ void AvatarData::setJointMappingsFromNetworkReply() { void AvatarData::sendAvatarDataPacket() { auto nodeList = DependencyManager::get(); - QByteArray avatarByteArray = toByteArray(); + QByteArray avatarByteArray = toByteArray(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 8bb874bc71..278ec2047c 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -171,7 +171,7 @@ public: glm::vec3 getHandPosition() const; void setHandPosition(const glm::vec3& handPosition); - virtual QByteArray toByteArray(); + virtual QByteArray toByteArray(bool cullSmallChanges); /// \return true if an error should be logged bool shouldLogError(const quint64& now); @@ -357,6 +357,7 @@ protected: char _handState; QVector _jointData; ///< the state of the skeleton joints + QVector _lastSentJointData; ///< the state of the skeleton joints last time we transmitted // key state KeyState _keyState; @@ -408,7 +409,6 @@ Q_DECLARE_METATYPE(AvatarData*) class JointData { public: - bool valid; glm::quat rotation; }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index b42daa710a..aa395b1b06 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -599,7 +599,7 @@ void ScriptEngine::run() { / (1000 * 1000)) + 0.5); const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t); - QByteArray avatarByteArray = _avatarData->toByteArray(); + QByteArray avatarByteArray = _avatarData->toByteArray(true); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); From 6f80c484b4eeb8e7c675138a6e766f81a25cf6c3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 1 Sep 2015 13:33:19 -0700 Subject: [PATCH 2/3] cleanups --- libraries/avatars/src/AvatarData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d761d861c3..123c9707ba 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -782,7 +782,7 @@ bool AvatarData::isJointDataValid(int index) const { Q_RETURN_ARG(bool, result), Q_ARG(int, index)); return result; } - return index < _jointData.size() /* && _jointData.at(index).valid */; + return index < _jointData.size(); } glm::quat AvatarData::getJointRotation(int index) const { From e32b9191f55b4a99a2bdc49ad6bff6b83015e387 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 1 Sep 2015 13:35:58 -0700 Subject: [PATCH 3/3] bump AvatarData protocol version --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 7e73b4c660..ac72f1bd68 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -69,7 +69,7 @@ PacketVersion versionForPacketType(PacketType::Value packetType) { case EntityData: return VERSION_ENTITIES_PARTICLE_MODIFICATIONS; case AvatarData: - return 12; + return 13; default: return 11; }