Merge pull request #1669 from birarda/avatar-list

add an AvatarManager to house a separate list of Avatars from NodeList
This commit is contained in:
AndrewMeadows 2014-01-24 09:54:17 -08:00
commit ea681f0d8f
42 changed files with 514 additions and 453 deletions

View file

@ -30,7 +30,8 @@ const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000;
AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes)
{
// make sure we hear about node kills so we can tell the other nodes
connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled);
}
unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) {
@ -46,7 +47,7 @@ unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *no
void attachAvatarDataToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
newNode->setLinkedData(new AvatarData(newNode));
newNode->setLinkedData(new AvatarData());
}
}
@ -116,6 +117,22 @@ void broadcastAvatarData() {
}
}
void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
if (killedNode->getType() == NODE_TYPE_AGENT
&& killedNode->getLinkedData()) {
// this was an avatar we were sending to other people
// send a kill packet for it to our other nodes
unsigned char packetData[MAX_PACKET_SIZE];
int numHeaderBytes = populateTypeAndVersion(packetData, PACKET_TYPE_KILL_AVATAR);
QByteArray rfcUUID = killedNode->getUUID().toRfc4122();
memcpy(packetData + numHeaderBytes, rfcUUID.constData(), rfcUUID.size());
NodeList::getInstance()->broadcastToNodes(packetData, numHeaderBytes + NUM_BYTES_RFC4122_UUID,
QSet<NODE_TYPE>() << NODE_TYPE_AGENT);
}
}
void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
NodeList* nodeList = NodeList::getInstance();
@ -136,7 +153,10 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc
}
break;
}
case PACKET_TYPE_KILL_NODE:
case PACKET_TYPE_KILL_AVATAR: {
nodeList->processKillNode(dataByteArray);
break;
}
default:
// hand this off to the NodeList
nodeList->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());

View file

@ -19,6 +19,8 @@ public:
public slots:
/// runs the avatar mixer
void run();
void nodeKilled(SharedNodePointer killedNode);
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
};

View file

@ -532,7 +532,7 @@ void DomainServer::addReleasedAssignmentBackToQueue(Assignment* releasedAssignme
void DomainServer::nodeKilled(SharedNodePointer node) {
// if this node has linked data it was from an assignment
if (node->getLinkedData()) {
Assignment* nodeAssignment = (Assignment*) node->getLinkedData();
Assignment* nodeAssignment = (Assignment*) node->getLinkedData();
addReleasedAssignmentBackToQueue(nodeAssignment);
}

View file

@ -116,6 +116,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_voxelImporter(_window),
_wantToKillLocalVoxels(false),
_audioScope(256, 200, true),
_avatarManager(),
_profile(QString()),
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
_mouseX(0),
@ -138,8 +139,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_nudgeStarted(false),
_lookingAlongX(false),
_lookingAwayFromOrigin(true),
_lookatTargetAvatar(NULL),
_lookatIndicatorScale(1.0f),
_chatEntryOn(false),
_audio(&_audioScope, STARTUP_JITTER_SAMPLES),
_enableProcessVoxelsThread(true),
@ -220,9 +219,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
// Voxel File.
_voxelsFilename = getCmdOption(argc, constArgv, "-i");
// the callback for our instance of NodeList is attachNewHeadToNode
nodeList->linkedDataCreateCallback = &attachNewHeadToNode;
#ifdef _WIN32
WSADATA WsaData;
int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData);
@ -294,7 +290,7 @@ Application::~Application() {
_settings->sync();
// let the avatar mixer know we're out
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
MyAvatar::sendKillAvatar();
// ask the datagram processing thread to quit and wait until it is done
_nodeThread->quit();
@ -1160,8 +1156,8 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
if (activeWindow() == _window) {
// orbit behavior
if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) {
if (_lookatTargetAvatar) {
_myAvatar.orbit(_lookatTargetAvatar->getPosition(), deltaX, deltaY);
if (_avatarManager.getLookAtTargetAvatar()) {
_myAvatar.orbit(_avatarManager.getLookAtTargetAvatar()->getPosition(), deltaX, deltaY);
return;
}
if (_isHoverVoxel) {
@ -1212,7 +1208,7 @@ void Application::mousePressEvent(QMouseEvent* event) {
return;
}
if (!_palette.isActive() && (!_isHoverVoxel || _lookatTargetAvatar)) {
if (!_palette.isActive() && (!_isHoverVoxel || _avatarManager.getLookAtTargetAvatar())) {
// disable for now
// _pieMenu.mousePressEvent(_mouseX, _mouseY);
}
@ -1846,40 +1842,6 @@ const float MAX_AVATAR_EDIT_VELOCITY = 1.0f;
const float MAX_VOXEL_EDIT_DISTANCE = 50.0f;
const float HEAD_SPHERE_RADIUS = 0.07f;
static QUuid DEFAULT_NODE_ID_REF;
void Application::updateLookatTargetAvatar(glm::vec3& eyePosition) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()");
if (!_mousePressed) {
_lookatTargetAvatar = findLookatTargetAvatar(eyePosition, DEFAULT_NODE_ID_REF);
}
}
Avatar* Application::findLookatTargetAvatar(glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) {
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
Avatar* avatar = (Avatar*)node->getLinkedData();
float distance;
if (avatar->findRayIntersection(_mouseRayOrigin, _mouseRayDirection, distance)) {
// rescale to compensate for head embiggening
eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) *
(avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot();
_lookatIndicatorScale = avatar->getHead().getScale();
_lookatOtherPosition = avatar->getHead().getPosition();
nodeUUID = avatar->getOwningNode()->getUUID();
return avatar;
}
}
}
return NULL;
}
bool Application::isLookingAtMyAvatar(Avatar* avatar) {
glm::vec3 theirLookat = avatar->getHead().getLookAtPosition();
glm::vec3 myHeadPosition = _myAvatar.getHead().getPosition();
@ -1890,17 +1852,6 @@ bool Application::isLookingAtMyAvatar(Avatar* avatar) {
return false;
}
void Application::renderLookatIndicator(glm::vec3 pointOfInterest) {
const float DISTANCE_FROM_HEAD_SPHERE = 0.1f * _lookatIndicatorScale;
const float INDICATOR_RADIUS = 0.1f * _lookatIndicatorScale;
const float YELLOW[] = { 1.0f, 1.0f, 0.0f };
const int NUM_SEGMENTS = 30;
glm::vec3 haloOrigin(pointOfInterest.x, pointOfInterest.y + DISTANCE_FROM_HEAD_SPHERE, pointOfInterest.z);
glColor3f(YELLOW[0], YELLOW[1], YELLOW[2]);
renderCircle(haloOrigin, INDICATOR_RADIUS, IDENTITY_UP, NUM_SEGMENTS);
}
void Application::renderHighlightVoxel(VoxelDetail voxel) {
glDisable(GL_LIGHTING);
glPushMatrix();
@ -1915,41 +1866,6 @@ void Application::renderHighlightVoxel(VoxelDetail voxel) {
glPopMatrix();
}
void Application::updateAvatars(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
QMutexLocker locker(&node->getMutex());
if (node->getLinkedData()) {
Avatar *avatar = (Avatar *)node->getLinkedData();
if (!avatar->isInitialized()) {
avatar->init();
}
avatar->simulate(deltaTime, NULL);
avatar->setMouseRay(_mouseRayOrigin, _mouseRayDirection);
}
}
// simulate avatar fades
for (vector<Avatar*>::iterator fade = _avatarFades.begin(); fade != _avatarFades.end(); fade++) {
Avatar* avatar = *fade;
const float SHRINK_RATE = 0.9f;
avatar->setTargetScale(avatar->getScale() * SHRINK_RATE);
const float MIN_FADE_SCALE = 0.001f;
if (avatar->getTargetScale() < MIN_FADE_SCALE) {
delete avatar;
_avatarFades.erase(fade--);
} else {
avatar->simulate(deltaTime, NULL);
}
}
}
void Application::updateMouseRay() {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
@ -2002,7 +1918,6 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) {
glm::vec3 rayOrigin, rayDirection;
_viewFrustum.computePickRay(0.5f, 0.5f, rayOrigin, rayDirection);
lookAtSpot = rayOrigin + rayDirection * FAR_AWAY_STARE;
} else {
// just look in direction of the mouse ray
lookAtSpot = _mouseRayOrigin + _mouseRayDirection * FAR_AWAY_STARE;
@ -2359,7 +2274,7 @@ void Application::update(float deltaTime) {
glm::vec3 lookAtSpot;
updateFaceshift();
updateLookatTargetAvatar(lookAtSpot);
_avatarManager.updateLookAtTargetAvatar(lookAtSpot);
updateMyAvatarLookAtPosition(lookAtSpot);
// Find the voxel we are hovering over, and respond if clicked
@ -2374,7 +2289,7 @@ void Application::update(float deltaTime) {
updateSerialDevices(deltaTime); // Read serial port interface devices
updateAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
updateAvatars(deltaTime); //loop through all the other avatars and simulate them...
_avatarManager.updateAvatars(deltaTime); //loop through all the other avatars and simulate them...
updateMyAvatarSimulation(deltaTime); // Simulate myself
updateParticles(deltaTime); // Simulate particle cloud movements
updateMetavoxels(deltaTime); // update metavoxels
@ -2743,7 +2658,7 @@ void Application::updateShadowMap() {
glTranslatef(translation.x, translation.y, translation.z);
renderAvatars(true);
_avatarManager.renderAvatars(true);
_particles.render();
glPopMatrix();
@ -2971,7 +2886,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
}
}
renderAvatars(whichCamera.getMode() == CAMERA_MODE_MIRROR, selfAvatarOnly);
_avatarManager.renderAvatars(whichCamera.getMode() == CAMERA_MODE_MIRROR, selfAvatarOnly);
if (!selfAvatarOnly) {
// Render the world box
@ -3125,11 +3040,8 @@ void Application::displayOverlay() {
glPointSize(1.0f);
char nodes[100];
int totalAvatars = 0, totalServers = 0;
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
node->getType() == NODE_TYPE_AGENT ? totalAvatars++ : totalServers++;
}
int totalAvatars = _avatarManager.size();
int totalServers = NodeList::getInstance()->size();
sprintf(nodes, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars);
drawtext(_glWidget->width() - 150, 20, 0.10f, 0, 1.0f, 0, nodes, 1, 0, 0);
@ -3545,46 +3457,6 @@ void Application::renderCoverageMapsRecursively(CoverageMap* map) {
}
}
void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
if (!Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
return;
}
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::renderAvatars()");
if (!selfAvatarOnly) {
// Render avatars of other nodes
NodeList* nodeList = NodeList::getInstance();
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
QMutexLocker locker(&node->getMutex());
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
Avatar *avatar = (Avatar *)node->getLinkedData();
if (!avatar->isInitialized()) {
avatar->init();
}
avatar->render(false);
avatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
}
}
// render avatar fades
Glower glower;
for (vector<Avatar*>::iterator fade = _avatarFades.begin(); fade != _avatarFades.end(); fade++) {
(*fade)->render(false);
}
}
// Render my own Avatar
_myAvatar.render(forceRenderHead);
_myAvatar.setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
if (Menu::getInstance()->isOptionChecked(MenuOption::LookAtIndicator) && _lookatTargetAvatar) {
renderLookatIndicator(_lookatOtherPosition);
}
}
// renderViewFrustum()
//
// Description: this will render the view frustum bounds for EITHER the head
@ -3849,17 +3721,6 @@ void Application::setMenuShortcutsEnabled(bool enabled) {
setShortcutsEnabled(_window->menuBar(), enabled);
}
void Application::attachNewHeadToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
newNode->setLinkedData(new Avatar(newNode));
// new UUID requires mesh and skeleton request to data-server
DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL,
newNode->getUUID(), Application::getInstance()->getProfile());
}
}
void Application::updateWindowTitle(){
QString title = "";
@ -3960,16 +3821,9 @@ void Application::nodeKilled(SharedNodePointer node) {
}
_voxelSceneStatsLock.unlock();
} else if (node->getType() == NODE_TYPE_AGENT) {
Avatar* avatar = static_cast<Avatar*>(node->getLinkedData());
if (avatar == _lookatTargetAvatar) {
_lookatTargetAvatar = NULL;
}
// take over the avatar in order to fade it out
node->setLinkedData(NULL);
_avatarFades.push_back(avatar);
} else if (node->getType() == NODE_TYPE_AVATAR_MIXER) {
// our avatar mixer has gone away - clear the hash of avatars
_avatarManager.clearHash();
}
}

View file

@ -50,6 +50,7 @@
#include "VoxelSystem.h"
#include "VoxelImporter.h"
#include "avatar/Avatar.h"
#include "avatar/AvatarManager.h"
#include "avatar/MyAvatar.h"
#include "avatar/Profile.h"
#include "devices/Faceshift.h"
@ -151,6 +152,7 @@ public:
VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; }
VoxelTree* getClipboard() { return &_clipboard; }
Environment* getEnvironment() { return &_environment; }
bool isMousePressed() const { return _mousePressed; }
bool isMouseHidden() const { return _mouseHidden; }
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
@ -169,8 +171,7 @@ public:
TextureCache* getTextureCache() { return &_textureCache; }
GlowEffect* getGlowEffect() { return &_glowEffect; }
Avatar* getLookatTargetAvatar() const { return _lookatTargetAvatar; }
AvatarManager& getAvatarManager() { return _avatarManager; }
Profile* getProfile() { return &_profile; }
void resetProfile(const QString& username);
@ -233,14 +234,12 @@ public slots:
void initAvatarAndViewFrustum();
private slots:
void timer();
void idle();
void setFullscreen(bool fullscreen);
void setEnable3DTVMode(bool enable3DTVMode);
void renderThrustAtVoxel(const glm::vec3& thrust);
void renderCoverageMap();
@ -279,7 +278,6 @@ private:
void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot);
void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face);
void updateMouseVoxels(float deltaTime, float& distance, BoxFace& face);
void updateLookatTargetAvatar(glm::vec3& eyePosition);
void updateHandAndTouch(float deltaTime);
void updateLeap(float deltaTime);
void updateSixense(float deltaTime);
@ -301,7 +299,6 @@ private:
void renderHighlightVoxel(VoxelDetail voxel);
void updateAvatar(float deltaTime);
void updateAvatars(float deltaTime);
void queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions);
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
@ -310,7 +307,6 @@ private:
void updateShadowMap();
void displayOverlay();
void displayStats();
void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
void renderViewFrustum(ViewFrustum& viewFrustum);
void checkBandwidthMeterClick();
@ -373,6 +369,7 @@ private:
VoxelQuery _voxelQuery; // NodeData derived class for querying voxels from voxel server
AvatarManager _avatarManager;
MyAvatar _myAvatar; // The rendered avatar of oneself
Profile _profile; // The data-server linked profile for this user
@ -442,10 +439,6 @@ private:
bool _lookingAwayFromOrigin;
glm::vec3 _nudgeGuidePosition;
Avatar* _lookatTargetAvatar;
glm::vec3 _lookatOtherPosition;
float _lookatIndicatorScale;
glm::vec3 _transmitterPickStart;
glm::vec3 _transmitterPickEnd;
@ -494,7 +487,6 @@ private:
QReadWriteLock _voxelSceneStatsLock;
std::vector<VoxelFade> _voxelFades;
std::vector<Avatar*> _avatarFades;
ControllerScriptingInterface _controllerScriptingInterface;
QPointer<LogDialog> _logDialog;

View file

@ -6,6 +6,8 @@
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
#include <QtCore/QWeakPointer>
#include <PerfStat.h>
#include "Application.h"
@ -90,11 +92,30 @@ void DatagramProcessor::processDatagrams() {
senderSockAddr);
break;
case PACKET_TYPE_BULK_AVATAR_DATA:
NodeList::getInstance()->processBulkNodeData(senderSockAddr,
incomingPacket,
bytesReceived);
case PACKET_TYPE_KILL_AVATAR: {
// update having heard from the avatar-mixer and record the bytes received
SharedNodePointer avatarMixer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
if (avatarMixer) {
avatarMixer->setLastHeardMicrostamp(usecTimestampNow());
avatarMixer->recordBytesReceived(bytesReceived);
QByteArray datagram(reinterpret_cast<char*>(incomingPacket), bytesReceived);
if (incomingPacket[0] == PACKET_TYPE_BULK_AVATAR_DATA) {
QMetaObject::invokeMethod(&application->getAvatarManager(), "processAvatarMixerDatagram",
Q_ARG(const QByteArray&, datagram),
Q_ARG(const QWeakPointer<Node>&, avatarMixer));
} else {
// this is an avatar kill, pass it to the application AvatarManager
QMetaObject::invokeMethod(&application->getAvatarManager(), "processKillAvatar",
Q_ARG(const QByteArray&, datagram));
}
}
application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived);
break;
}
case PACKET_TYPE_DATA_SERVER_GET:
case PACKET_TYPE_DATA_SERVER_PUT:
case PACKET_TYPE_DATA_SERVER_SEND:

View file

@ -334,7 +334,6 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollisionProxies);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtIndicator, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu,
MenuOption::FaceshiftTCP,
0,
@ -902,7 +901,7 @@ void Menu::goToDomain() {
}
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
Application::getInstance()->getAvatar()->sendKillAvatar();
// give our nodeList the new domain-server hostname
NodeList::getInstance()->setDomainHostname(domainDialog.textValue());
@ -944,7 +943,7 @@ void Menu::goToLocation() {
if (newAvatarPos != avatarPos) {
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
MyAvatar::sendKillAvatar();
qDebug("Going To Location: %f, %f, %f...", x, y, z);
myAvatar->setPosition(newAvatarPos);

View file

@ -221,7 +221,6 @@ namespace MenuOption {
const QString LodTools = "LOD Tools";
const QString Log = "Log";
const QString Login = "Login";
const QString LookAtIndicator = "Look-at Indicator";
const QString LookAtVectors = "Look-at Vectors";
const QString MetavoxelEditor = "Metavoxel Editor...";
const QString Metavoxels = "Metavoxels";

View file

@ -57,7 +57,7 @@ GLubyte identityIndicesFront[] = { 0, 2, 1, 0, 3, 2 };
GLubyte identityIndicesBack[] = { 4, 5, 6, 4, 6, 7 };
VoxelSystem::VoxelSystem(float treeScale, int maxVoxels)
: NodeData(NULL),
: NodeData(),
_treeScale(treeScale),
_maxVoxels(maxVoxels),
_initialized(false) {

View file

@ -58,8 +58,8 @@ const int NUM_BODY_CONE_SIDES = 9;
const float CHAT_MESSAGE_SCALE = 0.0015f;
const float CHAT_MESSAGE_HEIGHT = 0.1f;
Avatar::Avatar(Node* owningNode) :
AvatarData(owningNode),
Avatar::Avatar() :
AvatarData(),
_head(this),
_hand(this),
_skeletonModel(this),
@ -75,6 +75,7 @@ Avatar::Avatar(Node* owningNode) :
_mouseRayDirection(0.0f, 0.0f, 0.0f),
_isCollisionsOn(true),
_moving(false),
_owningAvatarMixer(),
_initialized(false)
{
// we may have been created in the network thread, but we live in the main thread
@ -85,17 +86,11 @@ Avatar::Avatar(Node* owningNode) :
_handData = &_hand;
}
Avatar::~Avatar() {
_headData = NULL;
_handData = NULL;
}
void Avatar::deleteOrDeleteLater() {
this->deleteLater();
}
void Avatar::init() {
_head.init();
_hand.init();

View file

@ -69,10 +69,8 @@ class Avatar : public AvatarData {
Q_OBJECT
public:
Avatar(Node* owningNode = NULL);
Avatar();
~Avatar();
void deleteOrDeleteLater();
void init();
void simulate(float deltaTime, Transmitter* transmitter);
@ -92,6 +90,9 @@ public:
Head& getHead() { return _head; }
Hand& getHand() { return _hand; }
glm::quat getWorldAlignedOrientation() 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;
@ -110,8 +111,10 @@ public:
/// \param collision[out] the details of the collision point
/// \return whether or not the sphere collided
virtual bool findSphereCollision(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision);
virtual bool isMyAvatar() { return false; }
virtual int parseData(unsigned char* sourceBuffer, int numBytes);
int parseData(unsigned char* sourceBuffer, int numBytes);
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);
@ -141,8 +144,8 @@ protected:
glm::vec3 _mouseRayDirection;
bool _isCollisionsOn;
float _stringLength;
bool _moving; ///< set when position is changing
QWeakPointer<Node> _owningAvatarMixer;
// protected methods...
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }

View file

@ -0,0 +1,233 @@
//
// AvatarManager.cpp
// hifi
//
// Created by Stephen Birarda on 1/23/2014.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
#include <PerfStat.h>
#include <UUID.h>
#include "Application.h"
#include "Avatar.h"
#include "Menu.h"
#include "MyAvatar.h"
#include "AvatarManager.h"
AvatarManager::AvatarManager(QObject* parent) :
_lookAtTargetAvatar(),
_lookAtOtherPosition(),
_lookAtIndicatorScale(1.0f),
_avatarHash(),
_avatarFades()
{
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
}
void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()");
Application* applicationInstance = Application::getInstance();
if (!applicationInstance->isMousePressed()) {
foreach (const AvatarSharedPointer& avatar, _avatarHash) {
float distance;
if (avatar->findRayIntersection(applicationInstance->getMouseRayOrigin(),
applicationInstance->getMouseRayDirection(), distance)) {
// rescale to compensate for head embiggening
eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) *
(avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot();
_lookAtIndicatorScale = avatar->getHead().getScale();
_lookAtOtherPosition = avatar->getHead().getPosition();
_lookAtTargetAvatar = avatar;
// found the look at target avatar, return
return;
}
}
_lookAtTargetAvatar.clear();
}
}
void AvatarManager::updateAvatars(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
// simulate avatars
AvatarHash::iterator avatar = _avatarHash.begin();
if (avatar != _avatarHash.end()) {
if (avatar->data()->getOwningAvatarMixer()) {
// this avatar's mixer is still around, go ahead and simulate it
avatar->data()->simulate(deltaTime, NULL);
Application* applicationInstance = Application::getInstance();
avatar->data()->setMouseRay(applicationInstance->getMouseRayOrigin(),
applicationInstance->getMouseRayDirection());
} else {
// the mixer that owned this avatar is gone, give it to the vector of fades and kill it
avatar = removeAvatarAtHashIterator(avatar);
}
}
// simulate avatar fades
simulateAvatarFades(deltaTime);
}
void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
if (!Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
return;
}
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::renderAvatars()");
if (!selfAvatarOnly) {
// Render avatars of other nodes
foreach (const AvatarSharedPointer& avatar, _avatarHash) {
if (!avatar->isInitialized()) {
avatar->init();
}
avatar->render(false);
avatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
}
renderAvatarFades();
}
// Render my own Avatar
Avatar* myAvatar = Application::getInstance()->getAvatar();
myAvatar->render(forceRenderHead);
myAvatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
}
void AvatarManager::simulateAvatarFades(float deltaTime) {
QVector<AvatarSharedPointer>::iterator fadingAvatar = _avatarFades.begin();
while (fadingAvatar != _avatarFades.end()) {
const float SHRINK_RATE = 0.9f;
fadingAvatar->data()->setTargetScale(fadingAvatar->data()->getScale() * SHRINK_RATE);
const float MIN_FADE_SCALE = 0.001f;
if (fadingAvatar->data()->getTargetScale() < MIN_FADE_SCALE) {
fadingAvatar = _avatarFades.erase(fadingAvatar);
} else {
fadingAvatar->data()->simulate(deltaTime, NULL);
}
}
}
void AvatarManager::renderAvatarFades() {
// render avatar fades
Glower glower;
foreach(const AvatarSharedPointer& fadingAvatar, _avatarFades) {
fadingAvatar->render(false);
}
}
void AvatarManager::processDataServerResponse(const QString& userString, const QStringList& keyList,
const QStringList &valueList) {
for (int i = 0; i < keyList.size(); i++) {
if (valueList[i] != " ") {
if (keyList[i] == DataServerKey::FaceMeshURL || keyList[i] == DataServerKey::SkeletonURL) {
// mesh URL for a UUID, find avatar in our list
AvatarSharedPointer matchingAvatar = _avatarHash.value(QUuid(userString));
if (matchingAvatar) {
if (keyList[i] == DataServerKey::FaceMeshURL) {
qDebug() << "Changing mesh to" << valueList[i] << "for avatar with UUID"
<< uuidStringWithoutCurlyBraces(QUuid(userString));
QMetaObject::invokeMethod(&matchingAvatar->getHead().getFaceModel(),
"setURL", Q_ARG(QUrl, QUrl(valueList[i])));
} else if (keyList[i] == DataServerKey::SkeletonURL) {
qDebug() << "Changing skeleton to" << valueList[i] << "for avatar with UUID"
<< uuidStringWithoutCurlyBraces(QString(userString));
QMetaObject::invokeMethod(&matchingAvatar->getSkeletonModel(),
"setURL", Q_ARG(QUrl, QUrl(valueList[i])));
}
}
}
}
}
}
void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer) {
unsigned char packetData[MAX_PACKET_SIZE];
memcpy(packetData, datagram.data(), datagram.size());
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
int bytesRead = numBytesPacketHeader;
unsigned char avatarData[MAX_PACKET_SIZE];
int numBytesDummyPacketHeader = populateTypeAndVersion(avatarData, PACKET_TYPE_HEAD_DATA);
// 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 nodeUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID));
AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID);
if (!matchingAvatar) {
// construct a new Avatar for this node
matchingAvatar = AvatarSharedPointer(new Avatar());
matchingAvatar->setOwningAvatarMixer(mixerWeakPointer);
// insert the new avatar into our hash
_avatarHash.insert(nodeUUID, matchingAvatar);
// new UUID requires mesh and skeleton request to data-server
DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL,
nodeUUID, this);
qDebug() << "Adding avatar with UUID" << nodeUUID << "to AvatarManager hash.";
}
// copy the rest of the packet to the avatarData holder so we can read the next Avatar from there
memcpy(avatarData + numBytesDummyPacketHeader, packetData + bytesRead, datagram.size() - bytesRead);
// have the matching (or new) avatar parse the data from the packet
bytesRead += matchingAvatar->parseData(avatarData, datagram.size() - bytesRead);
}
}
void AvatarManager::processKillAvatar(const QByteArray& datagram) {
// read the node id
QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(reinterpret_cast<const unsigned char*>
(datagram.data())),
NUM_BYTES_RFC4122_UUID));
// remove the avatar with that UUID from our hash, if it exists
AvatarHash::iterator matchedAvatar = _avatarHash.find(nodeUUID);
if (matchedAvatar != _avatarHash.end()) {
removeAvatarAtHashIterator(matchedAvatar);
}
}
AvatarHash::iterator AvatarManager::removeAvatarAtHashIterator(const AvatarHash::iterator& iterator) {
qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash.";
_avatarFades.push_back(iterator.value());
return _avatarHash.erase(iterator);
}
void AvatarManager::clearHash() {
// clear the AvatarManager hash - typically happens on the removal of the avatar-mixer
AvatarHash::iterator removeAvatar = _avatarHash.begin();
while (removeAvatar != _avatarHash.end()) {
removeAvatar = removeAvatarAtHashIterator(removeAvatar);
}
}

View file

@ -0,0 +1,60 @@
//
// AvatarManager.h
// hifi
//
// Created by Stephen Birarda on 1/23/2014.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
#ifndef __hifi__AvatarManager__
#define __hifi__AvatarManager__
#include <QtCore/QHash>
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#include <DataServerClient.h>
#include "Avatar.h"
typedef QSharedPointer<Avatar> AvatarSharedPointer;
typedef QHash<QUuid, AvatarSharedPointer> AvatarHash;
class AvatarManager : public QObject, public DataServerCallbackObject {
Q_OBJECT
public:
AvatarManager(QObject* parent = 0);
const AvatarHash& getAvatarHash() { return _avatarHash; }
int size() const { return _avatarHash.size(); }
Avatar* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
void updateLookAtTargetAvatar(glm::vec3& eyePosition);
void updateAvatars(float deltaTime);
void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
void clearHash();
public slots:
void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList);
void processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer);
void processKillAvatar(const QByteArray& datagram);
private:
void simulateAvatarFades(float deltaTime);
void renderAvatarFades();
AvatarHash::iterator removeAvatarAtHashIterator(const AvatarHash::iterator& iterator);
QWeakPointer<Avatar> _lookAtTargetAvatar;
glm::vec3 _lookAtOtherPosition;
float _lookAtIndicatorScale;
AvatarHash _avatarHash;
QVector<AvatarSharedPointer> _avatarFades;
};
#endif /* defined(__hifi__AvatarManager__) */

View file

@ -43,7 +43,7 @@ Hand::Hand(Avatar* owningAvatar) :
void Hand::init() {
// Different colors for my hand and others' hands
if (_owningAvatar && _owningAvatar->getOwningNode() == NULL) {
if (_owningAvatar && _owningAvatar->isMyAvatar()) {
_ballColor = glm::vec3(0.0, 0.4, 0.0);
}
else {
@ -54,8 +54,6 @@ void Hand::init() {
void Hand::reset() {
}
glm::vec3 Hand::getAndResetGrabDelta() {
const float HAND_GRAB_SCALE_DISTANCE = 2.f;
glm::vec3 delta = _grabDelta * _owningAvatar->getScale() * HAND_GRAB_SCALE_DISTANCE;
@ -185,49 +183,46 @@ void Hand::updateCollisions() {
glm::vec3 totalPenetration;
// check other avatars
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
Avatar* otherAvatar = (Avatar*)node->getLinkedData();
if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
// Check for palm collisions
glm::vec3 myPalmPosition = palm.getPosition();
float palmCollisionDistance = 0.1f;
bool wasColliding = palm.getIsCollidingWithPalm();
palm.setIsCollidingWithPalm(false);
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
for (size_t j = 0; j < otherAvatar->getHand().getNumPalms(); j++) {
PalmData& otherPalm = otherAvatar->getHand().getPalms()[j];
if (!otherPalm.isActive()) {
continue;
}
glm::vec3 otherPalmPosition = otherPalm.getPosition();
if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) {
palm.setIsCollidingWithPalm(true);
if (!wasColliding) {
const float PALM_COLLIDE_VOLUME = 1.f;
const float PALM_COLLIDE_FREQUENCY = 1000.f;
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
PALM_COLLIDE_FREQUENCY,
PALM_COLLIDE_DURATION_MAX,
PALM_COLLIDE_DECAY_PER_SAMPLE);
// If the other person's palm is in motion, move mine downward to show I was hit
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {
// add slapback here
}
foreach (const AvatarSharedPointer& avatar, Application::getInstance()->getAvatarManager().getAvatarHash()) {
if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
// Check for palm collisions
glm::vec3 myPalmPosition = palm.getPosition();
float palmCollisionDistance = 0.1f;
bool wasColliding = palm.getIsCollidingWithPalm();
palm.setIsCollidingWithPalm(false);
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) {
PalmData& otherPalm = avatar->getHand().getPalms()[j];
if (!otherPalm.isActive()) {
continue;
}
glm::vec3 otherPalmPosition = otherPalm.getPosition();
if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) {
palm.setIsCollidingWithPalm(true);
if (!wasColliding) {
const float PALM_COLLIDE_VOLUME = 1.f;
const float PALM_COLLIDE_FREQUENCY = 1000.f;
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
PALM_COLLIDE_FREQUENCY,
PALM_COLLIDE_DURATION_MAX,
PALM_COLLIDE_DECAY_PER_SAMPLE);
// If the other person's palm is in motion, move mine downward to show I was hit
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {
// add slapback here
}
}
}
}
glm::vec3 avatarPenetration;
if (otherAvatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, avatarPenetration)) {
totalPenetration = addPenetrations(totalPenetration, avatarPenetration);
// Check for collisions with the other avatar's leap palms
}
}
glm::vec3 avatarPenetration;
if (avatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, avatarPenetration)) {
totalPenetration = addPenetrations(totalPenetration, avatarPenetration);
// Check for collisions with the other avatar's leap palms
}
}

View file

@ -37,8 +37,8 @@ const bool USING_HEAD_LEAN = false;
const float SKIN_COLOR[] = {1.0f, 0.84f, 0.66f};
const float DARK_SKIN_COLOR[] = {0.9f, 0.78f, 0.63f};
MyAvatar::MyAvatar(Node* owningNode) :
Avatar(owningNode),
MyAvatar::MyAvatar() :
Avatar(),
_mousePressed(false),
_bodyPitchDelta(0.0f),
_bodyRollDelta(0.0f),
@ -472,6 +472,21 @@ void MyAvatar::loadData(QSettings* settings) {
settings->endGroup();
}
void MyAvatar::sendKillAvatar() {
unsigned char packet[MAX_PACKET_SIZE];
unsigned char* packetPosition = packet;
packetPosition += populateTypeAndVersion(packetPosition, PACKET_TYPE_KILL_AVATAR);
NodeList* nodeList = NodeList::getInstance();
QByteArray rfcUUID = nodeList->getOwnerUUID().toRfc4122();
memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size());
packetPosition += rfcUUID.size();
nodeList->broadcastToNodes(packet, packetPosition - packet, QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
}
void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
// first orbit horizontally
glm::quat orientation = getOrientation();
@ -776,16 +791,16 @@ void MyAvatar::updateChatCircle(float deltaTime) {
// find all circle-enabled members and sort by distance
QVector<SortedAvatar> sortedAvatars;
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
SortedAvatar sortedAvatar;
sortedAvatar.avatar = (Avatar*)node->getLinkedData();
if (!sortedAvatar.avatar->isChatCirclingEnabled()) {
continue;
}
sortedAvatar.distance = glm::distance(_position, sortedAvatar.avatar->getPosition());
sortedAvatars.append(sortedAvatar);
foreach (const AvatarSharedPointer& avatar, Application::getInstance()->getAvatarManager().getAvatarHash()) {
SortedAvatar sortedAvatar;
sortedAvatar.avatar = avatar.data();
if (!sortedAvatar.avatar->isChatCirclingEnabled()) {
continue;
}
sortedAvatar.distance = glm::distance(_position, sortedAvatar.avatar->getPosition());
sortedAvatars.append(sortedAvatar);
}
qSort(sortedAvatars.begin(), sortedAvatars.end());

View file

@ -24,8 +24,8 @@ enum AvatarHandState
class MyAvatar : public Avatar {
public:
MyAvatar(Node* owningNode = NULL);
MyAvatar();
void reset();
void simulate(float deltaTime, Transmitter* transmitter);
void updateFromGyros(bool turnWithHead);
@ -62,6 +62,10 @@ public:
void setDriveKeys(int key, float val) { _driveKeys[key] = val; };
bool getDriveKeys(int key) { return _driveKeys[key]; };
void jump() { _shouldJump = true; };
bool isMyAvatar() { return true; }
static void sendKillAvatar();
// Set/Get update the thrust that will move the avatar around
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };

View file

@ -158,35 +158,11 @@ void Profile::processDataServerResponse(const QString& userString, const QString
if (userString == _username || userString == uuidStringWithoutCurlyBraces(_uuid)) {
qDebug("Changing user's face model URL to %s", valueList[i].toLocal8Bit().constData());
Application::getInstance()->getProfile()->setFaceModelURL(QUrl(valueList[i]));
} else {
// mesh URL for a UUID, find avatar in our list
SharedNodePointer matchingNode = NodeList::getInstance()->nodeWithUUID(QUuid(userString));
if (matchingNode && matchingNode->getType() == NODE_TYPE_AGENT) {
qDebug() << "Changing mesh to" << valueList[i] << "for avatar with UUID"
<< uuidStringWithoutCurlyBraces(matchingNode->getUUID());
Avatar* avatar = (Avatar *) matchingNode->getLinkedData();
QMetaObject::invokeMethod(&avatar->getHead().getFaceModel(),
"setURL", Q_ARG(QUrl, QUrl(valueList[i])));
}
}
} else if (keyList[i] == DataServerKey::SkeletonURL) {
if (userString == _username || userString == uuidStringWithoutCurlyBraces(_uuid)) {
qDebug("Changing user's skeleton URL to %s", valueList[i].toLocal8Bit().constData());
Application::getInstance()->getProfile()->setSkeletonModelURL(QUrl(valueList[i]));
} else {
// skeleton URL for a UUID, find avatar in our list
SharedNodePointer matchingNode = NodeList::getInstance()->nodeWithUUID(QUuid(userString));
if (matchingNode && matchingNode->getType() == NODE_TYPE_AGENT) {
qDebug() << "Changing skeleton to" << valueList[i] << "for avatar with UUID"
<< uuidStringWithoutCurlyBraces(matchingNode->getUUID());
Avatar* avatar = (Avatar *) matchingNode->getLinkedData();
QMetaObject::invokeMethod(&avatar->getSkeletonModel(),
"setURL", Q_ARG(QUrl, QUrl(valueList[i])));
}
}
} else if (keyList[i] == DataServerKey::Domain && keyList[i + 1] == DataServerKey::Position &&
keyList[i + 2] == DataServerKey::Orientation && valueList[i] != " " &&
@ -198,7 +174,7 @@ void Profile::processDataServerResponse(const QString& userString, const QString
if (coordinateItems.size() == 3 && orientationItems.size() == 3) {
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
MyAvatar::sendKillAvatar();
qDebug() << "Changing domain to" << valueList[i].toLocal8Bit().constData() <<
", position to" << valueList[i + 1].toLocal8Bit().constData() <<
@ -211,8 +187,7 @@ void Profile::processDataServerResponse(const QString& userString, const QString
orientationItems[1].toFloat(),
orientationItems[2].toFloat()))) *
glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f);
Application::getInstance()->getAvatar()
->setOrientation(newOrientation);
Application::getInstance()->getAvatar()->setOrientation(newOrientation);
// move the user a couple units away
const float DISTANCE_TO_USER = 2.0f;

View file

@ -16,7 +16,7 @@
#include "AudioRingBuffer.h"
AudioRingBuffer::AudioRingBuffer(int numFrameSamples) :
NodeData(NULL),
NodeData(),
_sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES),
_isStarved(true),
_hasStarted(false)

View file

@ -22,8 +22,8 @@ using namespace std;
static const float fingerVectorRadix = 4; // bits of precision when converting from float<->fixed
AvatarData::AvatarData(Node* owningNode) :
NodeData(owningNode),
AvatarData::AvatarData() :
NodeData(),
_handPosition(0,0,0),
_bodyYaw(-90.0),
_bodyPitch(0.0),

View file

@ -70,7 +70,7 @@ class AvatarData : public NodeData {
Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll)
Q_PROPERTY(QString chatMessage READ getQStringChatMessage WRITE setChatMessage)
public:
AvatarData(Node* owningNode = NULL);
AvatarData();
~AvatarData();
const glm::vec3& getPosition() const { return _position; }

View file

@ -13,8 +13,7 @@
#include <cstdio>
#include "OctreeSendThread.h"
OctreeQueryNode::OctreeQueryNode(Node* owningNode) :
OctreeQuery(owningNode),
OctreeQueryNode::OctreeQueryNode() :
_viewSent(false),
_octreePacketAvailableBytes(MAX_PACKET_SIZE),
_maxSearchLevel(1),
@ -38,9 +37,8 @@ OctreeQueryNode::OctreeQueryNode(Node* owningNode) :
_sequenceNumber = 0;
}
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer) {
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID) {
// Create octree sending thread...
QUuid nodeUUID = getOwningNode()->getUUID();
_octreeSendThread = new OctreeSendThread(nodeUUID, octreeServer);
_octreeSendThread->initialize(true);
}

View file

@ -24,7 +24,7 @@ class OctreeServer;
class OctreeQueryNode : public OctreeQuery {
public:
OctreeQueryNode(Node* owningNode);
OctreeQueryNode();
virtual ~OctreeQueryNode();
virtual PACKET_TYPE getMyPacketType() const = 0;
@ -80,7 +80,7 @@ public:
OctreeSceneStats stats;
void initializeOctreeSendThread(OctreeServer* octreeServer);
void initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID);
bool isOctreeSendThreadInitalized() { return _octreeSendThread; }
void dumpOutOfView();

View file

@ -22,23 +22,12 @@ OctreeServer* OctreeServer::_instance = NULL;
void OctreeServer::attachQueryNodeToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode(newNode);
OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode();
newQueryNodeData->resetOctreePacket(true); // don't bump sequence
newNode->setLinkedData(newQueryNodeData);
}
}
void OctreeServer::nodeKilled(SharedNodePointer node) {
// Use this to cleanup our node
if (node->getType() == NODE_TYPE_AGENT) {
OctreeQueryNode* nodeData = (OctreeQueryNode*)node->getLinkedData();
if (nodeData) {
node->setLinkedData(NULL);
delete nodeData;
}
}
};
OctreeServer::OctreeServer(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes),
_argc(0),
@ -499,7 +488,7 @@ void OctreeServer::processDatagram(const QByteArray& dataByteArray, const HifiSo
}
OctreeQueryNode* nodeData = (OctreeQueryNode*) node->getLinkedData();
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
nodeData->initializeOctreeSendThread(this);
nodeData->initializeOctreeSendThread(this, nodeUUID);
}
}
} else if (packetType == PACKET_TYPE_JURISDICTION_REQUEST) {
@ -573,8 +562,6 @@ void OctreeServer::run() {
setvbuf(stdout, NULL, _IOLBF, 0);
// tell our NodeList about our desire to get notifications
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), this, SLOT(nodeKilled(SharedNodePointer)));
nodeList->linkedDataCreateCallback = &OctreeServer::attachQueryNodeToNode;
srand((unsigned)time(0));

View file

@ -48,7 +48,7 @@ public:
uint64_t getLoadElapsedTime() const { return (_persistThread) ? _persistThread->getLoadElapsedTime() : 0; }
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode) = 0;
virtual OctreeQueryNode* createOctreeQueryNode() = 0;
virtual Octree* createTree() = 0;
virtual unsigned char getMyNodeType() const = 0;
virtual PACKET_TYPE getMyQueryMessageType() const = 0;
@ -69,8 +69,6 @@ public slots:
void run();
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
void nodeKilled(SharedNodePointer node);
protected:
void parsePayload();
void initHTTPManager(int port);

View file

@ -23,6 +23,7 @@
/// within the domain. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets
/// and adding them to the processing queue by calling queueReceivedPacket()
class JurisdictionListener : public ReceivedPacketProcessor {
Q_OBJECT
public:
static const int DEFAULT_PACKETS_PER_SECOND = 1;
static const int NO_SERVER_CHECK_RATE = 60; // if no servers yet detected, keep checking at 60fps

View file

@ -22,8 +22,8 @@ using namespace std;
static const float fingerVectorRadix = 4; // bits of precision when converting from float<->fixed
OctreeQuery::OctreeQuery(Node* owningNode) :
NodeData(owningNode),
OctreeQuery::OctreeQuery() :
NodeData(),
_uuid(),
_cameraPosition(0,0,0),
_cameraOrientation(),

View file

@ -51,7 +51,7 @@ class OctreeQuery : public NodeData {
Q_OBJECT
public:
OctreeQuery(Node* owningNode = NULL);
OctreeQuery();
virtual ~OctreeQuery();
int getBroadcastData(unsigned char* destinationBuffer);

View file

@ -15,8 +15,8 @@
class ParticleNodeData : public OctreeQueryNode {
public:
ParticleNodeData(Node* owningNode) :
OctreeQueryNode(owningNode),
ParticleNodeData() :
OctreeQueryNode(),
_lastDeletedParticlesSentAt(0) { };
virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_PARTICLE_DATA; }

View file

@ -26,8 +26,8 @@ ParticleServer::~ParticleServer() {
tree->removeNewlyCreatedHook(this);
}
OctreeQueryNode* ParticleServer::createOctreeQueryNode(Node* newNode) {
return new ParticleNodeData(newNode);
OctreeQueryNode* ParticleServer::createOctreeQueryNode() {
return new ParticleNodeData();
}
Octree* ParticleServer::createTree() {

View file

@ -24,7 +24,7 @@ public:
~ParticleServer();
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode);
virtual OctreeQueryNode* createOctreeQueryNode();
virtual Octree* createTree();
virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_PARTICLE_QUERY; }

View file

@ -191,37 +191,37 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
}
// loop through all the other avatars for potential interactions...
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
//qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n";
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
AvatarData* avatar = static_cast<AvatarData*>(node->getLinkedData());
CollisionInfo collisionInfo;
if (avatar->findSphereCollision(center, radius, collisionInfo)) {
collisionInfo._addedVelocity /= (float)(TREE_SCALE);
glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
// TODO: make this less hacky when we have more per-collision details
float elasticity = ELASTICITY;
float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED;
float damping = DAMPING;
if (attenuationFactor < 1.f) {
collisionInfo._addedVelocity *= attenuationFactor;
elasticity *= attenuationFactor;
// NOTE: the math below keeps the damping piecewise continuous,
// while ramping it up to 1.0 when attenuationFactor = 0
damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
}
// HACK END
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
collisionInfo._penetration /= (float)(TREE_SCALE);
applyHardCollision(particle, ELASTICITY, damping, collisionInfo);
}
}
}
}
// foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
// //qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n";
// if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
// AvatarData* avatar = static_cast<AvatarData*>(node->getLinkedData());
// CollisionInfo collisionInfo;
// if (avatar->findSphereCollision(center, radius, collisionInfo)) {
// collisionInfo._addedVelocity /= (float)(TREE_SCALE);
// glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
// if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
// // HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
// // NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
// // TODO: make this less hacky when we have more per-collision details
// float elasticity = ELASTICITY;
// float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED;
// float damping = DAMPING;
// if (attenuationFactor < 1.f) {
// collisionInfo._addedVelocity *= attenuationFactor;
// elasticity *= attenuationFactor;
// // NOTE: the math below keeps the damping piecewise continuous,
// // while ramping it up to 1.0 when attenuationFactor = 0
// damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
// }
// // HACK END
//
// updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
// collisionInfo._penetration /= (float)(TREE_SCALE);
// applyHardCollision(particle, ELASTICITY, damping, collisionInfo);
// }
// }
// }
// }
}
// TODO: convert applyHardCollision() to take a CollisionInfo& instead of penetration + addedVelocity

View file

@ -39,7 +39,7 @@ Node::Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const
Node::~Node() {
if (_linkedData) {
_linkedData->deleteOrDeleteLater();
delete _linkedData;
}
delete _bytesReceivedMovingAverage;

View file

@ -8,16 +8,6 @@
#include "NodeData.h"
NodeData::NodeData(Node* owningNode) :
_owningNode(owningNode)
{
}
NodeData::~NodeData() {
}
void NodeData::deleteOrDeleteLater() {
delete this;
}

View file

@ -16,16 +16,9 @@ class Node;
class NodeData : public QObject {
Q_OBJECT
public:
NodeData(Node* owningNode = NULL);
virtual ~NodeData() = 0;
virtual int parseData(unsigned char* sourceBuffer, int numBytes) = 0;
virtual void deleteOrDeleteLater();
Node* getOwningNode() { return _owningNode; }
protected:
Node* _owningNode;
};
#endif

View file

@ -178,53 +178,6 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, unsigned char
processSTUNResponse(packetData, dataBytes);
break;
}
case PACKET_TYPE_KILL_NODE: {
processKillNode(packetData, dataBytes);
break;
}
}
}
void NodeList::processBulkNodeData(const HifiSockAddr& senderAddress, unsigned char *packetData, int numTotalBytes) {
SharedNodePointer bulkSendNode = nodeWithAddress(senderAddress);
// find the avatar mixer in our node list and update the lastRecvTime from it
if (bulkSendNode) {
bulkSendNode->setLastHeardMicrostamp(usecTimestampNow());
bulkSendNode->recordBytesReceived(numTotalBytes);
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
unsigned char* startPosition = packetData;
unsigned char* currentPosition = startPosition + numBytesPacketHeader;
unsigned char* packetHolder = new unsigned char[numTotalBytes];
// we've already verified packet version for the bulk packet, so all head data in the packet is also up to date
populateTypeAndVersion(packetHolder, PACKET_TYPE_HEAD_DATA);
while ((currentPosition - startPosition) < numTotalBytes) {
memcpy(packetHolder + numBytesPacketHeader,
currentPosition,
numTotalBytes - (currentPosition - startPosition));
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)currentPosition, NUM_BYTES_RFC4122_UUID));
SharedNodePointer matchingNode = nodeWithUUID(nodeUUID);
if (!matchingNode) {
// we're missing this node, we need to add it to the list
matchingNode = addOrUpdateNode(nodeUUID, NODE_TYPE_AGENT, HifiSockAddr(), HifiSockAddr());
}
currentPosition += updateNodeWithData(matchingNode.data(),
HifiSockAddr(),
packetHolder,
numTotalBytes - (currentPosition - startPosition));
}
delete[] packetHolder;
}
}
@ -460,30 +413,13 @@ NodeHash::iterator NodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItem
return _nodeHash.erase(nodeItemToKill);
}
void NodeList::sendKillNode(const QSet<NODE_TYPE>& destinationNodeTypes) {
unsigned char packet[MAX_PACKET_SIZE];
unsigned char* packetPosition = packet;
packetPosition += populateTypeAndVersion(packetPosition, PACKET_TYPE_KILL_NODE);
QByteArray rfcUUID = _ownerUUID.toRfc4122();
memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size());
packetPosition += rfcUUID.size();
broadcastToNodes(packet, packetPosition - packet, destinationNodeTypes);
}
void NodeList::processKillNode(unsigned char* packetData, size_t dataBytes) {
// skip the header
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
packetData += numBytesPacketHeader;
dataBytes -= numBytesPacketHeader;
void NodeList::processKillNode(const QByteArray& dataByteArray) {
// read the node id
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)packetData, NUM_BYTES_RFC4122_UUID));
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader(reinterpret_cast
<const unsigned char*>(dataByteArray.data())),
NUM_BYTES_RFC4122_UUID));
packetData += NUM_BYTES_RFC4122_UUID;
dataBytes -= NUM_BYTES_RFC4122_UUID;
// kill the node with this UUID, if it exists
killNodeWithUUID(nodeUUID);
}

View file

@ -104,8 +104,6 @@ public:
int fillPingPacket(unsigned char* buffer);
int fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer);
void pingPublicAndLocalSocketsForInactiveNode(Node* node);
void sendKillNode(const QSet<NODE_TYPE>& destinationNodeTypes);
SharedNodePointer nodeWithAddress(const HifiSockAddr& senderSockAddr);
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID);
@ -113,7 +111,8 @@ public:
SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
void processNodeData(const HifiSockAddr& senderSockAddr, unsigned char *packetData, size_t dataBytes);
void processBulkNodeData(const HifiSockAddr& senderSockAddr, unsigned char *packetData, int numTotalBytes);
void processKillNode(const QByteArray& datagram);
int updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, unsigned char *packetData, int dataBytes);
@ -144,7 +143,6 @@ private:
void sendSTUNRequest();
void processSTUNResponse(unsigned char* packetData, size_t dataBytes);
void processKillNode(unsigned char* packetData, size_t dataBytes);
NodeHash::iterator killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill);
NodeHash _nodeHash;

View file

@ -20,7 +20,7 @@ const PACKET_TYPE PACKET_TYPE_STUN_RESPONSE = 1;
const PACKET_TYPE PACKET_TYPE_DOMAIN = 'D';
const PACKET_TYPE PACKET_TYPE_PING = 'P';
const PACKET_TYPE PACKET_TYPE_PING_REPLY = 'R';
const PACKET_TYPE PACKET_TYPE_KILL_NODE = 'K';
const PACKET_TYPE PACKET_TYPE_KILL_AVATAR = 'K';
const PACKET_TYPE PACKET_TYPE_HEAD_DATA = 'H';
const PACKET_TYPE PACKET_TYPE_INJECT_AUDIO = 'I';
const PACKET_TYPE PACKET_TYPE_MIXED_AUDIO = 'A';

View file

@ -14,7 +14,7 @@
class VoxelNodeData : public OctreeQueryNode {
public:
VoxelNodeData(Node* owningNode) : OctreeQueryNode(owningNode) { };
VoxelNodeData() : OctreeQueryNode() { };
virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_VOXEL_DATA; }
};

View file

@ -24,8 +24,8 @@ VoxelServer::~VoxelServer() {
// nothing special to do here...
}
OctreeQueryNode* VoxelServer::createOctreeQueryNode(Node* newNode) {
return new VoxelNodeData(newNode);
OctreeQueryNode* VoxelServer::createOctreeQueryNode() {
return new VoxelNodeData();
}
Octree* VoxelServer::createTree() {

View file

@ -34,7 +34,7 @@ public:
int getEnvironmentDataCount() const { return sizeof(_environmentData)/sizeof(EnvironmentData); }
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode);
virtual OctreeQueryNode* createOctreeQueryNode();
virtual Octree* createTree();
virtual unsigned char getMyNodeType() const { return NODE_TYPE_VOXEL_SERVER; }
virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_VOXEL_QUERY; }

View file

@ -6,9 +6,4 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include "VoxelQuery.h"
VoxelQuery::VoxelQuery(Node* owningNode) : OctreeQuery(owningNode) {
}
#include "VoxelQuery.h"

View file

@ -14,10 +14,8 @@
class VoxelQuery : public OctreeQuery {
public:
VoxelQuery(Node* owningNode = NULL);
// currently just an alias
VoxelQuery() : OctreeQuery() {};
private:
// privatize the copy constructor and assignment operator so they cannot be called
VoxelQuery(const VoxelQuery&);