overte-HifiExperiments/libraries/avatars/src/AvatarData.cpp
2013-09-13 16:46:48 -07:00

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);
}