mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-23 02:07:24 +02:00
507 lines
20 KiB
C++
507 lines
20 KiB
C++
//
|
|
// AvatarData.cpp
|
|
// hifi
|
|
//
|
|
// Created by Stephen Birarda on 4/9/13.
|
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
|
//
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <stdint.h>
|
|
|
|
#include <NodeList.h>
|
|
#include <PacketHeaders.h>
|
|
#include <SharedUtil.h>
|
|
|
|
#include "AvatarData.h"
|
|
#include <VoxelConstants.h>
|
|
|
|
using namespace std;
|
|
|
|
static const float fingerVectorRadix = 4; // bits of precision when converting from float<->fixed
|
|
|
|
AvatarData::AvatarData(Node* owningNode) :
|
|
NodeData(owningNode),
|
|
_handPosition(0,0,0),
|
|
_bodyYaw(-90.0),
|
|
_bodyPitch(0.0),
|
|
_bodyRoll(0.0),
|
|
_newScale(1.0f),
|
|
_leaderID(UNKNOWN_NODE_ID),
|
|
_handState(0),
|
|
_cameraPosition(0,0,0),
|
|
_cameraOrientation(),
|
|
_cameraFov(0.0f),
|
|
_cameraAspectRatio(0.0f),
|
|
_cameraNearClip(0.0f),
|
|
_cameraFarClip(0.0f),
|
|
_keyState(NO_KEY_DOWN),
|
|
_wantColor(true),
|
|
_wantDelta(true),
|
|
_wantLowResMoving(true),
|
|
_wantOcclusionCulling(true),
|
|
_headData(NULL),
|
|
_handData(NULL)
|
|
{
|
|
|
|
}
|
|
|
|
AvatarData::~AvatarData() {
|
|
delete _headData;
|
|
delete _handData;
|
|
}
|
|
|
|
void AvatarData::setPositionFromVariantMap(QVariantMap positionMap) {
|
|
_position = glm::vec3(positionMap.value("x").toFloat(),
|
|
positionMap.value("y").toFloat(),
|
|
positionMap.value("z").toFloat());
|
|
}
|
|
|
|
QVariantMap AvatarData::getPositionVariantMap() {
|
|
QVariantMap positionMap;
|
|
|
|
positionMap.insert("x", _position.x);
|
|
positionMap.insert("y", _position.y);
|
|
positionMap.insert("z", _position.z);
|
|
|
|
return positionMap;
|
|
}
|
|
|
|
void AvatarData::setHandPositionFromVariantMap(QVariantMap handPositionMap) {
|
|
_handPosition = glm::vec3(handPositionMap.value("x").toFloat(),
|
|
handPositionMap.value("y").toFloat(),
|
|
handPositionMap.value("z").toFloat());
|
|
}
|
|
|
|
QVariantMap AvatarData::getHandPositionVariantMap() {
|
|
QVariantMap positionMap;
|
|
|
|
positionMap.insert("x", _handPosition.x);
|
|
positionMap.insert("y", _handPosition.y);
|
|
positionMap.insert("z", _handPosition.z);
|
|
|
|
return positionMap;
|
|
}
|
|
|
|
void AvatarData::sendData() {
|
|
|
|
// called from Agent visual loop to send data
|
|
if (Node* avatarMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AVATAR_MIXER)) {
|
|
unsigned char packet[MAX_PACKET_SIZE];
|
|
|
|
unsigned char* endOfPacket = packet;
|
|
endOfPacket += populateTypeAndVersion(endOfPacket, PACKET_TYPE_HEAD_DATA);
|
|
endOfPacket += packNodeId(endOfPacket, NodeList::getInstance()->getOwnerID());
|
|
|
|
int numPacketBytes = (endOfPacket - packet) + getBroadcastData(endOfPacket);
|
|
|
|
NodeList::getInstance()->getNodeSocket()->send(avatarMixer->getActiveSocket(), packet, numPacketBytes);
|
|
}
|
|
}
|
|
|
|
int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
|
|
unsigned char* bufferStart = destinationBuffer;
|
|
|
|
// 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
|
|
|
|
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
|
if (!_headData) {
|
|
_headData = new HeadData(this);
|
|
}
|
|
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
|
if (!_handData) {
|
|
_handData = new HandData(this);
|
|
}
|
|
|
|
// Body world position
|
|
memcpy(destinationBuffer, &_position, sizeof(float) * 3);
|
|
destinationBuffer += sizeof(float) * 3;
|
|
|
|
// Body rotation (NOTE: This needs to become a quaternion to save two bytes)
|
|
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyYaw);
|
|
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyPitch);
|
|
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyRoll);
|
|
|
|
// Body scale
|
|
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _newScale);
|
|
|
|
// Follow mode info
|
|
memcpy(destinationBuffer, &_leaderID, sizeof(uint16_t));
|
|
destinationBuffer += sizeof(uint16_t);
|
|
|
|
// Head rotation (NOTE: This needs to become a quaternion to save two bytes)
|
|
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_yaw);
|
|
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_pitch);
|
|
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_roll);
|
|
|
|
// Head lean X,Z (head lateral and fwd/back motion relative to torso)
|
|
memcpy(destinationBuffer, &_headData->_leanSideways, sizeof(_headData->_leanSideways));
|
|
destinationBuffer += sizeof(_headData->_leanSideways);
|
|
memcpy(destinationBuffer, &_headData->_leanForward, sizeof(_headData->_leanForward));
|
|
destinationBuffer += sizeof(_headData->_leanForward);
|
|
|
|
// Hand Position - is relative to body position
|
|
glm::vec3 handPositionRelative = _handPosition - _position;
|
|
memcpy(destinationBuffer, &handPositionRelative, sizeof(float) * 3);
|
|
destinationBuffer += sizeof(float) * 3;
|
|
|
|
// Lookat Position
|
|
memcpy(destinationBuffer, &_headData->_lookAtPosition, sizeof(_headData->_lookAtPosition));
|
|
destinationBuffer += sizeof(_headData->_lookAtPosition);
|
|
|
|
// Instantaneous audio loudness (used to drive facial animation)
|
|
//destinationBuffer += packFloatToByte(destinationBuffer, std::min(MAX_AUDIO_LOUDNESS, _audioLoudness), MAX_AUDIO_LOUDNESS);
|
|
memcpy(destinationBuffer, &_headData->_audioLoudness, sizeof(float));
|
|
destinationBuffer += sizeof(float);
|
|
|
|
// camera details
|
|
memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition));
|
|
destinationBuffer += sizeof(_cameraPosition);
|
|
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _cameraOrientation);
|
|
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _cameraFov);
|
|
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _cameraAspectRatio);
|
|
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraNearClip);
|
|
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraFarClip);
|
|
memcpy(destinationBuffer, &_cameraEyeOffsetPosition, sizeof(_cameraEyeOffsetPosition));
|
|
destinationBuffer += sizeof(_cameraEyeOffsetPosition);
|
|
|
|
// chat message
|
|
*destinationBuffer++ = _chatMessage.size();
|
|
memcpy(destinationBuffer, _chatMessage.data(), _chatMessage.size() * sizeof(char));
|
|
destinationBuffer += _chatMessage.size() * sizeof(char);
|
|
|
|
// bitMask of less than byte wide items
|
|
unsigned char bitItems = 0;
|
|
if (_wantLowResMoving) { setAtBit(bitItems, WANT_LOW_RES_MOVING_BIT); }
|
|
if (_wantColor) { setAtBit(bitItems, WANT_COLOR_AT_BIT); }
|
|
if (_wantDelta) { setAtBit(bitItems, WANT_DELTA_AT_BIT); }
|
|
if (_wantOcclusionCulling) { setAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); }
|
|
|
|
// key state
|
|
setSemiNibbleAt(bitItems,KEY_STATE_START_BIT,_keyState);
|
|
// hand state
|
|
setSemiNibbleAt(bitItems,HAND_STATE_START_BIT,_handState);
|
|
*destinationBuffer++ = bitItems;
|
|
|
|
bitItems = 0;
|
|
if (_headData->_isFaceshiftConnected) { setAtBit(bitItems, IS_FACESHIFT_CONNECTED); }
|
|
|
|
*destinationBuffer++ = bitItems;
|
|
|
|
// If it is connected, pack up the data
|
|
if (_headData->_isFaceshiftConnected) {
|
|
memcpy(destinationBuffer, &_headData->_leftEyeBlink, sizeof(float));
|
|
destinationBuffer += sizeof(float);
|
|
|
|
memcpy(destinationBuffer, &_headData->_rightEyeBlink, sizeof(float));
|
|
destinationBuffer += sizeof(float);
|
|
|
|
memcpy(destinationBuffer, &_headData->_averageLoudness, sizeof(float));
|
|
destinationBuffer += sizeof(float);
|
|
|
|
memcpy(destinationBuffer, &_headData->_browAudioLift, sizeof(float));
|
|
destinationBuffer += sizeof(float);
|
|
}
|
|
|
|
// leap hand data
|
|
destinationBuffer += _handData->encodeRemoteData(destinationBuffer);
|
|
|
|
// skeleton joints
|
|
*destinationBuffer++ = (unsigned char)_joints.size();
|
|
for (vector<JointData>::iterator it = _joints.begin(); it != _joints.end(); it++) {
|
|
*destinationBuffer++ = (unsigned char)it->jointID;
|
|
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, it->rotation);
|
|
}
|
|
|
|
return destinationBuffer - bufferStart;
|
|
}
|
|
|
|
// called on the other nodes - assigns it to my views of the others
|
|
int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|
|
|
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
|
if (!_headData) {
|
|
_headData = new HeadData(this);
|
|
}
|
|
|
|
// lazily allocate memory for HandData in case we're not an Avatar instance
|
|
if (!_handData) {
|
|
_handData = new HandData(this);
|
|
}
|
|
|
|
// increment to push past the packet header
|
|
int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
|
|
sourceBuffer += numBytesPacketHeader;
|
|
|
|
unsigned char* startPosition = sourceBuffer;
|
|
|
|
// push past the node ID
|
|
sourceBuffer += sizeof(uint16_t);
|
|
|
|
// Body world position
|
|
memcpy(&_position, sourceBuffer, sizeof(float) * 3);
|
|
sourceBuffer += sizeof(float) * 3;
|
|
|
|
// Body rotation (NOTE: This needs to become a quaternion to save two bytes)
|
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyYaw);
|
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyPitch);
|
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyRoll);
|
|
|
|
// Body scale
|
|
sourceBuffer += unpackFloatRatioFromTwoByte( sourceBuffer, _newScale);
|
|
|
|
// Follow mode info
|
|
memcpy(&_leaderID, sourceBuffer, sizeof(uint16_t));
|
|
sourceBuffer += sizeof(uint16_t);
|
|
|
|
// Head rotation (NOTE: This needs to become a quaternion to save two bytes)
|
|
float headYaw, headPitch, headRoll;
|
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw);
|
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headPitch);
|
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headRoll);
|
|
|
|
_headData->setYaw(headYaw);
|
|
_headData->setPitch(headPitch);
|
|
_headData->setRoll(headRoll);
|
|
|
|
// Head position relative to pelvis
|
|
memcpy(&_headData->_leanSideways, sourceBuffer, sizeof(_headData->_leanSideways));
|
|
sourceBuffer += sizeof(float);
|
|
memcpy(&_headData->_leanForward, sourceBuffer, sizeof(_headData->_leanForward));
|
|
sourceBuffer += sizeof(_headData->_leanForward);
|
|
|
|
// Hand Position - is relative to body position
|
|
glm::vec3 handPositionRelative;
|
|
memcpy(&handPositionRelative, sourceBuffer, sizeof(float) * 3);
|
|
_handPosition = _position + handPositionRelative;
|
|
sourceBuffer += sizeof(float) * 3;
|
|
|
|
// Lookat Position
|
|
memcpy(&_headData->_lookAtPosition, sourceBuffer, sizeof(_headData->_lookAtPosition));
|
|
sourceBuffer += sizeof(_headData->_lookAtPosition);
|
|
|
|
// Instantaneous audio loudness (used to drive facial animation)
|
|
//sourceBuffer += unpackFloatFromByte(sourceBuffer, _audioLoudness, MAX_AUDIO_LOUDNESS);
|
|
memcpy(&_headData->_audioLoudness, sourceBuffer, sizeof(float));
|
|
sourceBuffer += sizeof(float);
|
|
|
|
// camera details
|
|
memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));
|
|
sourceBuffer += sizeof(_cameraPosition);
|
|
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, _cameraOrientation);
|
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_cameraFov);
|
|
sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer,_cameraAspectRatio);
|
|
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraNearClip);
|
|
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraFarClip);
|
|
memcpy(&_cameraEyeOffsetPosition, sourceBuffer, sizeof(_cameraEyeOffsetPosition));
|
|
sourceBuffer += sizeof(_cameraEyeOffsetPosition);
|
|
|
|
// the rest is a chat message
|
|
int chatMessageSize = *sourceBuffer++;
|
|
_chatMessage = string((char*)sourceBuffer, chatMessageSize);
|
|
sourceBuffer += chatMessageSize * sizeof(char);
|
|
|
|
// voxel sending features...
|
|
unsigned char bitItems = 0;
|
|
bitItems = (unsigned char)*sourceBuffer++;
|
|
_wantLowResMoving = oneAtBit(bitItems, WANT_LOW_RES_MOVING_BIT);
|
|
_wantColor = oneAtBit(bitItems, WANT_COLOR_AT_BIT);
|
|
_wantDelta = oneAtBit(bitItems, WANT_DELTA_AT_BIT);
|
|
_wantOcclusionCulling = oneAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT);
|
|
|
|
// key state, stored as a semi-nibble in the bitItems
|
|
_keyState = (KeyState)getSemiNibbleAt(bitItems,KEY_STATE_START_BIT);
|
|
|
|
// hand state, stored as a semi-nibble in the bitItems
|
|
_handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT);
|
|
|
|
bitItems = (unsigned char)*sourceBuffer++;
|
|
_headData->_isFaceshiftConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED);
|
|
|
|
// If it is connected, pack up the data
|
|
if (_headData->_isFaceshiftConnected) {
|
|
memcpy(&_headData->_leftEyeBlink, sourceBuffer, sizeof(float));
|
|
sourceBuffer += sizeof(float);
|
|
|
|
memcpy(&_headData->_rightEyeBlink, sourceBuffer, sizeof(float));
|
|
sourceBuffer += sizeof(float);
|
|
|
|
memcpy(&_headData->_averageLoudness, sourceBuffer, sizeof(float));
|
|
sourceBuffer += sizeof(float);
|
|
|
|
memcpy(&_headData->_browAudioLift, sourceBuffer, sizeof(float));
|
|
sourceBuffer += sizeof(float);
|
|
}
|
|
|
|
// leap hand data
|
|
if (sourceBuffer - startPosition < numBytes) {
|
|
// check passed, bytes match
|
|
sourceBuffer += _handData->decodeRemoteData(sourceBuffer);
|
|
}
|
|
|
|
// skeleton joints
|
|
if (sourceBuffer - startPosition < numBytes) {
|
|
// check passed, bytes match
|
|
_joints.resize(*sourceBuffer++);
|
|
for (vector<JointData>::iterator it = _joints.begin(); it != _joints.end(); it++) {
|
|
it->jointID = *sourceBuffer++;
|
|
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, it->rotation);
|
|
}
|
|
}
|
|
|
|
return sourceBuffer - startPosition;
|
|
}
|
|
|
|
glm::vec3 AvatarData::calculateCameraDirection() const {
|
|
glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FRONT, 0.0f));
|
|
return direction;
|
|
}
|
|
|
|
|
|
// Allows sending of fixed-point numbers: radix 1 makes 15.1 number, radix 8 makes 8.8 number, etc
|
|
int packFloatScalarToSignedTwoByteFixed(unsigned char* buffer, float scalar, int radix) {
|
|
int16_t outVal = (int16_t)(scalar * (float)(1 << radix));
|
|
memcpy(buffer, &outVal, sizeof(uint16_t));
|
|
return sizeof(uint16_t);
|
|
}
|
|
|
|
int unpackFloatScalarFromSignedTwoByteFixed(int16_t* byteFixedPointer, float* destinationPointer, int radix) {
|
|
*destinationPointer = *byteFixedPointer / (float)(1 << radix);
|
|
return sizeof(int16_t);
|
|
}
|
|
|
|
int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix) {
|
|
const unsigned char* startPosition = destBuffer;
|
|
destBuffer += packFloatScalarToSignedTwoByteFixed(destBuffer, srcVector.x, radix);
|
|
destBuffer += packFloatScalarToSignedTwoByteFixed(destBuffer, srcVector.y, radix);
|
|
destBuffer += packFloatScalarToSignedTwoByteFixed(destBuffer, srcVector.z, radix);
|
|
return destBuffer - startPosition;
|
|
}
|
|
|
|
int unpackFloatVec3FromSignedTwoByteFixed(unsigned char* sourceBuffer, glm::vec3& destination, int radix) {
|
|
const unsigned char* startPosition = sourceBuffer;
|
|
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(destination.x), radix);
|
|
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(destination.y), radix);
|
|
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(destination.z), radix);
|
|
return sourceBuffer - startPosition;
|
|
}
|
|
|
|
|
|
int packFloatAngleToTwoByte(unsigned char* buffer, float angle) {
|
|
const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.0);
|
|
|
|
uint16_t angleHolder = floorf((angle + 180) * ANGLE_CONVERSION_RATIO);
|
|
memcpy(buffer, &angleHolder, sizeof(uint16_t));
|
|
|
|
return sizeof(uint16_t);
|
|
}
|
|
|
|
int unpackFloatAngleFromTwoByte(uint16_t* byteAnglePointer, float* destinationPointer) {
|
|
*destinationPointer = (*byteAnglePointer / (float) std::numeric_limits<uint16_t>::max()) * 360.0 - 180;
|
|
return sizeof(uint16_t);
|
|
}
|
|
|
|
int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput) {
|
|
const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 2.0);
|
|
uint16_t quatParts[4];
|
|
quatParts[0] = floorf((quatInput.x + 1.0) * QUAT_PART_CONVERSION_RATIO);
|
|
quatParts[1] = floorf((quatInput.y + 1.0) * QUAT_PART_CONVERSION_RATIO);
|
|
quatParts[2] = floorf((quatInput.z + 1.0) * QUAT_PART_CONVERSION_RATIO);
|
|
quatParts[3] = floorf((quatInput.w + 1.0) * QUAT_PART_CONVERSION_RATIO);
|
|
|
|
memcpy(buffer, &quatParts, sizeof(quatParts));
|
|
return sizeof(quatParts);
|
|
}
|
|
|
|
int unpackOrientationQuatFromBytes(unsigned char* buffer, glm::quat& quatOutput) {
|
|
uint16_t quatParts[4];
|
|
memcpy(&quatParts, buffer, sizeof(quatParts));
|
|
|
|
quatOutput.x = ((quatParts[0] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
|
|
quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
|
|
quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
|
|
quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
|
|
|
|
return sizeof(quatParts);
|
|
}
|
|
|
|
float SMALL_LIMIT = 10.0;
|
|
float LARGE_LIMIT = 1000.0;
|
|
|
|
int packFloatRatioToTwoByte(unsigned char* buffer, float ratio) {
|
|
// if the ratio is less than 10, then encode it as a positive number scaled from 0 to int16::max()
|
|
int16_t ratioHolder;
|
|
|
|
if (ratio < SMALL_LIMIT) {
|
|
const float SMALL_RATIO_CONVERSION_RATIO = (std::numeric_limits<int16_t>::max() / SMALL_LIMIT);
|
|
ratioHolder = floorf(ratio * SMALL_RATIO_CONVERSION_RATIO);
|
|
} else {
|
|
const float LARGE_RATIO_CONVERSION_RATIO = std::numeric_limits<int16_t>::min() / LARGE_LIMIT;
|
|
ratioHolder = floorf((std::min(ratio,LARGE_LIMIT) - SMALL_LIMIT) * LARGE_RATIO_CONVERSION_RATIO);
|
|
}
|
|
memcpy(buffer, &ratioHolder, sizeof(ratioHolder));
|
|
return sizeof(ratioHolder);
|
|
}
|
|
|
|
int unpackFloatRatioFromTwoByte(unsigned char* buffer, float& ratio) {
|
|
int16_t ratioHolder;
|
|
memcpy(&ratioHolder, buffer, sizeof(ratioHolder));
|
|
|
|
// If it's positive, than the original ratio was less than SMALL_LIMIT
|
|
if (ratioHolder > 0) {
|
|
ratio = (ratioHolder / (float) std::numeric_limits<int16_t>::max()) * SMALL_LIMIT;
|
|
} else {
|
|
// If it's negative, than the original ratio was between SMALL_LIMIT and LARGE_LIMIT
|
|
ratio = ((ratioHolder / (float) std::numeric_limits<int16_t>::min()) * LARGE_LIMIT) + SMALL_LIMIT;
|
|
}
|
|
return sizeof(ratioHolder);
|
|
}
|
|
|
|
int packClipValueToTwoByte(unsigned char* buffer, float clipValue) {
|
|
// Clip values must be less than max signed 16bit integers
|
|
assert(clipValue < std::numeric_limits<int16_t>::max());
|
|
int16_t holder;
|
|
|
|
// if the clip is less than 10, then encode it as a positive number scaled from 0 to int16::max()
|
|
if (clipValue < SMALL_LIMIT) {
|
|
const float SMALL_RATIO_CONVERSION_RATIO = (std::numeric_limits<int16_t>::max() / SMALL_LIMIT);
|
|
holder = floorf(clipValue * SMALL_RATIO_CONVERSION_RATIO);
|
|
} else {
|
|
// otherwise we store it as a negative integer
|
|
holder = -1 * floorf(clipValue);
|
|
}
|
|
memcpy(buffer, &holder, sizeof(holder));
|
|
return sizeof(holder);
|
|
}
|
|
|
|
int unpackClipValueFromTwoByte(unsigned char* buffer, float& clipValue) {
|
|
int16_t holder;
|
|
memcpy(&holder, buffer, sizeof(holder));
|
|
|
|
// If it's positive, than the original clipValue was less than SMALL_LIMIT
|
|
if (holder > 0) {
|
|
clipValue = (holder / (float) std::numeric_limits<int16_t>::max()) * SMALL_LIMIT;
|
|
} else {
|
|
// If it's negative, than the original holder can be found as the opposite sign of holder
|
|
clipValue = -1.0f * holder;
|
|
}
|
|
return sizeof(holder);
|
|
}
|
|
|
|
int packFloatToByte(unsigned char* buffer, float value, float scaleBy) {
|
|
unsigned char holder;
|
|
const float CONVERSION_RATIO = (255 / scaleBy);
|
|
holder = floorf(value * CONVERSION_RATIO);
|
|
memcpy(buffer, &holder, sizeof(holder));
|
|
return sizeof(holder);
|
|
}
|
|
|
|
int unpackFloatFromByte(unsigned char* buffer, float& value, float scaleBy) {
|
|
unsigned char holder;
|
|
memcpy(&holder, buffer, sizeof(holder));
|
|
value = ((float)holder / (float) 255) * scaleBy;
|
|
return sizeof(holder);
|
|
}
|