diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b7761a7042..289c2b7a45 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1038,6 +1038,11 @@ void Application::setWantsResIn(bool wantsResIn) { _myAvatar.setWantResIn(wantsResIn); } + +void Application::setWantsDelta(bool wantsDelta) { + _myAvatar.setWantDelta(wantsDelta); +} + static QIcon createSwatchIcon(const QColor& color) { QPixmap map(16, 16); map.fill(color); @@ -1051,7 +1056,7 @@ void Application::chooseVoxelPaintColor() { _voxelPaintColor->setIcon(createSwatchIcon(selected)); } } - + void Application::initMenu() { QMenuBar* menuBar = new QMenuBar(); _window->setMenuBar(menuBar); @@ -1114,10 +1119,12 @@ void Application::initMenu() { debugMenu->addAction("Calculate Tree Stats", this, SLOT(doTreeStats()), Qt::SHIFT | Qt::Key_S); debugMenu->addAction("Wants Res-In", this, SLOT(setWantsResIn(bool)))->setCheckable(true); debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true); + debugMenu->addAction("Wants View Delta Sending", this, SLOT(setWantsDelta(bool)))->setCheckable(true); } void Application::updateFrustumRenderModeAction() { switch (_frustumDrawingMode) { + default: case FRUSTUM_DRAW_MODE_ALL: _frustumRenderModeAction->setText("Render Mode - All"); break; diff --git a/interface/src/Application.h b/interface/src/Application.h index 6ad644de63..3d145991ce 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -87,6 +87,7 @@ private slots: void doTreeStats(); void setWantsMonochrome(bool wantsMonochrome); void setWantsResIn(bool wantsResIn); + void setWantsDelta(bool wantsDelta); void chooseVoxelPaintColor(); private: diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 60d28ce6fb..1f4856abf7 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -108,8 +108,12 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { destinationBuffer += _chatMessage.size() * sizeof(char); // voxel sending features... - *destinationBuffer++ = _wantResIn; - *destinationBuffer++ = _wantColor; + // voxel sending features... + unsigned char wantItems = 0; + if (_wantResIn) { setAtBit(wantItems,WANT_RESIN_AT_BIT); } + if (_wantColor) { setAtBit(wantItems,WANT_COLOR_AT_BIT); } + if (_wantDelta) { setAtBit(wantItems,WANT_DELTA_AT_BIT); } + *destinationBuffer++ = wantItems; return destinationBuffer - bufferStart; } @@ -117,6 +121,8 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { // called on the other agents - assigns it to my views of the others int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { +//printf("AvatarData::parseData()\n"); + // increment to push past the packet header sourceBuffer += sizeof(PACKET_HEADER_HEAD_DATA); @@ -184,8 +190,12 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { sourceBuffer += chatMessageSize * sizeof(char); // voxel sending features... - _wantResIn = (bool)*sourceBuffer++; - _wantColor = (bool)*sourceBuffer++; + unsigned char wantItems = 0; + wantItems = (unsigned char)*sourceBuffer++; + + _wantResIn = oneAtBit(wantItems,WANT_RESIN_AT_BIT); + _wantColor = oneAtBit(wantItems,WANT_COLOR_AT_BIT); + _wantDelta = oneAtBit(wantItems,WANT_DELTA_AT_BIT); return sourceBuffer - startPosition; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 8eb7093545..c66402e21a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -15,6 +15,10 @@ #include +const int WANT_RESIN_AT_BIT = 0; +const int WANT_COLOR_AT_BIT = 1; +const int WANT_DELTA_AT_BIT = 2; + enum KeyState { NO_KEY_DOWN, @@ -124,8 +128,10 @@ public: // related to Voxel Sending strategies bool getWantResIn() const { return _wantResIn; } bool getWantColor() const { return _wantColor; } + bool getWantDelta() const { return _wantDelta; } void setWantResIn(bool wantResIn) { _wantResIn = wantResIn; } void setWantColor(bool wantColor) { _wantColor = wantColor; } + void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; } protected: glm::vec3 _position; @@ -168,8 +174,10 @@ protected: // chat message std::string _chatMessage; + // voxel server sending items bool _wantResIn; bool _wantColor; + bool _wantDelta; }; #endif /* defined(__hifi__AvatarData__) */ diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 0c399faf6c..d6820897b8 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -94,6 +94,11 @@ bool oneAtBit(unsigned char byte, int bitIndex) { return (byte >> (7 - bitIndex) & 1); } +void setAtBit(unsigned char& byte, int bitIndex) { + byte += (1 << (7 - bitIndex)); +} + + void switchToResourcesParentIfRequired() { #ifdef __APPLE__ CFBundleRef mainBundle = CFBundleGetMainBundle(); diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 9d2fcb8799..98baa5488a 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -49,6 +49,7 @@ void outputBits(unsigned char byte, bool withNewLine = true); void printVoxelCode(unsigned char* voxelCode); int numberOfOnes(unsigned char byte); bool oneAtBit(unsigned char byte, int bitIndex); +void setAtBit(unsigned char& byte, int bitIndex); void switchToResourcesParentIfRequired(); diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index a107e2f869..25022ae521 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -276,3 +276,17 @@ void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& origin = _nearTopLeft + x*(_nearTopRight - _nearTopLeft) + y*(_nearBottomLeft - _nearTopLeft); direction = glm::normalize(origin - _position); } + + +void ViewFrustum::printDebugDetails() const { + printLog("ViewFrustum::printDebugDetails()... \n"); + printLog("_position=%f,%f,%f\n", _position.x, _position.y, _position.z ); + printLog("_direction=%f,%f,%f\n", _direction.x, _direction.y, _direction.z ); + printLog("_up=%f,%f,%f\n", _up.x, _up.y, _up.z ); + printLog("_right=%f,%f,%f\n", _right.x, _right.y, _right.z ); + printLog("_fieldOfView=%f\n", _fieldOfView); + printLog("_aspectRatio=%f\n", _aspectRatio); + printLog("_nearClip=%f\n", _nearClip); + printLog("_farClip=%f\n", _farClip); +} + diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index 007654fcf4..aadb1f86e8 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -102,6 +102,8 @@ public: bool matches(const ViewFrustum& compareTo) const; bool matches(const ViewFrustum* compareTo) const { return matches(*compareTo); }; void computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const; + + void printDebugDetails() const; }; diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 5df39140ec..d22073a54b 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -617,12 +617,14 @@ void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float v this->reaverageVoxelColors(this->rootNode); } -int VoxelTree::searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag) { +int VoxelTree::searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag, + bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) { // call the recursive version, this will add all found colored node roots to the bag int currentSearchLevel = 0; - int levelReached = searchForColoredNodesRecursion(maxSearchLevel, currentSearchLevel, rootNode, viewFrustum, bag); + int levelReached = searchForColoredNodesRecursion(maxSearchLevel, currentSearchLevel, rootNode, + viewFrustum, bag, deltaViewFrustum, lastViewFrustum); return levelReached; } @@ -667,7 +669,8 @@ bool VoxelTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& di } int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel, - VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag) { + VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag, + bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) { // Keep track of how deep we've searched. currentSearchLevel++; @@ -702,7 +705,7 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe bool childIsColored = (childNode && childNode->isColored()); bool childIsInView = (childNode && childNode->isInView(viewFrustum)); bool childIsLeaf = (childNode && childNode->isLeaf()); - + if (childIsInView) { // track children in view as existing and not a leaf @@ -739,7 +742,8 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe for (int i = 0; i < inViewCount; i++) { VoxelNode* childNode = inViewChildren[i]; thisLevel = currentSearchLevel; // reset this, since the children will munge it up - int childLevelReached = searchForColoredNodesRecursion(maxSearchLevel, thisLevel, childNode, viewFrustum, bag); + int childLevelReached = searchForColoredNodesRecursion(maxSearchLevel, thisLevel, childNode, viewFrustum, bag, + deltaViewFrustum, lastViewFrustum); maxChildLevel = std::max(maxChildLevel, childLevelReached); } } @@ -747,7 +751,8 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe } int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes, - VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor) const { + VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor, + bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const { // How many bytes have we written so far at this level; int bytesWritten = 0; @@ -767,7 +772,8 @@ int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned int currentEncodeLevel = 0; int childBytesWritten = encodeTreeBitstreamRecursion(maxEncodeLevel, currentEncodeLevel, - node, outputBuffer, availableBytes, bag, viewFrustum, includeColor); + node, outputBuffer, availableBytes, bag, viewFrustum, includeColor, + deltaViewFrustum, lastViewFrustum); // if childBytesWritten == 1 then something went wrong... that's not possible assert(childBytesWritten != 1); @@ -790,7 +796,8 @@ int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes, - VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor) const { + VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor, + bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const { // How many bytes have we written so far at this level; int bytesAtThisLevel = 0; @@ -846,6 +853,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { VoxelNode* childNode = node->getChildAtIndex(i); bool childIsInView = (childNode && (!viewFrustum || childNode->isInView(*viewFrustum))); + if (childIsInView) { // Before we determine consider this further, let's see if it's in our LOD scope... float distance = viewFrustum ? childNode->distanceToCamera(*viewFrustum) : 0; @@ -861,9 +869,12 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco childrenExistBits += (1 << (7 - i)); inViewNotLeafCount++; } + + bool childWasInView = (childNode && deltaViewFrustum && + (lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*lastViewFrustum))); - // track children with actual color - if (childNode && childNode->isColored()) { + // track children with actual color, only if the child wasn't previously in view! + if (childNode && childNode->isColored() && !childWasInView) { childrenColoredBits += (1 << (7 - i)); inViewWithColorCount++; } @@ -925,7 +936,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco int thisLevel = currentEncodeLevel; int childTreeBytesOut = encodeTreeBitstreamRecursion(maxEncodeLevel, thisLevel, childNode, outputBuffer, availableBytes, bag, - viewFrustum, includeColor); + viewFrustum, includeColor, + deltaViewFrustum, lastViewFrustum); // if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space, // basically, the children below don't contain any info. diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 649bbfc92d..3c33c7b02c 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -55,9 +55,11 @@ public: void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL); int encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes, - VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor = true) const; + VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor = true, + bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = NULL) const; - int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag); + int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag, + bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = NULL); bool isDirty() const { return _isDirty; }; void clearDirtyBit() { _isDirty = false; }; @@ -79,10 +81,12 @@ public: private: int encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel, VoxelNode* node, unsigned char* outputBuffer, int availableBytes, - VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor) const; + VoxelNodeBag& bag, const ViewFrustum* viewFrustum, bool includeColor, + bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) const; int searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel, - VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag); + VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag, + bool deltaViewFrustum, const ViewFrustum* lastViewFrustum); static bool countVoxelsOperation(VoxelNode* node, void* extraData); diff --git a/voxel-server/src/VoxelAgentData.cpp b/voxel-server/src/VoxelAgentData.cpp index 32526be383..4eb971f275 100644 --- a/voxel-server/src/VoxelAgentData.cpp +++ b/voxel-server/src/VoxelAgentData.cpp @@ -22,6 +22,7 @@ void VoxelAgentData::init() { _maxSearchLevel = 1; _maxLevelReachedInLastSearch = 1; resetVoxelPacket(); + _viewSent = false; } void VoxelAgentData::resetVoxelPacket() { @@ -50,3 +51,31 @@ VoxelAgentData::VoxelAgentData(const VoxelAgentData &otherAgentData) { VoxelAgentData* VoxelAgentData::clone() const { return new VoxelAgentData(*this); } + +bool VoxelAgentData::updateCurrentViewFrustum() { + bool currentViewFrustumChanged = false; + ViewFrustum newestViewFrustum; + // get position and orientation details from the camera + newestViewFrustum.setPosition(getCameraPosition()); + newestViewFrustum.setOrientation(getCameraDirection(), getCameraUp(), getCameraRight()); + + // Also make sure it's got the correct lens details from the camera + newestViewFrustum.setFieldOfView(getCameraFov()); + newestViewFrustum.setAspectRatio(getCameraAspectRatio()); + newestViewFrustum.setNearClip(getCameraNearClip()); + newestViewFrustum.setFarClip(getCameraFarClip()); + + // if there has been a change, then recalculate + if (!newestViewFrustum.matches(_currentViewFrustum)) { + _currentViewFrustum = newestViewFrustum; + _currentViewFrustum.calculate(); + currentViewFrustumChanged = true; + } + return currentViewFrustumChanged; +} + +void VoxelAgentData::updateLastKnownViewFrustum() { + // save our currentViewFrustum into our lastKnownViewFrustum + _lastKnownViewFrustum = _currentViewFrustum; +} + diff --git a/voxel-server/src/VoxelAgentData.h b/voxel-server/src/VoxelAgentData.h index 74a402bb78..99be7e6042 100644 --- a/voxel-server/src/VoxelAgentData.h +++ b/voxel-server/src/VoxelAgentData.h @@ -40,13 +40,29 @@ public: void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; } VoxelNodeBag nodeBag; + + ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; }; + ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; }; + + // These are not classic setters because they are calculating and maintaining state + // which is set asynchronously through the network receive + bool updateCurrentViewFrustum(); + void updateLastKnownViewFrustum(); + + bool getViewSent() const { return _viewSent; }; + void setViewSent(bool viewSent) { _viewSent = viewSent; } + private: + bool _viewSent; unsigned char* _voxelPacket; unsigned char* _voxelPacketAt; int _voxelPacketAvailableBytes; bool _voxelPacketWaiting; int _maxSearchLevel; int _maxLevelReachedInLastSearch; + ViewFrustum _currentViewFrustum; + ViewFrustum _lastKnownViewFrustum; + }; #endif /* defined(__hifi__VoxelAgentData__) */ diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 29b211e44f..74a2ca73ca 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -107,10 +107,8 @@ void eraseVoxelTreeAndCleanupAgentVisitData() { // Version of voxel distributor that sends each LOD level at a time void resInVoxelDistributor(AgentList* agentList, AgentList::iterator& agent, - VoxelAgentData* agentData, - ViewFrustum& viewFrustum) { - - printf("resInVoxelDistributor()\n"); + VoxelAgentData* agentData) { + ViewFrustum viewFrustum = agentData->getCurrentViewFrustum(); bool searchReset = false; int searchLoops = 0; int searchLevelWas = agentData->getMaxSearchLevel(); @@ -227,14 +225,32 @@ void resInVoxelDistributor(AgentList* agentList, void deepestLevelVoxelDistributor(AgentList* agentList, AgentList::iterator& agent, VoxelAgentData* agentData, - ViewFrustum& viewFrustum) { - - printf("deepestLevelVoxelDistributor()\n"); + bool viewFrustumChanged) { int maxLevelReached = 0; double start = usecTimestampNow(); - if (agentData->nodeBag.isEmpty()) { - maxLevelReached = randomTree.searchForColoredNodes(INT_MAX, randomTree.rootNode, viewFrustum, agentData->nodeBag); + + // FOR NOW... agent tells us if it wants to receive only view frustum deltas + bool wantDelta = agentData->getWantDelta(); + const ViewFrustum* lastViewFrustum = wantDelta ? &agentData->getLastKnownViewFrustum() : NULL; + + if (::debugVoxelSending) { + printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", + viewFrustumChanged ? "yes" : "no", + agentData->nodeBag.isEmpty() ? "yes" : "no", + agentData->getViewSent() ? "yes" : "no" + ); + } + + // If the current view frustum has changed OR we have nothing to send, then search against + // the current view frustum for things to send. + if (viewFrustumChanged || agentData->nodeBag.isEmpty()) { + // If the bag was empty, then send everything in view, not just the delta + maxLevelReached = randomTree.searchForColoredNodes(INT_MAX, randomTree.rootNode, agentData->getCurrentViewFrustum(), + agentData->nodeBag, wantDelta, lastViewFrustum); + + agentData->setViewSent(false); + } double end = usecTimestampNow(); double elapsedmsec = (end - start)/1000.0; @@ -266,8 +282,8 @@ void deepestLevelVoxelDistributor(AgentList* agentList, VoxelNode* subTree = agentData->nodeBag.extract(); bytesWritten = randomTree.encodeTreeBitstream(INT_MAX, subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, - agentData->nodeBag, &viewFrustum, - agentData->getWantColor()); + agentData->nodeBag, &agentData->getCurrentViewFrustum(), + agentData->getWantColor(), wantDelta, lastViewFrustum); if (agentData->getAvailable() >= bytesWritten) { agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten); @@ -313,7 +329,16 @@ void deepestLevelVoxelDistributor(AgentList* agentList, printf("packetLoop() took %lf milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", elapsedmsec, trueBytesSent, truePacketsSent, agentData->nodeBag.count()); } - } + + // if after sending packets we've emptied our bag, then we want to remember that we've sent all + // the voxels from the current view frustum + if (agentData->nodeBag.isEmpty()) { + agentData->updateLastKnownViewFrustum(); + agentData->setViewSent(true); + } + + + } // end if bag wasn't empty, and so we sent stuff... } void persistVoxelsWhenDirty() { @@ -340,23 +365,15 @@ void *distributeVoxelsToListeners(void *args) { // Sometimes the agent data has not yet been linked, in which case we can't really do anything if (agentData) { - ViewFrustum viewFrustum; - // get position and orientation details from the camera - viewFrustum.setPosition(agentData->getCameraPosition()); - viewFrustum.setOrientation(agentData->getCameraDirection(), agentData->getCameraUp(), agentData->getCameraRight()); - - // Also make sure it's got the correct lens details from the camera - viewFrustum.setFieldOfView(agentData->getCameraFov()); - viewFrustum.setAspectRatio(agentData->getCameraAspectRatio()); - viewFrustum.setNearClip(agentData->getCameraNearClip()); - viewFrustum.setFarClip(agentData->getCameraFarClip()); - - viewFrustum.calculate(); + bool viewFrustumChanged = agentData->updateCurrentViewFrustum(); + if (::debugVoxelSending) { + printf("agentData->updateCurrentViewFrustum() changed=%s\n", (viewFrustumChanged ? "yes" : "no")); + } if (agentData->getWantResIn()) { - resInVoxelDistributor(agentList, agent, agentData, viewFrustum); + resInVoxelDistributor(agentList, agent, agentData); } else { - deepestLevelVoxelDistributor(agentList, agent, agentData, viewFrustum); + deepestLevelVoxelDistributor(agentList, agent, agentData, viewFrustumChanged); } } }