From 1ad197df268393a0d07cea7e1c0bce56543d1532 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 16 Apr 2019 17:40:43 -0700 Subject: [PATCH] Send avatar skeleton trait --- .../src/avatars/AvatarMixerClientData.cpp | 8 +- .../src/avatars-renderer/Avatar.cpp | 24 ++++ .../src/avatars-renderer/Avatar.h | 2 + libraries/avatars/src/AvatarData.cpp | 103 ++++++++++++++++++ libraries/avatars/src/AvatarData.h | 42 ++++++- libraries/avatars/src/AvatarTraits.h | 2 +- 6 files changed, 178 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 1b86e0dff2..29c0697249 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -179,7 +179,13 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, if (packetTraitVersion > _lastReceivedTraitVersions[traitType]) { _avatar->processTrait(traitType, message.read(traitSize)); _lastReceivedTraitVersions[traitType] = packetTraitVersion; - + if (traitType == AvatarTraits::SkeletonData) { + qDebug() << "Sending skeleton avatar trait"; + auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true); + AvatarTraits::packVersionedTrait(AvatarTraits::SkeletonData, *packet, packetTraitVersion, *_avatar); + auto nodeList = DependencyManager::get(); + nodeList->sendPacket(std::move(packet), sendingNode); + } if (traitType == AvatarTraits::SkeletonModelURL) { // special handling for skeleton model URL, since we need to make sure it is in the whitelist checkSkeletonURLAgainstWhitelist(slaveSharedData, sendingNode, packetTraitVersion); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index b6c5c6d235..0d78bd810a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1449,6 +1449,28 @@ QStringList Avatar::getJointNames() const { return result; } +std::vector Avatar::getSkeletonDefaultData() { + std::vector defaultSkeletonData; + if (_skeletonModel->isLoaded()) { + auto jointNames = getJointNames(); + int sizeCount = 0; + for (int i = 0; i < min(43, jointNames.size()); i++) { + AvatarSkeletonTrait::UnpackedJointData jointData; + jointData.jointParent = _skeletonModel->getRig().getJointParentIndex(i); + jointData.jointIndex = i; + jointData.defaultRotation = getDefaultJointRotation(i); + jointData.defaultTranslation = getDefaultJointTranslation(i); + jointData.defaultScale = glm::length(getAbsoluteJointScaleInObjectFrame(i)); + jointData.jointName = jointNames[i]; + jointData.stringLength = jointNames[i].size(); + jointData.stringStart = sizeCount; + sizeCount += jointNames[i].size(); + defaultSkeletonData.push_back(jointData); + } + } + return defaultSkeletonData; +} + glm::vec3 Avatar::getJointPosition(int index) const { glm::vec3 position; _skeletonModel->getJointPositionInWorldFrame(index, position); @@ -1515,6 +1537,8 @@ void Avatar::rigReady() { buildSpine2SplineRatioCache(); computeMultiSphereShapes(); buildSpine2SplineRatioCache(); + setSkeletonData(getSkeletonDefaultData()); + sendSkeletonData(); } // rig has been reset. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index aef5ac09e9..afec768e33 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -199,6 +199,8 @@ public: virtual int getJointIndex(const QString& name) const override; virtual QStringList getJointNames() const override; + std::vector getSkeletonDefaultData(); + /**jsdoc * Gets the default rotation of a joint (in the current avatar) relative to its parent. *

For information on the joint hierarchy used, see diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a2b0b808ba..3491933ce5 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1990,11 +1990,98 @@ QUrl AvatarData::getWireSafeSkeletonModelURL() const { return QUrl(); } } +QByteArray AvatarData::packSkeletonData() const { + int avatarDataSize = 0; + QByteArray avatarDataByteArray; + //_avatarSkeletonDataLock.withReadLock([&] { + // Add header + AvatarSkeletonTrait::Header header; + header.maxScaleDimension = 0.0f; + header.maxTranslationDimension = 0.0f; + header.numJoints = (uint8_t)_avatarSkeletonData.size(); + header.padding = 0; + header.stringTableLength = 0; + qDebug() << "Dealing with: " << _avatarSkeletonData.size() << " joints"; + for (size_t i = 0; i < _avatarSkeletonData.size(); i++) { + header.stringTableLength += (uint16_t)_avatarSkeletonData[i].jointName.size(); + auto &translation = _avatarSkeletonData[i].defaultTranslation; + header.maxTranslationDimension = std::max(header.maxTranslationDimension, std::max(std::max(translation.x, translation.y), translation.z)); + header.maxScaleDimension = std::max(header.maxScaleDimension, _avatarSkeletonData[i].defaultScale); + } + + const int byteArraySize = (int)sizeof(AvatarSkeletonTrait::Header) + (int)(header.numJoints * sizeof(AvatarSkeletonTrait::JointData)) + header.stringTableLength; + avatarDataByteArray = QByteArray(byteArraySize, 0); + unsigned char* destinationBuffer = reinterpret_cast(avatarDataByteArray.data()); + const unsigned char* const startPosition = destinationBuffer; + + memcpy(destinationBuffer, &header, sizeof(header)); + destinationBuffer += sizeof(AvatarSkeletonTrait::Header); + + QString stringTable = ""; + for (size_t i = 0; i < _avatarSkeletonData.size(); i++) { + AvatarSkeletonTrait::JointData jdata; + jdata.boneType = _avatarSkeletonData[i].boneType; + jdata.jointParent = _avatarSkeletonData[i].jointParent; + packFloatRatioToTwoByte((uint8_t*)(&jdata.defaultScale), _avatarSkeletonData[i].defaultScale / header.maxScaleDimension); + packOrientationQuatToSixBytes(jdata.defaultRotation, _avatarSkeletonData[i].defaultRotation); + packFloatVec3ToSignedTwoByteFixed(jdata.defaultTranslation, _avatarSkeletonData[i].defaultTranslation / header.maxTranslationDimension, TRANSLATION_COMPRESSION_RADIX); + jdata.jointIndex = (uint16_t)i; + jdata.stringStart = (uint16_t)_avatarSkeletonData[i].stringStart; + jdata.stringLength = (uint8_t)_avatarSkeletonData[i].stringLength; + stringTable += _avatarSkeletonData[i].jointName; + memcpy(destinationBuffer, &jdata, sizeof(AvatarSkeletonTrait::JointData)); + destinationBuffer += sizeof(AvatarSkeletonTrait::JointData); + } + + memcpy(destinationBuffer, stringTable.toUtf8(), header.stringTableLength); + destinationBuffer += header.stringTableLength; + + avatarDataSize = destinationBuffer - startPosition; + qDebug() << "Data size: " << avatarDataSize; + //}); + return avatarDataByteArray.left(avatarDataSize); +} QByteArray AvatarData::packSkeletonModelURL() const { return getWireSafeSkeletonModelURL().toEncoded(); } +void AvatarData::unpackSkeletonData(const QByteArray& data) { + + const unsigned char* startPosition = reinterpret_cast(data.data()); + const unsigned char* endPosition = startPosition + data.size(); + const unsigned char* sourceBuffer = startPosition; + + auto header = reinterpret_cast(sourceBuffer); + sourceBuffer += sizeof(const AvatarSkeletonTrait::Header); + + std::vector joints; + for (uint8_t i = 0; i < header->numJoints; i++) { + auto jointData = reinterpret_cast(sourceBuffer); + sourceBuffer += sizeof(const AvatarSkeletonTrait::JointData); + AvatarSkeletonTrait::UnpackedJointData uJointData; + uJointData.boneType = (int)jointData->boneType; + uJointData.jointIndex = (int)i; + uJointData.stringLength = (int)jointData->stringLength; + uJointData.stringStart = (int)jointData->stringStart; + unpackOrientationQuatFromSixBytes(reinterpret_cast(&jointData->defaultRotation), uJointData.defaultRotation); + unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast(&jointData->defaultTranslation), uJointData.defaultTranslation, TRANSLATION_COMPRESSION_RADIX); + unpackFloatScalarFromSignedTwoByteFixed((const int16_t*)(&jointData->defaultScale), &uJointData.defaultScale, TRANSLATION_COMPRESSION_RADIX); + uJointData.defaultTranslation *= header->maxTranslationDimension; + uJointData.defaultScale *= header->maxScaleDimension; + joints.push_back(uJointData); + } + qDebug() << "_____length: " << header->stringTableLength; + QString table = QString::fromUtf8(reinterpret_cast(sourceBuffer), (int)header->stringTableLength); + for (size_t i = 0; i < joints.size(); i++) { + QStringRef subString(&table, joints[i].stringStart, joints[i].stringLength); + joints[i].jointName = subString.toString(); + qDebug() << "_____data: " << joints[i].boneType << " " << joints[i].jointIndex << " " << joints[i].stringLength << " " << joints[i].stringStart << " " << joints[i].defaultRotation << " " << joints[i].defaultScale << joints[i].defaultTranslation; + qDebug() << "_____JointNameReveived: " << joints[i].jointName; + } + setSkeletonData(joints); +} + void AvatarData::unpackSkeletonModelURL(const QByteArray& data) { auto skeletonModelURL = QUrl::fromEncoded(data); setSkeletonModelURL(skeletonModelURL); @@ -2030,6 +2117,8 @@ QByteArray AvatarData::packTrait(AvatarTraits::TraitType traitType) const { // Call packer function if (traitType == AvatarTraits::SkeletonModelURL) { traitBinaryData = packSkeletonModelURL(); + } else if (traitType == AvatarTraits::SkeletonData) { + traitBinaryData = packSkeletonData(); } return traitBinaryData; @@ -2051,6 +2140,8 @@ QByteArray AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, Avat void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { if (traitType == AvatarTraits::SkeletonModelURL) { unpackSkeletonModelURL(traitBinaryData); + } else if (traitType == AvatarTraits::SkeletonData) { + unpackSkeletonData(traitBinaryData); } } @@ -2997,6 +3088,18 @@ AABox AvatarData::computeBubbleBox(float bubbleScale) const { return box; } +void AvatarData::setSkeletonData(const std::vector& skeletonData) { + _avatarSkeletonDataLock.withWriteLock([&] { + _avatarSkeletonData = skeletonData; + }); +} + +void AvatarData::sendSkeletonData() const{ + if (_clientTraitsHandler) { + _clientTraitsHandler->markTraitUpdated(AvatarTraits::SkeletonData); + } +} + AABox AvatarData::getDefaultBubbleBox() const { AABox bubbleBox(_defaultBubbleBox); bubbleBox.translate(_globalPosition); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 1c4b0cfc53..a0ce5b2201 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -145,6 +145,39 @@ const char AVATARDATA_FLAGS_MINIMUM = 0; using SmallFloat = uint16_t; // a compressed float with less precision, user defined radix +namespace AvatarSkeletonTrait { + PACKED_BEGIN struct Header { + float maxTranslationDimension; + float maxScaleDimension; + uint8_t numJoints; + uint8_t padding; + uint16_t stringTableLength; + } PACKED_END; + + PACKED_BEGIN struct JointData { + uint16_t stringStart; + uint8_t stringLength; + uint8_t boneType; + uint8_t defaultTranslation[6]; + uint8_t defaultRotation[6]; + uint16_t defaultScale; + uint16_t jointIndex; + uint16_t jointParent; + } PACKED_END; + + struct UnpackedJointData { + int jointParent; + int stringStart; + int stringLength; + int boneType; + glm::vec3 defaultTranslation; + glm::quat defaultRotation; + float defaultScale; + int jointIndex; + QString jointName; + }; +} + namespace AvatarDataPacket { // NOTE: every time AvatarData is sent from mixer to client, it also includes the GUIID for the session @@ -258,6 +291,7 @@ namespace AvatarDataPacket { PACKED_BEGIN struct AvatarLocalPosition { float localPosition[3]; // parent frame translation of the avatar } PACKED_END; + const size_t AVATAR_LOCAL_POSITION_SIZE = 12; static_assert(sizeof(AvatarLocalPosition) == AVATAR_LOCAL_POSITION_SIZE, "AvatarDataPacket::AvatarLocalPosition size doesn't match."); @@ -1419,6 +1453,8 @@ public: void setIsNewAvatar(bool isNewAvatar) { _isNewAvatar = isNewAvatar; } bool getIsNewAvatar() { return _isNewAvatar; } void setIsClientAvatar(bool isClientAvatar) { _isClientAvatar = isClientAvatar; } + void setSkeletonData(const std::vector& skeletonData); + void sendSkeletonData() const; signals: @@ -1597,12 +1633,13 @@ protected: bool hasParent() const { return !getParentID().isNull(); } bool hasFaceTracker() const { return _headData ? _headData->_isFaceTrackerConnected : false; } + QByteArray packSkeletonData() const; QByteArray packSkeletonModelURL() const; QByteArray packAvatarEntityTraitInstance(AvatarTraits::TraitInstanceID traitInstanceID); QByteArray packGrabTraitInstance(AvatarTraits::TraitInstanceID traitInstanceID); void unpackSkeletonModelURL(const QByteArray& data); - + void unpackSkeletonData(const QByteArray& data); // isReplicated will be true on downstream Avatar Mixers and their clients, but false on the upstream "master" // Audio Mixer that the replicated avatar is connected to. @@ -1719,6 +1756,9 @@ protected: AvatarGrabDataMap _avatarGrabData; bool _avatarGrabDataChanged { false }; // by network + mutable ReadWriteLockable _avatarSkeletonDataLock; + std::vector _avatarSkeletonData; + // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. ThreadSafeValueCache _sensorToWorldMatrixCache { glm::mat4() }; ThreadSafeValueCache _controllerLeftHandMatrixCache { glm::mat4() }; diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index 13d64ec225..007b0418a9 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -29,7 +29,7 @@ namespace AvatarTraits { // Simple traits SkeletonModelURL = 0, - + SkeletonData, // Instanced traits FirstInstancedTrait, AvatarEntity = FirstInstancedTrait,