diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 33cee4299c..7e6d0deaf2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -139,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), @@ -223,9 +221,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); @@ -1146,8 +1141,8 @@ void Application::mouseMoveEvent(QMouseEvent* event) { // 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) { @@ -1198,7 +1193,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); } @@ -1422,7 +1417,7 @@ void Application::terminate() { _settings->sync(); // let the avatar mixer know we're out - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); + _myAvatar.sendKillAvatar(); _voxelProcessor.terminate(); _voxelHideShowThread.terminate(); @@ -1858,40 +1853,6 @@ const float HEAD_SPHERE_RADIUS = 0.07f; static QUuid DEFAULT_NODE_ID_REF; -void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition) { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()"); - - if (!_mousePressed) { - _lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF); - } -} - -Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - 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(); @@ -1902,17 +1863,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(); @@ -1927,40 +1877,6 @@ void Application::renderHighlightVoxel(VoxelDetail voxel) { glPopMatrix(); } -void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection) { - 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::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(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection) { @@ -2016,7 +1932,7 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& _viewFrustum.computePickRay(0.5f, 0.5f, rayOrigin, rayDirection); lookAtSpot = rayOrigin + rayDirection * FAR_AWAY_STARE; - } else if (!_lookatTargetAvatar) { + } else if (!_avatarManager.getLookAtTargetAvatar()) { if (_isHoverVoxel) { // Look at the hovered voxel lookAtSpot = getMouseVoxelWorldCoordinates(_hoverVoxel); @@ -2381,7 +2297,7 @@ void Application::update(float deltaTime) { glm::vec3 lookAtSpot; updateFaceshift(); - updateLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, lookAtSpot); + _avatarManager.updateLookAtTargetAvatar(mouseRayOrigin, mouseRayDirection, lookAtSpot); updateMyAvatarLookAtPosition(lookAtSpot, mouseRayOrigin, mouseRayDirection); // Find the voxel we are hovering over, and respond if clicked @@ -2396,7 +2312,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, mouseRayOrigin, mouseRayDirection); //loop through all the other avatars and simulate them... + _avatarManager.updateAvatars(deltaTime, mouseRayOrigin, mouseRayDirection); //loop through all the other avatars and simulate them... updateMyAvatarSimulation(deltaTime); // Simulate myself updateParticles(deltaTime); // Simulate particle cloud movements updateMetavoxels(deltaTime); // update metavoxels @@ -3145,11 +3061,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); @@ -3566,43 +3479,7 @@ 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::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() @@ -3869,17 +3746,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 = ""; @@ -3980,16 +3846,9 @@ void Application::nodeKilled(SharedNodePointer node) { } _voxelSceneStatsLock.unlock(); - } else if (node->getType() == NODE_TYPE_AGENT) { - Avatar* avatar = static_cast(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(); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 373020c571..fc288722f5 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -151,6 +151,7 @@ public: VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; } VoxelTree* getClipboard() { return &_clipboard; } Environment* getEnvironment() { return &_environment; } + bool isMousePressed() const { return _mousePressed; } bool isMouseHidden() const { return _mouseHidden; } Faceshift* getFaceshift() { return &_faceshift; } SixenseManager* getSixenseManager() { return &_sixenseManager; } @@ -167,7 +168,6 @@ 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); @@ -275,8 +275,6 @@ private: float& distance, BoxFace& face); void updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, float& distance, BoxFace& face); - void updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition); void updateHandAndTouch(float deltaTime); void updateLeap(float deltaTime); void updateSixense(float deltaTime); @@ -299,7 +297,6 @@ private: void renderHighlightVoxel(VoxelDetail voxel); void updateAvatar(float deltaTime); - void updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection); void queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions); void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); @@ -438,10 +435,6 @@ private: bool _lookingAwayFromOrigin; glm::vec3 _nudgeGuidePosition; - Avatar* _lookatTargetAvatar; - glm::vec3 _lookatOtherPosition; - float _lookatIndicatorScale; - glm::vec3 _transmitterPickStart; glm::vec3 _transmitterPickEnd; @@ -490,7 +483,6 @@ private: QReadWriteLock _voxelSceneStatsLock; std::vector _voxelFades; - std::vector _avatarFades; ControllerScriptingInterface _controllerScriptingInterface; QPointer _logDialog; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 740effcf1c..e9728b51d0 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -331,7 +331,6 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollisionProxies); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, true); - addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtIndicator, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::FaceshiftTCP, 0, @@ -899,7 +898,7 @@ void Menu::goToDomain() { } // send a node kill request, indicating to other clients that they should play the "disappeared" effect - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); + Application::getInstance()->getAvatar()->sendKillAvatar(); // give our nodeList the new domain-server hostname NodeList::getInstance()->setDomainHostname(domainDialog.textValue()); @@ -941,7 +940,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(&NODE_TYPE_AVATAR_MIXER, 1); + MyAvatar::sendKillAvatar(); qDebug("Going To Location: %f, %f, %f...", x, y, z); myAvatar->setPosition(newAvatarPos); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index dd3d5b7588..c17785e550 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -217,7 +217,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 Metavoxels = "Metavoxels"; const QString Mirror = "Mirror"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 7705a5d03e..2d15c2bfda 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -87,6 +87,7 @@ Avatar::Avatar(Node* owningNode) : Avatar::~Avatar() { + qDebug() << "Avatar" << this << "going away"; _headData = NULL; _handData = NULL; } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index f5090343f2..704c9bfb32 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -6,50 +6,185 @@ // Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // +#include #include +#include "Application.h" #include "Avatar.h" +#include "Menu.h" +#include "MyAvatar.h" #include "AvatarManager.h" AvatarManager::AvatarManager(QObject* parent) : - _hash() + _lookAtTargetAvatar(), + _lookAtOtherPosition(), + _lookAtIndicatorScale(1.0f), + _avatarHash(), + _avatarFades() { } +void AvatarManager::updateLookAtTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, + glm::vec3 &eyePosition) { + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()"); + + if (!Application::getInstance()->isMousePressed()) { + foreach (const AvatarSharedPointer& avatar, _avatarHash) { + 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(); + + _lookAtTargetAvatar = avatar; + + // found the look at target avatar, return + return; + } + } + + _lookAtTargetAvatar.clear(); + } +} + +void AvatarManager::updateAvatars(float deltaTime, const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection) { + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); + + // simulate avatars + foreach (const AvatarSharedPointer& avatar, _avatarHash) { + avatar->simulate(deltaTime, NULL); + avatar->setMouseRay(mouseRayOrigin, mouseRayDirection); + } + + // 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::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) { + qDebug() << "Fade for" << fadingAvatar->data() << "over"; + 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) { unsigned char packetData[MAX_PACKET_SIZE]; memcpy(packetData, datagram.data(), datagram.size()); int numBytesPacketHeader = numBytesForPacketHeader(packetData); - - QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(numBytesPacketHeader, NUM_BYTES_RFC4122_UUID)); int bytesRead = numBytesPacketHeader; unsigned char avatarData[MAX_PACKET_SIZE]; - populateTypeAndVersion(avatarData, PACKET_TYPE_HEAD_DATA); + int numBytesDummyPacketHeader = populateTypeAndVersion(avatarData, PACKET_TYPE_HEAD_DATA); while (bytesRead < datagram.size()) { - Avatar* matchingAvatar = _hash.value(nodeUUID); + 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 = new Avatar(); + matchingAvatar = AvatarSharedPointer(new Avatar()); // insert the new avatar into our hash - _hash.insert(nodeUUID, matchingAvatar); + _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, packetData + bytesRead, datagram.size() - bytesRead); + 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); + bytesRead += matchingAvatar->parseData(avatarData, datagram.size() - bytesRead); } } @@ -59,11 +194,16 @@ void AvatarManager::processKillAvatar(const QByteArray& datagram) { (datagram.data())), NUM_BYTES_RFC4122_UUID)); - // kill the avatar with that UUID from our hash, if it exists - _hash.remove(nodeUUID); + // remove the avatar with that UUID from our hash, if it exists + AvatarSharedPointer removedAvatar = _avatarHash.take(nodeUUID); + + if (removedAvatar) { + // add this avatar to our vector of fades + _avatarFades.push_back(removedAvatar); + } } void AvatarManager::clearHash() { // clear the AvatarManager hash - typically happens on the removal of the avatar-mixer - _hash.clear(); + _avatarHash.clear(); } \ No newline at end of file diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index dde84ba62b..ea3647ecd7 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -11,19 +11,49 @@ #include #include +#include + +#include #include "Avatar.h" -class AvatarManager : public QObject { +typedef QSharedPointer AvatarSharedPointer; +typedef QHash 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(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, + glm::vec3& eyePosition); + + void updateAvatars(float deltaTime, const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection); + void renderAvatars(bool forceRenderHead, bool selfAvatarOnly); + + void clearHash(); + public slots: + void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList); + void processAvatarMixerDatagram(const QByteArray& datagram); void processKillAvatar(const QByteArray& datagram); - void clearHash(); + private: - QHash _hash; + void simulateAvatarFades(float deltaTime); + void renderAvatarFades(); + + QWeakPointer _lookAtTargetAvatar; + glm::vec3 _lookAtOtherPosition; + float _lookAtIndicatorScale; + + AvatarHash _avatarHash; + QVector _avatarFades; }; #endif /* defined(__hifi__AvatarManager__) */ diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index e5aa33ad40..7183fc52c8 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -185,49 +185,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 } } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6d7ab5f96c..d56bd71e52 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -791,16 +791,16 @@ void MyAvatar::updateChatCircle(float deltaTime) { // find all circle-enabled members and sort by distance QVector 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()); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f9da7e3a82..8d6d93e4ae 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -63,7 +63,7 @@ public: bool getDriveKeys(int key) { return _driveKeys[key]; }; void jump() { _shouldJump = true; }; - void sendKillAvatar(); + static void sendKillAvatar(); // Set/Get update the thrust that will move the avatar around void addThrust(glm::vec3 newThrust) { _thrust += newThrust; }; diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index fee5fbf6fc..83105f3f41 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -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(&NODE_TYPE_AVATAR_MIXER, 1); + 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; diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index ff65bc4298..d582b1f09e 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -192,37 +192,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(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 - - collisionInfo._penetration /= (float)(TREE_SCALE); - updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); - 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(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 +// +// collisionInfo._penetration /= (float)(TREE_SCALE); +// updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); +// applyHardCollision(particle, ELASTICITY, damping, collisionInfo); +// } +// } +// } +// } } // TODO: convert applyHardCollision() to take a CollisionInfo& instead of penetration + addedVelocity diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index d4ee5d1bf7..f3940f8808 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -102,8 +102,6 @@ public: int fillPingPacket(unsigned char* buffer); int fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer); void pingPublicAndLocalSocketsForInactiveNode(Node* node); - - void sendKillNode(const char* nodeTypes, int numNodeTypes); SharedNodePointer nodeWithAddress(const HifiSockAddr& senderSockAddr); SharedNodePointer nodeWithUUID(const QUuid& nodeUUID);