add compression and minimal data flags to AvatarData

This commit is contained in:
Brad Hefta-Gaub 2016-12-14 11:47:45 -08:00
parent 7189f766a2
commit 2290179281
4 changed files with 250 additions and 188 deletions

View file

@ -227,7 +227,7 @@ void MyAvatar::simulateAttachments(float deltaTime) {
// don't update attachments here, do it in harvestResultsFromPhysicsSimulation()
}
QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum, bool compressed) {
CameraMode mode = qApp->getCamera()->getMode();
_globalPosition = getPosition();
_globalBoundingBoxCorner.x = _characterController.getCapsuleRadius();
@ -238,12 +238,12 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
// fake the avatar position that is sent up to the AvatarMixer
glm::vec3 oldPosition = getPosition();
setPosition(getSkeletonPosition());
QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll);
QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll, sendMinimum, compressed);
// copy the correct position back
setPosition(oldPosition);
return array;
}
return AvatarData::toByteArray(cullSmallChanges, sendAll);
return AvatarData::toByteArray(cullSmallChanges, sendAll, sendMinimum, compressed);
}
void MyAvatar::centerBody() {

View file

@ -333,7 +333,7 @@ private:
glm::vec3 getWorldBodyPosition() const;
glm::quat getWorldBodyOrientation() const;
QByteArray toByteArray(bool cullSmallChanges, bool sendAll) override;
QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum, bool compressed) override;
void simulate(float deltaTime);
void updateFromTrackers(float deltaTime);
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;

View file

@ -181,7 +181,7 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) {
_handPosition = glm::inverse(getOrientation()) * (handPosition - getPosition());
}
QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum, bool compressed) {
// 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
@ -199,6 +199,20 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
unsigned char* destinationBuffer = reinterpret_cast<unsigned char*>(avatarDataByteArray.data());
unsigned char* startPosition = destinationBuffer;
// Leading flags, to indicate how much data is actually included in the packet...
UINT8 packetStateFlags = 0;
if (sendMinimum) {
setAtBit(packetStateFlags, AVATARDATA_FLAGS_MINIMUM);
}
// do we need this, or do we just always compress it?
if (compressed) {
setAtBit(packetStateFlags, AVATARDATA_FLAGS_COMPRESSED);
}
memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags));
destinationBuffer += sizeof(packetStateFlags);
if (!sendMinimum) {
auto header = reinterpret_cast<AvatarDataPacket::Header*>(destinationBuffer);
header->position[0] = getLocalPosition().x;
header->position[1] = getLocalPosition().y;
@ -400,7 +414,27 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
<< (destinationBuffer - startPosition);
}
#endif
}
// NOTE: first byte of array should always be uncompressed
if (compressed) {
static const int SKIP_PACKET_FLAGS = 1;
static const int COMPRESSION_LEVEL = 9;
QByteArray uncompressedPortion = avatarDataByteArray.mid(SKIP_PACKET_FLAGS,
(destinationBuffer - startPosition) - SKIP_PACKET_FLAGS);
QByteArray compressedPortion = qCompress(uncompressedPortion, COMPRESSION_LEVEL);
QByteArray flagsAndCompressed;
flagsAndCompressed.append(packetStateFlags);
flagsAndCompressed.append(compressedPortion);
//qDebug() << __FUNCTION__ << "compressing data was:" << (uncompressedPortion.size() + SKIP_PACKET_FLAGS) << "now:" << flagsAndCompressed.size();
return flagsAndCompressed;
}
// entire buffer is uncompressed
return avatarDataByteArray.left(destinationBuffer - startPosition);
}
@ -474,9 +508,30 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
_headData = new HeadData(this);
}
UINT8 packetStateFlags = buffer.at(0);
bool minimumSent = oneAtBit(packetStateFlags, AVATARDATA_FLAGS_MINIMUM);
bool packetIsCompressed = oneAtBit(packetStateFlags, AVATARDATA_FLAGS_COMPRESSED);
// if this is the minimum, then it only includes the flags
if (minimumSent) {
int numBytesRead = sizeof(packetStateFlags);
_averageBytesReceived.updateAverage(numBytesRead);
return numBytesRead;
}
QByteArray uncompressBuffer;
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(buffer.data());
const unsigned char* endPosition = startPosition + buffer.size();
const unsigned char* sourceBuffer = startPosition;
if (packetIsCompressed) {
uncompressBuffer = qUncompress(buffer.right(buffer.size() - sizeof(packetStateFlags)));
startPosition = reinterpret_cast<const unsigned char*>(uncompressBuffer.data());
endPosition = startPosition + uncompressBuffer.size();
sourceBuffer = startPosition;
//qDebug() << __FUNCTION__ << "uncompressing compressed data was:" << buffer.size() << "now:" << uncompressBuffer.size();
}
quint64 now = usecTimestampNow();
PACKET_READ_CHECK(Header, sizeof(AvatarDataPacket::Header));

View file

@ -106,6 +106,13 @@ const char LEFT_HAND_POINTING_FLAG = 1;
const char RIGHT_HAND_POINTING_FLAG = 2;
const char IS_FINGER_POINTING_FLAG = 4;
// AvatarData state flags - we store the details about the packet encoding in the first byte,
// before the "header" structure
const char AVATARDATA_FLAGS_MINIMUM = 0;
const char AVATARDATA_FLAGS_COMPRESSED = 1;
static const float MAX_AVATAR_SCALE = 1000.0f;
static const float MIN_AVATAR_SCALE = .005f;
@ -201,7 +208,7 @@ public:
glm::vec3 getHandPosition() const;
void setHandPosition(const glm::vec3& handPosition);
virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll);
virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll, bool sendMinimum = false, bool compressed = true);
virtual void doneEncoding(bool cullSmallChanges);
/// \return true if an error should be logged