From a7750501c6751473f8749befbf96c90c2158d883 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 13 Dec 2016 18:58:11 -0800 Subject: [PATCH] first cut at adding view frustum support to avatar mixer --- assignment-client/src/avatars/AvatarMixer.cpp | 23 +++++++ assignment-client/src/avatars/AvatarMixer.h | 1 + .../src/avatars/AvatarMixerClientData.cpp | 7 ++ .../src/avatars/AvatarMixerClientData.h | 9 +++ interface/src/Application.cpp | 9 +++ interface/src/Application.h | 1 + libraries/networking/src/udt/PacketHeaders.h | 3 +- libraries/shared/src/ViewFrustum.cpp | 67 +++++++++++++++++++ libraries/shared/src/ViewFrustum.h | 4 ++ 9 files changed, 123 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index a37c9f7dd5..36952d4fd6 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -44,6 +44,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); + packetReceiver.registerListener(PacketType::ViewFrustum, this, "handleViewFrustumPacket"); packetReceiver.registerListener(PacketType::AvatarData, this, "handleAvatarDataPacket"); packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); @@ -275,6 +276,16 @@ void AvatarMixer::broadcastAvatarData() { } // Not close enough to ignore nodeData->removeFromRadiusIgnoringSet(otherNode->getUUID()); + + + // Also check to see if the other node is in our view + glm::vec3 otherNodeBoxScale = (otherData->getPosition() - otherData->getGlobalBoundingBoxCorner()) * 2.0f; + AABox otherNodeBox(otherData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + if (!nodeData->otherAvatarInView(otherNodeBox)) { + qDebug() << "Avatar out of view!"; + return false; + } + return true; } }, @@ -448,6 +459,18 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { } } +void AvatarMixer::handleViewFrustumPacket(QSharedPointer message, SharedNodePointer senderNode) { + auto nodeList = DependencyManager::get(); + nodeList->getOrCreateLinkedData(senderNode); + + if (senderNode->getLinkedData()) { + AvatarMixerClientData* nodeData = dynamic_cast(senderNode->getLinkedData()); + if (nodeData != nullptr) { + nodeData->readViewFrustumPacket(message->getMessage()); + } + } +} + void AvatarMixer::handleAvatarDataPacket(QSharedPointer message, SharedNodePointer senderNode) { auto nodeList = DependencyManager::get(); nodeList->updateNodeWithDataFromPacket(message, senderNode); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index f537cc9244..ba74b43308 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -34,6 +34,7 @@ public slots: void sendStatsPacket() override; private slots: + void handleViewFrustumPacket(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarDataPacket(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode); void handleKillAvatarPacket(QSharedPointer message); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index de843c7cea..3773bd723f 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -57,6 +57,13 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe } } +void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) { + _currentViewFrustumIsValid = true; + _currentViewFrustum.fromByteArray(message); + qDebug() << __FUNCTION__; + _currentViewFrustum.printDebugDetails(); +} + void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["display_name"] = _avatar->getDisplayName(); jsonObject["full_rate_distance"] = _fullRateDistance; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 20c1b6b647..85955804a9 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -27,6 +27,7 @@ #include #include #include +#include const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps"; const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps"; @@ -87,6 +88,12 @@ public: void removeFromRadiusIgnoringSet(const QUuid& other) { _radiusIgnoredOthers.erase(other); } void ignoreOther(SharedNodePointer self, SharedNodePointer other); + void readViewFrustumPacket(const QByteArray& message); + + bool otherAvatarInView(const AABox& otherAvatarBox) { + return !_currentViewFrustumIsValid || _currentViewFrustum.boxIntersectsKeyhole(otherAvatarBox); + } + private: AvatarSharedPointer _avatar { new AvatarData() }; @@ -108,6 +115,8 @@ private: SimpleMovingAverage _avgOtherAvatarDataRate; std::unordered_set _radiusIgnoredOthers; + ViewFrustum _currentViewFrustum; + bool _currentViewFrustumIsValid { false }; }; #endif // hifi_AvatarMixerClientData_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4ec29b636b..8c3e3fad73 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4176,6 +4176,7 @@ void Application::update(float deltaTime) { if (DependencyManager::get()->shouldRenderEntities()) { queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions); } + sendAvatarViewFrustum(); _lastQueriedViewFrustum = _viewFrustum; } } @@ -4215,6 +4216,14 @@ void Application::update(float deltaTime) { AnimDebugDraw::getInstance().update(); } +void Application::sendAvatarViewFrustum() { + QByteArray viewFrustumByteArray = _viewFrustum.toByteArray(); + auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size()); + avatarPacket->write(viewFrustumByteArray); + + DependencyManager::get()->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer); +} + int Application::sendNackPackets() { diff --git a/interface/src/Application.h b/interface/src/Application.h index 5ab94465cc..ea768b956b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -415,6 +415,7 @@ private: void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed); int sendNackPackets(); + void sendAvatarViewFrustum(); std::shared_ptr getMyAvatar() const; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index dbeb1e63b0..bc5ff6fe98 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -101,7 +101,8 @@ public: NodeKickRequest, NodeMuteRequest, RadiusIgnoreRequest, - LAST_PACKET_TYPE = RadiusIgnoreRequest + ViewFrustum, + LAST_PACKET_TYPE = ViewFrustum }; }; diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 0a1a41ace1..ead8f8634e 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -130,6 +130,73 @@ const char* ViewFrustum::debugPlaneName (int plane) const { return "Unknown"; } +void ViewFrustum::fromByteArray(const QByteArray& input) { + + // From the wire! + glm::vec3 cameraPosition; + glm::quat cameraOrientation; + float cameraCenterRadius; + float cameraFov; + float cameraAspectRatio; + float cameraNearClip; + float cameraFarClip; + + const unsigned char* startPosition = reinterpret_cast(input.constData()); + const unsigned char* sourceBuffer = startPosition; + + // camera details + memcpy(&cameraPosition, sourceBuffer, sizeof(cameraPosition)); + sourceBuffer += sizeof(cameraPosition); + sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, cameraOrientation); + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &cameraFov); + sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, cameraAspectRatio); + sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, cameraNearClip); + sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, cameraFarClip); + memcpy(&cameraCenterRadius, sourceBuffer, sizeof(cameraCenterRadius)); + sourceBuffer += sizeof(cameraCenterRadius); + + setPosition(cameraPosition); + setOrientation(cameraOrientation); + setCenterRadius(cameraCenterRadius); + + // Also make sure it's got the correct lens details from the camera + const float VIEW_FRUSTUM_FOV_OVERSEND = 60.0f; + float originalFOV = cameraFov; + float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND; + + if (0.0f != cameraAspectRatio && + 0.0f != cameraNearClip && + 0.0f != cameraFarClip && + cameraNearClip != cameraFarClip) { + setProjection(glm::perspective( + glm::radians(wideFOV), // hack + cameraAspectRatio, + cameraNearClip, + cameraFarClip)); + } +} + + +QByteArray ViewFrustum::toByteArray() { + static const int LARGE_ENOUGH = 1024; + QByteArray viewFrustumDataByteArray(LARGE_ENOUGH, 0); + unsigned char* destinationBuffer = reinterpret_cast(viewFrustumDataByteArray.data()); + unsigned char* startPosition = destinationBuffer; + + // camera details + memcpy(destinationBuffer, &_position, sizeof(_position)); + destinationBuffer += sizeof(_position); + destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _orientation); + destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _fieldOfView); + destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _aspectRatio); + destinationBuffer += packClipValueToTwoByte(destinationBuffer, _nearClip); + destinationBuffer += packClipValueToTwoByte(destinationBuffer, _farClip); + memcpy(destinationBuffer, &_centerSphereRadius, sizeof(_centerSphereRadius)); + destinationBuffer += sizeof(_centerSphereRadius); + + return viewFrustumDataByteArray.left(destinationBuffer - startPosition); +} + ViewFrustum::intersection ViewFrustum::calculateCubeFrustumIntersection(const AACube& cube) const { // only check against frustum ViewFrustum::intersection result = INSIDE; diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 091aff7a92..9a6cb9ab68 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -139,6 +139,10 @@ public: const ::Plane* getPlanes() const { return _planes; } void invalidate(); // causes all reasonable intersection tests to fail + + QByteArray toByteArray(); + void fromByteArray(const QByteArray& input); + private: glm::mat4 _view; glm::mat4 _projection;