mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 21:43:03 +02:00
Merge pull request #2691 from birarda/agent-avatar-manager
expose AvatarHashMap to Agent for Avatar querying from assignment-client
This commit is contained in:
commit
220c9c40f3
12 changed files with 223 additions and 140 deletions
|
@ -33,7 +33,8 @@ Agent::Agent(const QByteArray& packet) :
|
||||||
ThreadedAssignment(packet),
|
ThreadedAssignment(packet),
|
||||||
_voxelEditSender(),
|
_voxelEditSender(),
|
||||||
_particleEditSender(),
|
_particleEditSender(),
|
||||||
_receivedAudioBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO)
|
_receivedAudioBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO),
|
||||||
|
_avatarHashMap()
|
||||||
{
|
{
|
||||||
// be the parent of the script engine so it gets moved when we do
|
// be the parent of the script engine so it gets moved when we do
|
||||||
_scriptEngine.setParent(this);
|
_scriptEngine.setParent(this);
|
||||||
|
@ -131,6 +132,16 @@ void Agent::readPendingDatagrams() {
|
||||||
// let this continue through to the NodeList so it updates last heard timestamp
|
// let this continue through to the NodeList so it updates last heard timestamp
|
||||||
// for the sending audio mixer
|
// for the sending audio mixer
|
||||||
NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket);
|
NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket);
|
||||||
|
} else if (datagramPacketType == PacketTypeBulkAvatarData
|
||||||
|
|| datagramPacketType == PacketTypeAvatarIdentity
|
||||||
|
|| datagramPacketType == PacketTypeAvatarBillboard
|
||||||
|
|| datagramPacketType == PacketTypeKillAvatar) {
|
||||||
|
// let the avatar hash map process it
|
||||||
|
_avatarHashMap.processAvatarMixerDatagram(receivedPacket, nodeList->sendingNodeForPacket(receivedPacket));
|
||||||
|
|
||||||
|
// let this continue through to the NodeList so it updates last heard timestamp
|
||||||
|
// for the sending avatar-mixer
|
||||||
|
NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket);
|
||||||
} else {
|
} else {
|
||||||
NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket);
|
NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket);
|
||||||
}
|
}
|
||||||
|
@ -191,6 +202,7 @@ void Agent::run() {
|
||||||
|
|
||||||
// give this AvatarData object to the script engine
|
// give this AvatarData object to the script engine
|
||||||
_scriptEngine.setAvatarData(&scriptedAvatar, "Avatar");
|
_scriptEngine.setAvatarData(&scriptedAvatar, "Avatar");
|
||||||
|
_scriptEngine.setAvatarHashMap(&_avatarHashMap, "AvatarList");
|
||||||
|
|
||||||
// register ourselves to the script engine
|
// register ourselves to the script engine
|
||||||
_scriptEngine.registerGlobalObject("Agent", this);
|
_scriptEngine.registerGlobalObject("Agent", this);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
|
|
||||||
|
#include <AvatarHashMap.h>
|
||||||
#include <MixedAudioRingBuffer.h>
|
#include <MixedAudioRingBuffer.h>
|
||||||
#include <ParticleEditPacketSender.h>
|
#include <ParticleEditPacketSender.h>
|
||||||
#include <ParticleTree.h>
|
#include <ParticleTree.h>
|
||||||
|
@ -65,6 +66,7 @@ private:
|
||||||
VoxelTreeHeadlessViewer _voxelViewer;
|
VoxelTreeHeadlessViewer _voxelViewer;
|
||||||
|
|
||||||
MixedAudioRingBuffer _receivedAudioBuffer;
|
MixedAudioRingBuffer _receivedAudioBuffer;
|
||||||
|
AvatarHashMap _avatarHashMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Agent_h
|
#endif // hifi_Agent_h
|
||||||
|
|
|
@ -56,7 +56,6 @@ Avatar::Avatar() :
|
||||||
_mouseRayOrigin(0.0f, 0.0f, 0.0f),
|
_mouseRayOrigin(0.0f, 0.0f, 0.0f),
|
||||||
_mouseRayDirection(0.0f, 0.0f, 0.0f),
|
_mouseRayDirection(0.0f, 0.0f, 0.0f),
|
||||||
_moving(false),
|
_moving(false),
|
||||||
_owningAvatarMixer(),
|
|
||||||
_collisionFlags(0),
|
_collisionFlags(0),
|
||||||
_initialized(false),
|
_initialized(false),
|
||||||
_shouldRenderBillboard(true)
|
_shouldRenderBillboard(true)
|
||||||
|
@ -685,6 +684,11 @@ void Avatar::setBillboard(const QByteArray& billboard) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int Avatar::parseDataAtOffset(const QByteArray& packet, int offset) {
|
int Avatar::parseDataAtOffset(const QByteArray& packet, int offset) {
|
||||||
|
if (!_initialized) {
|
||||||
|
// now that we have data for this Avatar we are go for init
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
// change in position implies movement
|
// change in position implies movement
|
||||||
glm::vec3 oldPosition = _position;
|
glm::vec3 oldPosition = _position;
|
||||||
|
|
||||||
|
|
|
@ -99,9 +99,6 @@ public:
|
||||||
|
|
||||||
/// Returns the distance to use as a LOD parameter.
|
/// Returns the distance to use as a LOD parameter.
|
||||||
float getLODDistance() const;
|
float getLODDistance() const;
|
||||||
|
|
||||||
Node* getOwningAvatarMixer() { return _owningAvatarMixer.data(); }
|
|
||||||
void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; }
|
|
||||||
|
|
||||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||||
|
|
||||||
|
@ -177,7 +174,6 @@ protected:
|
||||||
glm::vec3 _mouseRayDirection;
|
glm::vec3 _mouseRayDirection;
|
||||||
float _stringLength;
|
float _stringLength;
|
||||||
bool _moving; ///< set when position is changing
|
bool _moving; ///< set when position is changing
|
||||||
QWeakPointer<Node> _owningAvatarMixer;
|
|
||||||
|
|
||||||
uint32_t _collisionFlags;
|
uint32_t _collisionFlags;
|
||||||
|
|
||||||
|
|
|
@ -51,14 +51,16 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
// simulate avatars
|
// simulate avatars
|
||||||
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
||||||
while (avatarIterator != _avatarHash.end()) {
|
while (avatarIterator != _avatarHash.end()) {
|
||||||
Avatar* avatar = static_cast<Avatar*>(avatarIterator.value().data());
|
AvatarSharedPointer sharedAvatar = avatarIterator.value();
|
||||||
if (avatar == static_cast<Avatar*>(_myAvatar.data()) || !avatar->isInitialized()) {
|
Avatar* avatar = reinterpret_cast<Avatar*>(sharedAvatar.data());
|
||||||
|
|
||||||
|
if (sharedAvatar == _myAvatar || !avatar->isInitialized()) {
|
||||||
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
||||||
// DO NOT update uninitialized Avatars
|
// DO NOT update uninitialized Avatars
|
||||||
++avatarIterator;
|
++avatarIterator;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (avatar->getOwningAvatarMixer()) {
|
if (!shouldKillAvatar(sharedAvatar)) {
|
||||||
// this avatar's mixer is still around, go ahead and simulate it
|
// this avatar's mixer is still around, go ahead and simulate it
|
||||||
avatar->simulate(deltaTime);
|
avatar->simulate(deltaTime);
|
||||||
avatar->setMouseRay(mouseOrigin, mouseDirection);
|
avatar->setMouseRay(mouseOrigin, mouseDirection);
|
||||||
|
@ -127,127 +129,12 @@ void AvatarManager::renderAvatarFades(const glm::vec3& cameraPosition, Avatar::R
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarSharedPointer AvatarManager::matchingOrNewAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
||||||
AvatarSharedPointer matchingAvatar = _avatarHash.value(sessionUUID);
|
return AvatarSharedPointer(new Avatar());
|
||||||
|
|
||||||
if (!matchingAvatar) {
|
|
||||||
// construct a new Avatar for this node
|
|
||||||
Avatar* avatar = new Avatar();
|
|
||||||
avatar->setOwningAvatarMixer(mixerWeakPointer);
|
|
||||||
|
|
||||||
// insert the new avatar into our hash
|
|
||||||
matchingAvatar = AvatarSharedPointer(avatar);
|
|
||||||
_avatarHash.insert(sessionUUID, matchingAvatar);
|
|
||||||
|
|
||||||
qDebug() << "Adding avatar with sessionUUID " << sessionUUID << "to AvatarManager hash.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchingAvatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer) {
|
|
||||||
switch (packetTypeForPacket(datagram)) {
|
|
||||||
case PacketTypeBulkAvatarData:
|
|
||||||
processAvatarDataPacket(datagram, mixerWeakPointer);
|
|
||||||
break;
|
|
||||||
case PacketTypeAvatarIdentity:
|
|
||||||
processAvatarIdentityPacket(datagram, mixerWeakPointer);
|
|
||||||
break;
|
|
||||||
case PacketTypeAvatarBillboard:
|
|
||||||
processAvatarBillboardPacket(datagram, mixerWeakPointer);
|
|
||||||
break;
|
|
||||||
case PacketTypeKillAvatar:
|
|
||||||
processKillAvatar(datagram);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarManager::processAvatarDataPacket(const QByteArray &datagram, const QWeakPointer<Node> &mixerWeakPointer) {
|
|
||||||
int bytesRead = numBytesForPacketHeader(datagram);
|
|
||||||
|
|
||||||
// enumerate over all of the avatars in this packet
|
|
||||||
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
|
|
||||||
while (bytesRead < datagram.size() && mixerWeakPointer.data()) {
|
|
||||||
QUuid sessionUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID));
|
|
||||||
bytesRead += NUM_BYTES_RFC4122_UUID;
|
|
||||||
|
|
||||||
AvatarSharedPointer matchingAvatarData = matchingOrNewAvatar(sessionUUID, mixerWeakPointer);
|
|
||||||
|
|
||||||
// have the matching (or new) avatar parse the data from the packet
|
|
||||||
bytesRead += matchingAvatarData->parseDataAtOffset(datagram, bytesRead);
|
|
||||||
|
|
||||||
Avatar* matchingAvatar = reinterpret_cast<Avatar*>(matchingAvatarData.data());
|
|
||||||
|
|
||||||
if (!matchingAvatar->isInitialized()) {
|
|
||||||
// now that we have AvatarData for this Avatar we are go for init
|
|
||||||
matchingAvatar->init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet, const QWeakPointer<Node>& mixerWeakPointer) {
|
|
||||||
// setup a data stream to parse the packet
|
|
||||||
QDataStream identityStream(packet);
|
|
||||||
identityStream.skipRawData(numBytesForPacketHeader(packet));
|
|
||||||
|
|
||||||
QUuid sessionUUID;
|
|
||||||
|
|
||||||
while (!identityStream.atEnd()) {
|
|
||||||
|
|
||||||
QUrl faceMeshURL, skeletonURL;
|
|
||||||
QString displayName;
|
|
||||||
identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> displayName;
|
|
||||||
|
|
||||||
// mesh URL for a UUID, find avatar in our list
|
|
||||||
AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(sessionUUID, mixerWeakPointer);
|
|
||||||
if (matchingAvatar) {
|
|
||||||
Avatar* avatar = static_cast<Avatar*>(matchingAvatar.data());
|
|
||||||
|
|
||||||
if (avatar->getFaceModelURL() != faceMeshURL) {
|
|
||||||
avatar->setFaceModelURL(faceMeshURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avatar->getSkeletonModelURL() != skeletonURL) {
|
|
||||||
avatar->setSkeletonModelURL(skeletonURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avatar->getDisplayName() != displayName) {
|
|
||||||
avatar->setDisplayName(displayName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarManager::processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer) {
|
|
||||||
int headerSize = numBytesForPacketHeader(packet);
|
|
||||||
QUuid sessionUUID = QUuid::fromRfc4122(QByteArray::fromRawData(packet.constData() + headerSize, NUM_BYTES_RFC4122_UUID));
|
|
||||||
|
|
||||||
AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(sessionUUID, mixerWeakPointer);
|
|
||||||
if (matchingAvatar) {
|
|
||||||
Avatar* avatar = static_cast<Avatar*>(matchingAvatar.data());
|
|
||||||
QByteArray billboard = packet.mid(headerSize + NUM_BYTES_RFC4122_UUID);
|
|
||||||
if (avatar->getBillboard() != billboard) {
|
|
||||||
avatar->setBillboard(billboard);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarManager::processKillAvatar(const QByteArray& datagram) {
|
|
||||||
// read the node id
|
|
||||||
QUuid sessionUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(datagram), NUM_BYTES_RFC4122_UUID));
|
|
||||||
|
|
||||||
// remove the avatar with that UUID from our hash, if it exists
|
|
||||||
AvatarHash::iterator matchedAvatar = _avatarHash.find(sessionUUID);
|
|
||||||
if (matchedAvatar != _avatarHash.end()) {
|
|
||||||
erase(matchedAvatar);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator) {
|
AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator) {
|
||||||
if (iterator.key() != MY_AVATAR_KEY) {
|
if (iterator.key() != MY_AVATAR_KEY) {
|
||||||
qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash.";
|
|
||||||
if (reinterpret_cast<Avatar*>(iterator.value().data())->isInitialized()) {
|
if (reinterpret_cast<Avatar*>(iterator.value().data())->isInitialized()) {
|
||||||
_avatarFades.push_back(iterator.value());
|
_avatarFades.push_back(iterator.value());
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
class MyAvatar;
|
class MyAvatar;
|
||||||
|
|
||||||
class AvatarManager : public QObject, public AvatarHashMap {
|
class AvatarManager : public AvatarHashMap {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
AvatarManager(QObject* parent = 0);
|
AvatarManager(QObject* parent = 0);
|
||||||
|
@ -35,23 +35,15 @@ public:
|
||||||
void renderAvatars(Avatar::RenderMode renderMode, bool selfAvatarOnly = false);
|
void renderAvatars(Avatar::RenderMode renderMode, bool selfAvatarOnly = false);
|
||||||
|
|
||||||
void clearOtherAvatars();
|
void clearOtherAvatars();
|
||||||
|
|
||||||
public slots:
|
|
||||||
void processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AvatarManager(const AvatarManager& other);
|
AvatarManager(const AvatarManager& other);
|
||||||
|
|
||||||
AvatarSharedPointer matchingOrNewAvatar(const QUuid& nodeUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
|
||||||
|
|
||||||
void processAvatarDataPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
|
||||||
void processAvatarIdentityPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
|
||||||
void processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
|
||||||
void processKillAvatar(const QByteArray& datagram);
|
|
||||||
|
|
||||||
void simulateAvatarFades(float deltaTime);
|
void simulateAvatarFades(float deltaTime);
|
||||||
void renderAvatarFades(const glm::vec3& cameraPosition, Avatar::RenderMode renderMode);
|
void renderAvatarFades(const glm::vec3& cameraPosition, Avatar::RenderMode renderMode);
|
||||||
|
|
||||||
|
AvatarSharedPointer newSharedAvatar();
|
||||||
|
|
||||||
// virtual override
|
// virtual override
|
||||||
AvatarHash::iterator erase(const AvatarHash::iterator& iterator);
|
AvatarHash::iterator erase(const AvatarHash::iterator& iterator);
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,9 @@ AvatarData::AvatarData() :
|
||||||
_displayNameTargetAlpha(0.0f),
|
_displayNameTargetAlpha(0.0f),
|
||||||
_displayNameAlpha(0.0f),
|
_displayNameAlpha(0.0f),
|
||||||
_billboard(),
|
_billboard(),
|
||||||
_errorLogExpiry(0)
|
_errorLogExpiry(0),
|
||||||
|
_owningAvatarMixer(),
|
||||||
|
_lastUpdateTimer()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -193,6 +195,10 @@ bool AvatarData::shouldLogError(const quint64& now) {
|
||||||
|
|
||||||
// read data in packet starting at byte offset and return number of bytes parsed
|
// read data in packet starting at byte offset and return number of bytes parsed
|
||||||
int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) {
|
int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) {
|
||||||
|
|
||||||
|
// reset the last heard timer since we have new data for this AvatarData
|
||||||
|
_lastUpdateTimer.restart();
|
||||||
|
|
||||||
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
||||||
if (!_headData) {
|
if (!_headData) {
|
||||||
_headData = new HeadData(this);
|
_headData = new HeadData(this);
|
||||||
|
|
|
@ -32,6 +32,7 @@ typedef unsigned long long quint64;
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
#include <QtCore/QElapsedTimer>
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
#include <QtCore/QHash>
|
#include <QtCore/QHash>
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
|
@ -44,6 +45,7 @@ typedef unsigned long long quint64;
|
||||||
|
|
||||||
#include <CollisionInfo.h>
|
#include <CollisionInfo.h>
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
|
#include <Node.h>
|
||||||
|
|
||||||
#include "HeadData.h"
|
#include "HeadData.h"
|
||||||
#include "HandData.h"
|
#include "HandData.h"
|
||||||
|
@ -220,6 +222,11 @@ public:
|
||||||
QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); }
|
QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); }
|
||||||
void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); }
|
void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); }
|
||||||
|
|
||||||
|
Node* getOwningAvatarMixer() { return _owningAvatarMixer.data(); }
|
||||||
|
void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; }
|
||||||
|
|
||||||
|
QElapsedTimer& getLastUpdateTimer() { return _lastUpdateTimer; }
|
||||||
|
|
||||||
virtual float getBoundingRadius() const { return 1.f; }
|
virtual float getBoundingRadius() const { return 1.f; }
|
||||||
|
|
||||||
static void setNetworkAccessManager(QNetworkAccessManager* sharedAccessManager) { networkAccessManager = sharedAccessManager; }
|
static void setNetworkAccessManager(QNetworkAccessManager* sharedAccessManager) { networkAccessManager = sharedAccessManager; }
|
||||||
|
@ -278,7 +285,10 @@ protected:
|
||||||
static QNetworkAccessManager* networkAccessManager;
|
static QNetworkAccessManager* networkAccessManager;
|
||||||
|
|
||||||
quint64 _errorLogExpiry; ///< time in future when to log an error
|
quint64 _errorLogExpiry; ///< time in future when to log an error
|
||||||
|
|
||||||
|
QWeakPointer<Node> _owningAvatarMixer;
|
||||||
|
QElapsedTimer _lastUpdateTimer;
|
||||||
|
|
||||||
/// Loads the joint indices, names from the FST file (if any)
|
/// Loads the joint indices, names from the FST file (if any)
|
||||||
virtual void updateJointMappings();
|
virtual void updateJointMappings();
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,14 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <PacketHeaders.h>
|
||||||
|
|
||||||
#include "AvatarHashMap.h"
|
#include "AvatarHashMap.h"
|
||||||
|
|
||||||
AvatarHashMap::AvatarHashMap() :
|
AvatarHashMap::AvatarHashMap() :
|
||||||
_avatarHash()
|
_avatarHash()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarHashMap::insert(const QUuid& id, AvatarSharedPointer avatar) {
|
void AvatarHashMap::insert(const QUuid& id, AvatarSharedPointer avatar) {
|
||||||
|
@ -22,6 +25,150 @@ void AvatarHashMap::insert(const QUuid& id, AvatarSharedPointer avatar) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarHash::iterator AvatarHashMap::erase(const AvatarHash::iterator& iterator) {
|
AvatarHash::iterator AvatarHashMap::erase(const AvatarHash::iterator& iterator) {
|
||||||
|
qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarHashMap.";
|
||||||
return _avatarHash.erase(iterator);
|
return _avatarHash.erase(iterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const qint64 AVATAR_SILENCE_THRESHOLD_MSECS = 5 * 1000;
|
||||||
|
|
||||||
|
bool AvatarHashMap::shouldKillAvatar(const AvatarSharedPointer& sharedAvatar) {
|
||||||
|
return (sharedAvatar->getOwningAvatarMixer() == NULL
|
||||||
|
|| sharedAvatar->getLastUpdateTimer().elapsed() > AVATAR_SILENCE_THRESHOLD_MSECS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarHashMap::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||||
|
switch (packetTypeForPacket(datagram)) {
|
||||||
|
case PacketTypeBulkAvatarData:
|
||||||
|
processAvatarDataPacket(datagram, mixerWeakPointer);
|
||||||
|
break;
|
||||||
|
case PacketTypeAvatarIdentity:
|
||||||
|
processAvatarIdentityPacket(datagram, mixerWeakPointer);
|
||||||
|
break;
|
||||||
|
case PacketTypeAvatarBillboard:
|
||||||
|
processAvatarBillboardPacket(datagram, mixerWeakPointer);
|
||||||
|
break;
|
||||||
|
case PacketTypeKillAvatar:
|
||||||
|
processKillAvatar(datagram);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvatarHashMap::containsAvatarWithDisplayName(const QString& displayName) {
|
||||||
|
|
||||||
|
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
||||||
|
while (avatarIterator != _avatarHash.end()) {
|
||||||
|
AvatarSharedPointer sharedAvatar = avatarIterator.value();
|
||||||
|
if (avatarIterator.value()->getDisplayName() == displayName) {
|
||||||
|
// this is a match
|
||||||
|
// check if this avatar should still be around
|
||||||
|
if (!shouldKillAvatar(sharedAvatar)) {
|
||||||
|
// we have a match, return true
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// we should remove this avatar, do that now
|
||||||
|
erase(avatarIterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
++avatarIterator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return false, no match
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarSharedPointer AvatarHashMap::newSharedAvatar() {
|
||||||
|
return AvatarSharedPointer(new AvatarData());
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarSharedPointer AvatarHashMap::matchingOrNewAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||||
|
AvatarSharedPointer matchingAvatar = _avatarHash.value(sessionUUID);
|
||||||
|
|
||||||
|
if (!matchingAvatar) {
|
||||||
|
// insert the new avatar into our hash
|
||||||
|
matchingAvatar = newSharedAvatar();
|
||||||
|
|
||||||
|
qDebug() << "Adding avatar with sessionUUID " << sessionUUID << "to AvatarHashMap.";
|
||||||
|
_avatarHash.insert(sessionUUID, matchingAvatar);
|
||||||
|
|
||||||
|
matchingAvatar->setOwningAvatarMixer(mixerWeakPointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingAvatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarHashMap::processAvatarDataPacket(const QByteArray &datagram, const QWeakPointer<Node> &mixerWeakPointer) {
|
||||||
|
int bytesRead = numBytesForPacketHeader(datagram);
|
||||||
|
|
||||||
|
// enumerate over all of the avatars in this packet
|
||||||
|
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
|
||||||
|
while (bytesRead < datagram.size() && mixerWeakPointer.data()) {
|
||||||
|
QUuid sessionUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID));
|
||||||
|
bytesRead += NUM_BYTES_RFC4122_UUID;
|
||||||
|
|
||||||
|
AvatarSharedPointer matchingAvatarData = matchingOrNewAvatar(sessionUUID, mixerWeakPointer);
|
||||||
|
|
||||||
|
// have the matching (or new) avatar parse the data from the packet
|
||||||
|
bytesRead += matchingAvatarData->parseDataAtOffset(datagram, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarHashMap::processAvatarIdentityPacket(const QByteArray &packet, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||||
|
// setup a data stream to parse the packet
|
||||||
|
QDataStream identityStream(packet);
|
||||||
|
identityStream.skipRawData(numBytesForPacketHeader(packet));
|
||||||
|
|
||||||
|
QUuid sessionUUID;
|
||||||
|
|
||||||
|
while (!identityStream.atEnd()) {
|
||||||
|
|
||||||
|
QUrl faceMeshURL, skeletonURL;
|
||||||
|
QString displayName;
|
||||||
|
identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> displayName;
|
||||||
|
|
||||||
|
// mesh URL for a UUID, find avatar in our list
|
||||||
|
AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(sessionUUID, mixerWeakPointer);
|
||||||
|
if (matchingAvatar) {
|
||||||
|
|
||||||
|
if (matchingAvatar->getFaceModelURL() != faceMeshURL) {
|
||||||
|
matchingAvatar->setFaceModelURL(faceMeshURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingAvatar->getSkeletonModelURL() != skeletonURL) {
|
||||||
|
matchingAvatar->setSkeletonModelURL(skeletonURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingAvatar->getDisplayName() != displayName) {
|
||||||
|
matchingAvatar->setDisplayName(displayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarHashMap::processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||||
|
int headerSize = numBytesForPacketHeader(packet);
|
||||||
|
QUuid sessionUUID = QUuid::fromRfc4122(QByteArray::fromRawData(packet.constData() + headerSize, NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
|
AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(sessionUUID, mixerWeakPointer);
|
||||||
|
if (matchingAvatar) {
|
||||||
|
QByteArray billboard = packet.mid(headerSize + NUM_BYTES_RFC4122_UUID);
|
||||||
|
if (matchingAvatar->getBillboard() != billboard) {
|
||||||
|
matchingAvatar->setBillboard(billboard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarHashMap::processKillAvatar(const QByteArray& datagram) {
|
||||||
|
// read the node id
|
||||||
|
QUuid sessionUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(datagram), NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
|
// remove the avatar with that UUID from our hash, if it exists
|
||||||
|
AvatarHash::iterator matchedAvatar = _avatarHash.find(sessionUUID);
|
||||||
|
if (matchedAvatar != _avatarHash.end()) {
|
||||||
|
erase(matchedAvatar);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,12 +16,15 @@
|
||||||
#include <QtCore/QSharedPointer>
|
#include <QtCore/QSharedPointer>
|
||||||
#include <QtCore/QUuid>
|
#include <QtCore/QUuid>
|
||||||
|
|
||||||
|
#include <Node.h>
|
||||||
|
|
||||||
#include "AvatarData.h"
|
#include "AvatarData.h"
|
||||||
|
|
||||||
typedef QSharedPointer<AvatarData> AvatarSharedPointer;
|
typedef QSharedPointer<AvatarData> AvatarSharedPointer;
|
||||||
typedef QHash<QUuid, AvatarSharedPointer> AvatarHash;
|
typedef QHash<QUuid, AvatarSharedPointer> AvatarHash;
|
||||||
|
|
||||||
class AvatarHashMap {
|
class AvatarHashMap : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
AvatarHashMap();
|
AvatarHashMap();
|
||||||
|
|
||||||
|
@ -29,9 +32,23 @@ public:
|
||||||
int size() const { return _avatarHash.size(); }
|
int size() const { return _avatarHash.size(); }
|
||||||
|
|
||||||
virtual void insert(const QUuid& id, AvatarSharedPointer avatar);
|
virtual void insert(const QUuid& id, AvatarSharedPointer avatar);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer);
|
||||||
|
bool containsAvatarWithDisplayName(const QString& displayName);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual AvatarHash::iterator erase(const AvatarHash::iterator& iterator);
|
virtual AvatarHash::iterator erase(const AvatarHash::iterator& iterator);
|
||||||
|
|
||||||
|
bool shouldKillAvatar(const AvatarSharedPointer& sharedAvatar);
|
||||||
|
|
||||||
|
virtual AvatarSharedPointer newSharedAvatar();
|
||||||
|
AvatarSharedPointer matchingOrNewAvatar(const QUuid& nodeUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||||
|
|
||||||
|
void processAvatarDataPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
||||||
|
void processAvatarIdentityPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
||||||
|
void processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
||||||
|
void processKillAvatar(const QByteArray& datagram);
|
||||||
|
|
||||||
AvatarHash _avatarHash;
|
AvatarHash _avatarHash;
|
||||||
};
|
};
|
||||||
|
|
|
@ -156,6 +156,14 @@ void ScriptEngine::setAvatarData(AvatarData* avatarData, const QString& objectNa
|
||||||
registerGlobalObject(objectName, _avatarData);
|
registerGlobalObject(objectName, _avatarData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::setAvatarHashMap(AvatarHashMap* avatarHashMap, const QString& objectName) {
|
||||||
|
// remove the old Avatar property, if it exists
|
||||||
|
_engine.globalObject().setProperty(objectName, QScriptValue());
|
||||||
|
|
||||||
|
// give the script engine the new avatar hash map
|
||||||
|
registerGlobalObject(objectName, avatarHashMap);
|
||||||
|
}
|
||||||
|
|
||||||
bool ScriptEngine::setScriptContents(const QString& scriptContents, const QString& fileNameString) {
|
bool ScriptEngine::setScriptContents(const QString& scriptContents, const QString& fileNameString) {
|
||||||
if (_isRunning) {
|
if (_isRunning) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <VoxelsScriptingInterface.h>
|
#include <VoxelsScriptingInterface.h>
|
||||||
|
|
||||||
#include <AvatarData.h>
|
#include <AvatarData.h>
|
||||||
|
#include <AvatarHashMap.h>
|
||||||
|
|
||||||
#include "AnimationCache.h"
|
#include "AnimationCache.h"
|
||||||
#include "AbstractControllerScriptingInterface.h"
|
#include "AbstractControllerScriptingInterface.h"
|
||||||
|
@ -63,6 +64,7 @@ public:
|
||||||
bool isAvatar() const { return _isAvatar; }
|
bool isAvatar() const { return _isAvatar; }
|
||||||
|
|
||||||
void setAvatarData(AvatarData* avatarData, const QString& objectName);
|
void setAvatarData(AvatarData* avatarData, const QString& objectName);
|
||||||
|
void setAvatarHashMap(AvatarHashMap* avatarHashMap, const QString& objectName);
|
||||||
|
|
||||||
bool isListeningToAudioStream() const { return _isListeningToAudioStream; }
|
bool isListeningToAudioStream() const { return _isListeningToAudioStream; }
|
||||||
void setIsListeningToAudioStream(bool isListeningToAudioStream) { _isListeningToAudioStream = isListeningToAudioStream; }
|
void setIsListeningToAudioStream(bool isListeningToAudioStream) { _isListeningToAudioStream = isListeningToAudioStream; }
|
||||||
|
|
Loading…
Reference in a new issue