// // OctreeQueryNode.cpp // libraries/octree/src // // Created by Stephen Birarda on 3/21/13. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "OctreeQueryNode.h" #include #include #include #include #include void OctreeQueryNode::nodeKilled() { _isShuttingDown = true; } bool OctreeQueryNode::packetIsDuplicate() const { // if shutting down, return immediately if (_isShuttingDown) { return false; } // since our packets now include header information, like sequence number, and createTime, we can't just do a memcmp // of the entire packet, we need to compare only the packet content... if (_lastOctreePacketLength == _octreePacket->getPayloadSize()) { if (memcmp(_lastOctreePayload.data() + OCTREE_PACKET_EXTRA_HEADERS_SIZE, _octreePacket->getPayload() + OCTREE_PACKET_EXTRA_HEADERS_SIZE, _octreePacket->getPayloadSize() - OCTREE_PACKET_EXTRA_HEADERS_SIZE) == 0) { return true; } } return false; } bool OctreeQueryNode::shouldSuppressDuplicatePacket() { // if shutting down, return immediately if (_isShuttingDown) { return true; } bool shouldSuppress = false; // assume we won't suppress // only consider duplicate packets if (packetIsDuplicate()) { _duplicatePacketCount++; // If this is the first suppressed packet, remember our time... if (_duplicatePacketCount == 1) { _firstSuppressedPacket = usecTimestampNow(); } // How long has it been since we've sent one, if we're still under our max time, then keep considering // this packet for suppression quint64 now = usecTimestampNow(); long sinceFirstSuppressedPacket = now - _firstSuppressedPacket; const long MAX_TIME_BETWEEN_DUPLICATE_PACKETS = 1000 * 1000; // 1 second. if (sinceFirstSuppressedPacket < MAX_TIME_BETWEEN_DUPLICATE_PACKETS) { // Finally, if we know we've sent at least one duplicate out, then suppress the rest... if (_duplicatePacketCount >= 1) { shouldSuppress = true; } } else { // Reset our count, we've reached our maximum time. _duplicatePacketCount = 0; } } else { // Reset our count, it wasn't a duplicate _duplicatePacketCount = 0; } return shouldSuppress; } void OctreeQueryNode::init() { _myPacketType = getMyPacketType(); _octreePacket = NLPacket::create(getMyPacketType()); resetOctreePacket(); // don't bump sequence } void OctreeQueryNode::resetOctreePacket() { // if shutting down, return immediately if (_isShuttingDown) { return; } // Whenever we call this, we will keep a copy of the last packet, so we can determine if the last packet has // changed since we last reset it. Since we know that no two packets can ever be identical without being the same // scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing // packet send rate. _lastOctreePacketLength = _octreePacket->getPayloadSize(); memcpy(_lastOctreePayload.data(), _octreePacket->getPayload(), _lastOctreePacketLength); // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use // the clients requested color state. OCTREE_PACKET_FLAGS flags = 0; setAtBit(flags, PACKET_IS_COLOR_BIT); // always color setAtBit(flags, PACKET_IS_COMPRESSED_BIT); // always compressed _octreePacket->reset(); // pack in flags _octreePacket->writePrimitive(flags); // pack in sequence number _octreePacket->writePrimitive(_sequenceNumber); // pack in timestamp OCTREE_PACKET_SENT_TIME now = usecTimestampNow(); _octreePacket->writePrimitive(now); _octreePacketWaiting = false; } void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int bytes) { // if shutting down, return immediately if (_isShuttingDown) { return; } // compressed packets include lead bytes which contain compressed size, this allows packing of // multiple compressed portions together OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionSize = bytes; _octreePacket->writePrimitive(sectionSize); if (bytes <= _octreePacket->bytesAvailableForWrite()) { _octreePacket->write(reinterpret_cast(buffer), bytes); _octreePacketWaiting = true; } } void OctreeQueryNode::copyCurrentViewFrustum(ViewFrustum& viewOut) const { QMutexLocker viewLocker(&_viewMutex); viewOut = _currentViewFrustum; } bool OctreeQueryNode::updateCurrentViewFrustum() { // if shutting down, return immediately if (_isShuttingDown) { return false; } if (!_usesFrustum) { // this client does not use a view frustum so the view frustum for this query has not changed return false; } else { bool currentViewFrustumChanged = false; ViewFrustum newestViewFrustum; // get position and orientation details from the camera newestViewFrustum.setPosition(getCameraPosition()); newestViewFrustum.setOrientation(getCameraOrientation()); newestViewFrustum.setCenterRadius(getCameraCenterRadius()); // Also make sure it's got the correct lens details from the camera float originalFOV = getCameraFov(); float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND; if (0.0f != getCameraAspectRatio() && 0.0f != getCameraNearClip() && 0.0f != getCameraFarClip() && getCameraNearClip() != getCameraFarClip()) { newestViewFrustum.setProjection(glm::perspective( glm::radians(wideFOV), // hack getCameraAspectRatio(), getCameraNearClip(), getCameraFarClip())); newestViewFrustum.calculate(); } { // if there has been a change, then recalculate QMutexLocker viewLocker(&_viewMutex); if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) { _currentViewFrustum = newestViewFrustum; currentViewFrustumChanged = true; } } // Also check for LOD changes from the client if (_lodInitialized) { if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) { _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust(); _lodChanged = true; } if (_lastClientOctreeSizeScale != getOctreeSizeScale()) { _lastClientOctreeSizeScale = getOctreeSizeScale(); _lodChanged = true; } } else { _lodInitialized = true; _lastClientOctreeSizeScale = getOctreeSizeScale(); _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust(); _lodChanged = false; } // When we first detect that the view stopped changing, we record this. // but we don't change it back to false until we've completely sent this // scene. if (_viewFrustumChanging && !currentViewFrustumChanged) { _viewFrustumJustStoppedChanging = true; } _viewFrustumChanging = currentViewFrustumChanged; return currentViewFrustumChanged; } } void OctreeQueryNode::setViewSent(bool viewSent) { _viewSent = viewSent; if (viewSent) { _viewFrustumJustStoppedChanging = false; _lodChanged = false; } } void OctreeQueryNode::packetSent(const NLPacket& packet) { _sentPacketHistory.packetSent(_sequenceNumber, packet); _sequenceNumber++; } bool OctreeQueryNode::hasNextNackedPacket() const { return !_nackedSequenceNumbers.isEmpty(); } const NLPacket* OctreeQueryNode::getNextNackedPacket() { if (!_nackedSequenceNumbers.isEmpty()) { // could return null if packet is not in the history return _sentPacketHistory.getPacket(_nackedSequenceNumbers.dequeue()); } return nullptr; } void OctreeQueryNode::parseNackPacket(ReceivedMessage& message) { // read sequence numbers while (message.getBytesLeftToRead()) { OCTREE_PACKET_SEQUENCE sequenceNumber; message.readPrimitive(&sequenceNumber); _nackedSequenceNumbers.enqueue(sequenceNumber); } } bool OctreeQueryNode::haveJSONParametersChanged() { bool parametersChanged = false; auto currentParameters = getJSONParameters(); if (_lastCheckJSONParameters != currentParameters) { parametersChanged = true; _lastCheckJSONParameters = currentParameters; } return parametersChanged; }