diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64a50a5b99..572f42bdee 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -86,6 +86,8 @@ const float MIRROR_FULLSCREEN_DISTANCE = 0.35f; const float MIRROR_REARVIEW_DISTANCE = 0.65f; const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f; +const char* LOCAL_VOXEL_CACHE = "/Users/brad/local_voxel_cache.svo"; + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { fprintf(stdout, "%s", message.toLocal8Bit().constData()); @@ -143,7 +145,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _resetRecentMaxPacketsSoon(true), _swatch(NULL), _pasteMode(false), - _logger(new FileLogger()) + _logger(new FileLogger()), + _persistThread(NULL) { _applicationStartupTime = startup_time; @@ -250,6 +253,14 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // Set the sixense filtering _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); + + _persistThread = new OctreePersistThread(_voxels.getTree(), LOCAL_VOXEL_CACHE); + + if (_persistThread) { + _voxels.beginLoadingLocalVoxelCache(); // while local voxels are importing, don't do individual node VBO updates + connect(_persistThread, SIGNAL(loadCompleted()), &_voxels, SLOT(localVoxelCacheLoaded())); + _persistThread->initialize(true); + } } Application::~Application() { @@ -1436,6 +1447,8 @@ void Application::terminate() { _voxelHideShowThread.terminate(); _voxelEditSender.terminate(); _particleEditSender.terminate(); + _persistThread->terminate(); + _persistThread = NULL; } static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& dataBytes) { @@ -2400,6 +2413,7 @@ void Application::updateThreads(float deltaTime) { _voxelHideShowThread.threadRoutine(); _voxelEditSender.threadRoutine(); _particleEditSender.threadRoutine(); + _persistThread->threadRoutine(); } } @@ -4536,3 +4550,8 @@ void Application::toggleLogDialog() { _logDialog->close(); } } + + +void Application::initAvatarAndViewFrustum() { + updateAvatar(0.f); +} \ No newline at end of file diff --git a/interface/src/Application.h b/interface/src/Application.h index 6f0463bc88..f36ad28a5d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -10,7 +10,7 @@ #define __interface__Application__ #include -#include +#include #include #include @@ -108,14 +108,14 @@ public: ~Application(); void restoreSizeAndPosition(); - void storeSizeAndPosition(); + void storeSizeAndPosition(); void initializeGL(); void paintGL(); void resizeGL(int width, int height); void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event); - + void mouseMoveEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); @@ -123,27 +123,27 @@ public: void touchBeginEvent(QTouchEvent* event); void touchEndEvent(QTouchEvent* event); void touchUpdateEvent(QTouchEvent* event); - + void updateWindowTitle(); void wheelEvent(QWheelEvent* event); void shootParticle(); // shoots a particle in the direction you're looking ParticleEditHandle* newParticleEditHandle(uint32_t id = NEW_PARTICLE); - ParticleEditHandle* makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, + ParticleEditHandle* makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString updateScript); - + void makeVoxel(glm::vec3 position, float scale, unsigned char red, unsigned char green, unsigned char blue, bool isDestructive); - + void removeVoxel(glm::vec3 position, float scale); - + const glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel); - + QGLWidget* getGLWidget() { return _glWidget; } MyAvatar* getAvatar() { return &_myAvatar; } Audio* getAudio() { return &_audio; } @@ -166,24 +166,24 @@ public: NodeToVoxelSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; } void lockVoxelSceneStats() { _voxelSceneStatsLock.lockForRead(); } void unlockVoxelSceneStats() { _voxelSceneStatsLock.unlock(); } - + QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; } GeometryCache* getGeometryCache() { return &_geometryCache; } TextureCache* getTextureCache() { return &_textureCache; } GlowEffect* getGlowEffect() { return &_glowEffect; } - + Avatar* getLookatTargetAvatar() const { return _lookatTargetAvatar; } - + Profile* getProfile() { return &_profile; } void resetProfile(const QString& username); - + static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes); - + void setupWorldLight(); void displaySide(Camera& whichCamera, bool selfAvatarOnly = false); - + /// Loads a view matrix that incorporates the specified model translation without the precision issues that can /// result from matrix multiplication at high translation magnitudes. void loadTranslatedViewMatrix(const glm::vec3& translation); @@ -197,9 +197,9 @@ public: virtual void nodeAdded(Node* node); virtual void nodeKilled(Node* node); virtual void packetSentNotification(ssize_t length); - + virtual void domainChanged(QString domain); - + VoxelShader& getVoxelShader() { return _voxelShader; } PointShader& getPointShader() { return _pointShader; } FileLogger* getLogger() { return _logger; } @@ -208,7 +208,7 @@ public: NodeToJurisdictionMap& getVoxelServerJurisdictions() { return _voxelServerJurisdictions; } NodeToJurisdictionMap& getParticleServerJurisdictions() { return _particleServerJurisdictions; } void pasteVoxelsToOctalCode(const unsigned char* octalCodeDestination); - + /// set a voxel which is to be rendered with a highlight void setHighlightVoxel(const VoxelDetail& highlightVoxel) { _highlightVoxel = highlightVoxel; } void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; } @@ -222,25 +222,26 @@ public slots: void pasteVoxels(); void nudgeVoxels(); void deleteVoxels(); - + void setRenderVoxels(bool renderVoxels); void doKillLocalVoxels(); void decreaseVoxelSize(); void increaseVoxelSize(); void loadScript(); void toggleLogDialog(); - + void initAvatarAndViewFrustum(); + private slots: - + void timer(); void idle(); void terminate(); - + void setFullscreen(bool fullscreen); - + void renderThrustAtVoxel(const glm::vec3& thrust); void renderLineToTouchedVoxel(); - + void renderCoverageMap(); void renderCoverageMapsRecursively(CoverageMap* map); @@ -250,7 +251,7 @@ private slots: glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint); void toggleFollowMode(); - + void closeMirrorView(); void restoreMirrorView(); void shrinkMirrorView(); @@ -265,17 +266,17 @@ private: static void processAvatarURLsMessage(unsigned char* packetData, size_t dataBytes); static void processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes); static void sendPingPackets(); - + void initDisplay(); void init(); - + void update(float deltaTime); // Various helper functions called during update() void updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection); void updateFaceshift(); void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, glm::vec3& lookAtRayDirection); - void updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, + void updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, float& distance, BoxFace& face); void updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, float& distance, BoxFace& face); @@ -298,32 +299,32 @@ private: Avatar* findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, glm::vec3& eyePosition, QUuid &nodeUUID); bool isLookingAtMyAvatar(Avatar* avatar); - + void renderLookatIndicator(glm::vec3 pointOfInterest); void renderFollowIndicator(); 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); - + glm::vec3 getSunDirection(); - + void updateShadowMap(); void displayOverlay(); void displayStats(); void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false); void renderViewFrustum(ViewFrustum& viewFrustum); - + void checkBandwidthMeterClick(); - + bool maybeEditVoxelUnderCursor(); void deleteVoxelUnderCursor(); void eyedropperVoxelUnderCursor(); - + void setMenuShortcutsEnabled(bool enabled); - + static void attachNewHeadToNode(Node *newNode); static void* networkReceive(void* args); // network receive thread @@ -333,18 +334,18 @@ private: QMainWindow* _window; QGLWidget* _glWidget; - + QAction* _followMode; - + BandwidthMeter _bandwidthMeter; SerialInterface _serialHeadSensor; QNetworkAccessManager* _networkAccessManager; QSettings* _settings; bool _displayLevels; - + glm::vec3 _gravity; - + // Frame Rate Measurement int _frameCount; float _fps; @@ -354,55 +355,55 @@ private: bool _justStarted; Stars _stars; - + Cloud _cloud; - + VoxelSystem _voxels; VoxelTree _clipboard; // if I copy/paste VoxelImporter _voxelImporter; VoxelSystem _sharedVoxelSystem; ViewFrustum _sharedVoxelSystemViewFrustum; - + ParticleTreeRenderer _particles; ParticleCollisionSystem _particleCollisionSystem; QByteArray _voxelsFilename; bool _wantToKillLocalVoxels; - + MetavoxelSystem _metavoxels; - + ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. Oscilloscope _audioScope; VoxelQuery _voxelQuery; // NodeData derived class for querying voxels from voxel server - + MyAvatar _myAvatar; // The rendered avatar of oneself Profile _profile; // The data-server linked profile for this user - + Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar - + Webcam _webcam; // The webcam interface - + Faceshift _faceshift; - + SixenseManager _sixenseManager; - + Camera _myCamera; // My view onto the world Camera _viewFrustumOffsetCamera; // The camera we use to sometimes show the view frustum from an offset mode Camera _mirrorCamera; // Cammera for mirror view QRect _mirrorViewRect; RearMirrorTools* _rearMirrorTools; - + glm::mat4 _untranslatedViewMatrix; glm::vec3 _viewMatrixTranslation; - + glm::mat4 _shadowMatrix; - + Environment _environment; - + int _headMouseX, _headMouseY; - + int _mouseX; int _mouseY; int _mouseDragStartedX; @@ -420,7 +421,7 @@ private: bool _isTouchPressed; // true if multitouch has been pressed (clear when finished) float _yawFromTouch; float _pitchFromTouch; - + VoxelDetail _mouseVoxelDragging; bool _mousePressed; // true if mouse has been pressed (clear when finished) @@ -428,7 +429,7 @@ private: bool _isHoverVoxel; bool _isHoverVoxelSounding; nodeColor _hoverVoxelOriginalColor; - + VoxelDetail _mouseVoxel; // details of the voxel to be edited float _mouseVoxelScale; // the scale for adding/removing voxels bool _mouseVoxelScaleInitialized; @@ -437,7 +438,7 @@ private: VoxelDetail _highlightVoxel; bool _isHighlightVoxel; - + VoxelDetail _nudgeVoxel; // details of the voxel to be nudged bool _nudgeStarted; bool _lookingAlongX; @@ -447,46 +448,46 @@ private: Avatar* _lookatTargetAvatar; glm::vec3 _lookatOtherPosition; float _lookatIndicatorScale; - + glm::vec3 _transmitterPickStart; glm::vec3 _transmitterPickEnd; - - bool _perfStatsOn; // Do we want to display perfStats? - - ChatEntry _chatEntry; // chat entry field - bool _chatEntryOn; // Whether to show the chat entry - + + bool _perfStatsOn; // Do we want to display perfStats? + + ChatEntry _chatEntry; // chat entry field + bool _chatEntryOn; // Whether to show the chat entry + GeometryCache _geometryCache; TextureCache _textureCache; - + GlowEffect _glowEffect; AmbientOcclusionEffect _ambientOcclusionEffect; VoxelShader _voxelShader; PointShader _pointShader; - + #ifndef _WIN32 Audio _audio; #endif - + bool _enableNetworkThread; pthread_t _networkReceiveThread; bool _stopNetworkReceiveThread; - + bool _enableProcessVoxelsThread; VoxelPacketProcessor _voxelProcessor; VoxelHideShowThread _voxelHideShowThread; VoxelEditPacketSender _voxelEditSender; ParticleEditPacketSender _particleEditSender; - + unsigned char _incomingPacket[MAX_PACKET_SIZE]; int _packetCount; int _packetsPerSecond; int _bytesPerSecond; int _bytesCount; - + int _recentMaxPackets; // recent max incoming voxel packets to process bool _resetRecentMaxPacketsSoon; - + StDev _idleLoopStdev; float _idleLoopMeasuredJitter; @@ -496,22 +497,24 @@ private: bool _pasteMode; PieMenu _pieMenu; - + int parseOctreeStats(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderAddress); void trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderSockAddr, bool wasStatsPacket); - + NodeToJurisdictionMap _voxelServerJurisdictions; NodeToJurisdictionMap _particleServerJurisdictions; NodeToVoxelSceneStats _octreeServerSceneStats; QReadWriteLock _voxelSceneStatsLock; - + std::vector _voxelFades; std::vector _avatarFades; ControllerScriptingInterface _controllerScriptingInterface; QPointer _logDialog; FileLogger* _logger; + + OctreePersistThread* _persistThread; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/VoxelHideShowThread.cpp b/interface/src/VoxelHideShowThread.cpp index 26a27bab52..512d269bd2 100644 --- a/interface/src/VoxelHideShowThread.cpp +++ b/interface/src/VoxelHideShowThread.cpp @@ -35,12 +35,12 @@ bool VoxelHideShowThread::process() { if (showExtraDebugging && elapsed > USECS_PER_FRAME) { qDebug() << "VoxelHideShowThread::process()... checkForCulling took " << elapsed << "\n"; } - + if (isStillRunning()) { if (elapsed < USECS_PER_FRAME) { uint64_t sleepFor = USECS_PER_FRAME - elapsed; usleep(sleepFor); } - } + } return isStillRunning(); // keep running till they terminate us } diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index d7c10215f8..e0b2decfaf 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -35,7 +35,6 @@ const bool VoxelSystem::DONT_BAIL_EARLY = false; - float identityVerticesGlobalNormals[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 }; float identityVertices[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1, //0-7 @@ -107,16 +106,18 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) _inSetupNewVoxelsForDrawing = false; _useFastVoxelPipeline = false; - + _culledOnce = false; _inhideOutOfView = false; _treeIsBusy = false; + } void VoxelSystem::elementDeleted(OctreeElement* element) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; if (voxel->getVoxelSystem() == this) { if (_voxelsInWriteArrays != 0) { +qDebug() << "elementDeleted()... about to call forceRemoveNodeFromArrays()\n"; forceRemoveNodeFromArrays(voxel); } else { if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { @@ -148,11 +149,11 @@ void VoxelSystem::elementUpdated(OctreeElement* element) { if (voxel->getShouldRender() != shouldRender) { voxel->setShouldRender(shouldRender); } - + if (!voxel->isLeaf()) { - + // As we check our children, see if any of them went from shouldRender to NOT shouldRender - // then we probably dropped LOD and if we don't have color, we want to average our children + // then we probably dropped LOD and if we don't have color, we want to average our children // for a new color. int childrenGotHiddenCount = 0; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -176,7 +177,7 @@ void VoxelSystem::elementUpdated(OctreeElement* element) { _voxelsUpdated++; voxel->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things. - + setupNewVoxelsForDrawingSingleNode(); } } @@ -219,7 +220,7 @@ void VoxelSystem::freeBufferIndex(glBufferIndex index) { break; } } - } + } if (!inList) { // make the index available for next node that needs to be drawn pthread_mutex_lock(&_freeIndexLock); @@ -308,7 +309,7 @@ void VoxelSystem::setVoxelsAsPoints(bool voxelsAsPoints) { } bool wasInitialized = _initialized; - + // If we're "turning on" Voxels as points, we need to double check that we're in voxel shader mode. // Voxels as points uses the VoxelShader memory model, so if we're not in voxel shader mode, // then set it to voxel shader mode. @@ -318,7 +319,7 @@ void VoxelSystem::setVoxelsAsPoints(bool voxelsAsPoints) { // If enabling this... then do it before checking voxel shader status, that way, if voxel // shader is already enabled, we just start drawing as points. _voxelsAsPoints = true; - + if (!_useVoxelShader) { setUseVoxelShader(true); _voxelShaderModeWhenVoxelsAsPointsEnabled = false; @@ -354,7 +355,7 @@ void VoxelSystem::cleanupVoxelMemory() { delete[] _writeVoxelShaderData; delete[] _readVoxelShaderData; - + _writeVoxelShaderData = _readVoxelShaderData = NULL; } else { @@ -368,7 +369,7 @@ void VoxelSystem::cleanupVoxelMemory() { glDeleteBuffers(1, &_vboIndicesRight); glDeleteBuffers(1, &_vboIndicesFront); glDeleteBuffers(1, &_vboIndicesBack); - + delete[] _readVerticesArray; delete[] _writeVerticesArray; delete[] _readColorsArray; @@ -420,12 +421,12 @@ void VoxelSystem::initVoxelMemory() { _memoryUsageRAM = 0; _memoryUsageVBO = 0; // our VBO allocations as we know them - + // if _voxelsAsPoints then we must have _useVoxelShader if (_voxelsAsPoints && !_useVoxelShader) { _useVoxelShader = true; } - + if (_useVoxelShader) { GLuint* indicesArray = new GLuint[_maxVoxels]; @@ -445,7 +446,7 @@ void VoxelSystem::initVoxelMemory() { glBindBuffer(GL_ARRAY_BUFFER, _vboVoxelsID); glBufferData(GL_ARRAY_BUFFER, _maxVoxels * sizeof(VoxelShaderVBOData), NULL, GL_DYNAMIC_DRAW); _memoryUsageVBO += _maxVoxels * sizeof(VoxelShaderVBOData); - + // delete the indices and normals arrays that are no longer needed delete[] indicesArray; @@ -474,7 +475,7 @@ void VoxelSystem::initVoxelMemory() { setupFaceIndices(_vboIndicesRight, identityIndicesRight); setupFaceIndices(_vboIndicesFront, identityIndicesFront); setupFaceIndices(_vboIndicesBack, identityIndicesBack); - + // Depending on if we're using per vertex normals, we will need more or less vertex points per voxel int vertexPointsPerVoxel = GLOBAL_NORMALS_VERTEX_POINTS_PER_VOXEL; glGenBuffers(1, &_vboVerticesID); @@ -519,10 +520,10 @@ void VoxelSystem::initVoxelMemory() { _perlinModulateProgram.bind(); _perlinModulateProgram.setUniformValue("permutationNormalTexture", 0); _perlinModulateProgram.release(); - + _shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/shadow_map.frag"); _shadowMapProgram.link(); - + _shadowMapProgram.bind(); _shadowMapProgram.setUniformValue("shadowMap", 0); _shadowMapProgram.release(); @@ -571,26 +572,26 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { switch(command) { case PACKET_TYPE_VOXEL_DATA: { PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData() PACKET_TYPE_VOXEL_DATA part...",showTimingDetails); - + unsigned char* dataAt = sourceBuffer + numBytesPacketHeader; VOXEL_PACKET_FLAGS flags = (*(VOXEL_PACKET_FLAGS*)(dataAt)); dataAt += sizeof(VOXEL_PACKET_FLAGS); VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(VOXEL_PACKET_SEQUENCE); - + VOXEL_PACKET_SENT_TIME sentAt = (*(VOXEL_PACKET_SENT_TIME*)dataAt); dataAt += sizeof(VOXEL_PACKET_SENT_TIME); - + bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT); bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT); - + VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int flightTime = arrivedAt - sentAt; - + VOXEL_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0; int dataBytes = numBytes - VOXEL_PACKET_HEADER_SIZE; - + int subsection = 1; while (dataBytes > 0) { if (packetIsCompressed) { @@ -605,7 +606,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { } else { sectionLength = dataBytes; } - + if (sectionLength) { // ask the VoxelTree to read the bitstream into the tree ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceUUID()); @@ -616,12 +617,12 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { qDebug("VoxelSystem::parseData() ... Got Packet Section" " color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d" " subsection:%d sectionLength:%d uncompressed:%d\n", - debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), + debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), sequence, flightTime, numBytes, dataBytes, subsection, sectionLength, packetData.getUncompressedSize()); } _tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args); unlockTree(); - + dataBytes -= sectionLength; dataAt += sectionLength; } @@ -635,15 +636,13 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { } else { setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY); } - + Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::VOXELS).updateValue(numBytes); - + return numBytes; } void VoxelSystem::setupNewVoxelsForDrawing() { - - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "setupNewVoxelsForDrawing()"); @@ -653,7 +652,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { uint64_t start = usecTimestampNow(); uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; - + bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) { return; // bail early, it hasn't been long enough since the last time we ran @@ -665,7 +664,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { if (_tree->isDirty()) { static char buffer[64] = { 0 }; if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { - sprintf(buffer, "newTreeToArrays() _writeRenderFullVBO=%s", debug::valueOf(_writeRenderFullVBO)); + sprintf(buffer, "newTreeToArrays() _writeRenderFullVBO=%s", debug::valueOf(_writeRenderFullVBO)); }; PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer); _callsToTreesToArrays++; @@ -678,17 +677,17 @@ void VoxelSystem::setupNewVoxelsForDrawing() { if (_writeRenderFullVBO) { _abandonedVBOSlots = 0; // reset the count of our abandoned slots, why is this here and not earlier???? } - + // since we called treeToArrays, we can assume that our VBO is in sync, and so partial updates to the VBOs are - // ok again, until/unless we call removeOutOfView() - _writeRenderFullVBO = false; + // ok again, until/unless we call removeOutOfView() + _writeRenderFullVBO = false; } else { _voxelsUpdated = 0; } - + // lock on the buffer write lock so we can't modify the data when the GPU is reading it pthread_mutex_lock(&_bufferWriteLock); - + if (_voxelsUpdated) { _voxelsDirty=true; } @@ -703,6 +702,11 @@ void VoxelSystem::setupNewVoxelsForDrawing() { _setupNewVoxelsForDrawingLastFinished = end; _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; _inSetupNewVoxelsForDrawing = false; + + bool extraDebugging = Application::getInstance()->getLogger()->extraDebugging(); + if (extraDebugging) { + qDebug("setupNewVoxelsForDrawing()... _voxelsUpdated=%lu...\n",_voxelsUpdated); + } } void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { @@ -713,7 +717,7 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging - if (allowBailEarly && !iAmDebugging && + if (allowBailEarly && !iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) { return; // bail early, it hasn't been long enough since the last time we ran } @@ -745,19 +749,17 @@ void VoxelSystem::checkForCulling() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "checkForCulling()"); uint64_t start = usecTimestampNow(); - - + // track how long its been since we were last moving. If we have recently moved then only use delta frustums, if // it's been a long time since we last moved, then go ahead and do a full frustum cull. if (isViewChanging()) { _lastViewIsChanging = start; } uint64_t sinceLastMoving = (start - _lastViewIsChanging) / 1000; - bool enoughTime = (sinceLastMoving >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)); // These has changed events will occur before we stop. So we need to remember this for when we finally have stopped - // moving long enough to be enoughTime + // moving long enough to be enoughTime if (hasViewChanged()) { _hasRecentlyChanged = true; } @@ -767,25 +769,25 @@ void VoxelSystem::checkForCulling() { bool forceFullFrustum = enoughTime && _hasRecentlyChanged; // in hide mode, we only track the full frustum culls, because we don't care about the partials. - if (forceFullFrustum) { + if (forceFullFrustum) { _lastViewCulling = start; _hasRecentlyChanged = false; } - + hideOutOfView(forceFullFrustum); - if (forceFullFrustum) { + if (forceFullFrustum) { uint64_t endViewCulling = usecTimestampNow(); _lastViewCullingElapsed = (endViewCulling - start) / 1000; } - + // Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So, // we should consider putting this someplace else... as this might be able to occur less frequently, and save us on // VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary. cleanupRemovedVoxels(); uint64_t sinceLastAudit = (start - _lastAudit) / 1000; - + if (Menu::getInstance()->isOptionChecked(MenuOption::AutomaticallyAuditTree)) { if (sinceLastAudit >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)) { _lastAudit = start; @@ -812,7 +814,7 @@ void VoxelSystem::cleanupRemovedVoxels() { const float TOO_MANY_ABANDONED_RATIO = 0.5f; if (!_writeRenderFullVBO && (_abandonedVBOSlots > (_voxelsInWriteArrays * TOO_MANY_ABANDONED_RATIO))) { if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { - qDebug() << "cleanupRemovedVoxels().. _abandonedVBOSlots [" + qDebug() << "cleanupRemovedVoxels().. _abandonedVBOSlots [" << _abandonedVBOSlots << "] > TOO_MANY_ABANDONED_RATIO \n"; } _writeRenderFullVBO = true; @@ -822,10 +824,10 @@ void VoxelSystem::cleanupRemovedVoxels() { void VoxelSystem::copyWrittenDataToReadArraysFullVBOs() { copyWrittenDataSegmentToReadArrays(0, _voxelsInWriteArrays - 1); _voxelsInReadArrays = _voxelsInWriteArrays; - + // clear our dirty flags memset(_writeVoxelDirtyArray, false, _voxelsInWriteArrays * sizeof(bool)); - + // let the reader know to get the full array _readRenderFullVBO = true; } @@ -851,7 +853,7 @@ void VoxelSystem::copyWrittenDataToReadArraysPartialVBOs() { } } } - + // if we got to the end of the array, and we're in an active dirty segment... if (inSegment) { copyWrittenDataSegmentToReadArrays(segmentStart, _voxelsInWriteArrays - 1); @@ -910,9 +912,9 @@ int VoxelSystem::newTreeToArrays(VoxelTreeElement* voxel) { voxel->setShouldRender(shouldRender); // let children figure out their renderness if (!voxel->isLeaf()) { - + // As we check our children, see if any of them went from shouldRender to NOT shouldRender - // then we probably dropped LOD and if we don't have color, we want to average our children + // then we probably dropped LOD and if we don't have color, we want to average our children // for a new color. int childrenGotHiddenCount = 0; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -930,17 +932,23 @@ int VoxelSystem::newTreeToArrays(VoxelTreeElement* voxel) { voxel->calculateAverageFromChildren(); } } - if (_writeRenderFullVBO) { - const bool DONT_REUSE_INDEX = false; - const bool FORCE_REDRAW = true; - voxelsUpdated += updateNodeInArrays(voxel, DONT_REUSE_INDEX, FORCE_REDRAW); - } else { - const bool REUSE_INDEX = true; - const bool DONT_FORCE_REDRAW = false; - voxelsUpdated += updateNodeInArrays(voxel, REUSE_INDEX, DONT_FORCE_REDRAW); + + // for either voxels that should not render, or those that should render and are in view + // update their geometry in the array.if the voxel "should render" but is not in view, then + // it actually doesn't need to be rendered + if (!shouldRender || voxel->isInView(*_viewFrustum)) { + if (_writeRenderFullVBO) { + const bool DONT_REUSE_INDEX = false; + const bool FORCE_REDRAW = true; + voxelsUpdated += updateNodeInArrays(voxel, DONT_REUSE_INDEX, FORCE_REDRAW); + } else { + const bool REUSE_INDEX = true; + const bool DONT_FORCE_REDRAW = false; + voxelsUpdated += updateNodeInArrays(voxel, REUSE_INDEX, DONT_FORCE_REDRAW); + } } voxel->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things. - + return voxelsUpdated; } @@ -959,7 +967,7 @@ int VoxelSystem::forceRemoveNodeFromArrays(VoxelTreeElement* node) { // If this node has not yet been written to the array, then add it to the end of the array. glBufferIndex nodeIndex = node->getBufferIndex(); node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN); - freeBufferIndex(nodeIndex); // NOTE: This is make the node invisible! + freeBufferIndex(nodeIndex); // NOTE: This will make the node invisible! return 1; // updated! } return 0; // not-updated @@ -982,11 +990,11 @@ int VoxelSystem::updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, boo if (!_initialized) { return 0; } - + // If we've changed any attributes (our renderness, our color, etc), or we've been told to force a redraw // then update the Arrays... if (forceDraw || node->isDirty()) { - // If we're should render, use our legit location and scale, + // If we're should render, use our legit location and scale, if (node->getShouldRender()) { glm::vec3 startVertex = node->getCorner(); float voxelScale = node->getScale(); @@ -1016,9 +1024,9 @@ int VoxelSystem::updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, boo void VoxelSystem::updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex, float voxelScale, const nodeColor& color) { - if (_initialized) { + if (_initialized && nodeIndex <= _maxVoxels) { _writeVoxelDirtyArray[nodeIndex] = true; - + if (_useVoxelShader) { if (_writeVoxelShaderData) { VoxelShaderVBOData* writeVerticesAt = &_writeVoxelShaderData[nodeIndex]; @@ -1071,15 +1079,17 @@ void VoxelSystem::init() { // VBO for the verticesArray _initialMemoryUsageGPU = getFreeMemoryGPU(); initVoxelMemory(); - + // our own _removedVoxels doesn't need to be notified of voxel deletes VoxelTreeElement::removeDeleteHook(&_removedVoxels); + } void VoxelSystem::changeTree(VoxelTree* newTree) { disconnect(_tree, 0, this, 0); _tree = newTree; + _tree->setDirtyBit(); _tree->getRoot()->setVoxelSystem(this); @@ -1102,7 +1112,7 @@ void VoxelSystem::updateFullVBOs() { PerformanceWarning warn(outputWarning,buffer); updateVBOSegment(0, _voxelsInReadArrays); } - + { PerformanceWarning warn(outputWarning,"updateFullVBOs() : memset(_readVoxelDirtyArray...)"); // consider the _readVoxelDirtyArray[] clean! @@ -1131,7 +1141,7 @@ void VoxelSystem::updatePartialVBOs() { _readVoxelDirtyArray[i] = false; // consider us clean! } } - + // if we got to the end of the array, and we're in an active dirty segment... if (inSegment) { updateVBOSegment(segmentStart, _voxelsInReadArrays - 1); @@ -1195,7 +1205,7 @@ void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex seg PerformanceWarning warn(showWarning, "updateVBOSegment() : glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);"); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); } - + { PerformanceWarning warn(showWarning, "updateVBOSegment() : glBufferSubData() _vboColorsID);"); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); @@ -1206,14 +1216,14 @@ void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex seg void VoxelSystem::render(bool texture) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "render()"); - + // If we got here and we're not initialized then bail! if (!_initialized) { return; } - + updateVBOs(); - + bool dontCallOpenGLDraw = Menu::getInstance()->isOptionChecked(MenuOption::DontCallOpenGLForVoxels); // if not don't... then do... if (_useVoxelShader) { @@ -1253,8 +1263,8 @@ void VoxelSystem::render(bool texture) { glEnableVertexAttribArray(attributeLocation); glVertexAttribPointer(attributeLocation, 1, GL_FLOAT, false, sizeof(VoxelShaderVBOData), BUFFER_OFFSET(3*sizeof(float))); } - - + + glEnableClientState(GL_COLOR_ARRAY); glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelShaderVBOData), BUFFER_OFFSET(4*sizeof(float)));//The starting point of colors @@ -1285,7 +1295,7 @@ void VoxelSystem::render(bool texture) { { PerformanceWarning warn(showWarnings,"render().. setup before glDrawRangeElementsEXT()..."); - + // tell OpenGL where to find vertex and color information glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); @@ -1306,7 +1316,7 @@ void VoxelSystem::render(bool texture) { if (!dontCallOpenGLDraw) { PerformanceWarning warn(showWarnings, "render().. glDrawRangeElementsEXT()..."); - + glNormal3f(0,1.0f,0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesTop); glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, @@ -1340,7 +1350,7 @@ void VoxelSystem::render(bool texture) { { PerformanceWarning warn(showWarnings, "render().. cleanup after glDrawRangeElementsEXT()..."); - + glDisable(GL_CULL_FACE); removeScaleAndReleaseProgram(texture); @@ -1365,16 +1375,16 @@ void VoxelSystem::applyScaleAndBindProgram(bool texture) { glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glEnable(GL_TEXTURE_2D); - + glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]); glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]); glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[2]); - + } else if (texture) { _perlinModulateProgram.bind(); glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPermutationNormalTextureID()); } - + glPushMatrix(); glScalef(_treeScale, _treeScale, _treeScale); } @@ -1382,7 +1392,7 @@ void VoxelSystem::applyScaleAndBindProgram(bool texture) { void VoxelSystem::removeScaleAndReleaseProgram(bool texture) { // scale back down to 1 so heads aren't massive glPopMatrix(); - + if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { _shadowMapProgram.release(); glBindTexture(GL_TEXTURE_2D, 0); @@ -1390,7 +1400,7 @@ void VoxelSystem::removeScaleAndReleaseProgram(bool texture) { glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_R); glDisable(GL_TEXTURE_2D); - + } else if (texture) { _perlinModulateProgram.release(); glBindTexture(GL_TEXTURE_2D, 0); @@ -1403,7 +1413,7 @@ void VoxelSystem::killLocalVoxels() { lockTree(); _tree->eraseAllOctreeElements(); unlockTree(); - clearFreeBufferIndexes(); + clearFreeBufferIndexes(); _voxelsInReadArrays = 0; // do we need to do this? setupNewVoxelsForDrawing(); } @@ -1422,7 +1432,7 @@ bool VoxelSystem::clearAllNodesBufferIndexOperation(OctreeElement* element, void void VoxelSystem::clearAllNodesBufferIndex() { _nodeCount = 0; - lockTree(); + lockTree(); _tree->recurseTreeWithOperation(clearAllNodesBufferIndexOperation); unlockTree(); if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { @@ -1584,9 +1594,9 @@ void VoxelSystem::falseColorizeBySource() { const int NUMBER_OF_COLOR_GROUPS = 6; const unsigned char MIN_COLOR = 128; int voxelServerCount = 0; - groupColor groupColors[NUMBER_OF_COLOR_GROUPS] = { - groupColor(255, 0, 0), - groupColor( 0, 255, 0), + groupColor groupColors[NUMBER_OF_COLOR_GROUPS] = { + groupColor(255, 0, 0), + groupColor( 0, 255, 0), groupColor( 0, 0, 255), groupColor(255, 0, 255), groupColor( 0, 255, 255), @@ -1614,7 +1624,7 @@ void VoxelSystem::falseColorizeBySource() { voxelServerCount++; } } - + _tree->recurseTreeWithOperation(falseColorizeBySourceOperation, &args); qDebug("setting false color by source for %d nodes\n", _nodeCount); _tree->setDirtyBit(); @@ -1690,7 +1700,7 @@ public: unsigned long nodesOutside; VoxelTreeElement* insideRoot; VoxelTreeElement* outsideRoot; - + removeOutOfViewArgs(VoxelSystem* voxelSystem, bool widenViewFrustum = true) : thisVoxelSystem(voxelSystem), thisViewFrustum(*voxelSystem->getViewFrustum()), @@ -1730,7 +1740,7 @@ bool VoxelSystem::removeOutOfViewOperation(OctreeElement* element, void* extraDa args->dontRecurseBag.remove(voxel); return false; // stop recursion } - + VoxelSystem* thisVoxelSystem = args->thisVoxelSystem; args->nodesScanned++; // Need to operate on our child nodes, so we can remove them @@ -1748,7 +1758,7 @@ bool VoxelSystem::removeOutOfViewOperation(OctreeElement* element, void* extraDa } break; case ViewFrustum::INSIDE: { // if the child node is fully INSIDE the view, then there's no need to recurse it - // because we know all it's children will also be in the view, so we want to + // because we know all it's children will also be in the view, so we want to // tell the caller to NOT recurse this child args->nodesInside++; args->dontRecurseBag.insert(childNode); @@ -1779,12 +1789,12 @@ bool VoxelSystem::isViewChanging() { bool VoxelSystem::hasViewChanged() { bool result = false; // assume the best - + // If we're still changing, report no change yet. if (isViewChanging()) { return false; } - + // If our viewFrustum has changed since our _lastKnownViewFrustum if (!_lastStableViewFrustum.isVerySimilar(_viewFrustum)) { result = true; @@ -1803,8 +1813,8 @@ void VoxelSystem::removeOutOfView() { } bool showRemoveDebugDetails = false; if (showRemoveDebugDetails) { - qDebug("removeOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld _removedVoxels.count()=%d \n", - args.nodesScanned, args.nodesRemoved, args.nodesInside, + qDebug("removeOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld _removedVoxels.count()=%d \n", + args.nodesScanned, args.nodesRemoved, args.nodesInside, args.nodesIntersect, args.nodesOutside, _removedVoxels.count() ); } @@ -1816,9 +1826,9 @@ public: VoxelSystem* thisVoxelSystem; ViewFrustum thisViewFrustum; unsigned long nodesScanned; - + showAllLocalVoxelsArgs(VoxelSystem* voxelSystem) : - thisVoxelSystem(voxelSystem), + thisVoxelSystem(voxelSystem), thisViewFrustum(*voxelSystem->getViewFrustum()), nodesScanned(0) { @@ -1880,8 +1890,9 @@ public: unsigned long nodesOutsideInside; unsigned long nodesInsideOutside; unsigned long nodesOutsideOutside; - - hideOutOfViewArgs(VoxelSystem* voxelSystem, VoxelTree* tree, + unsigned long nodesShown; + + hideOutOfViewArgs(VoxelSystem* voxelSystem, VoxelTree* tree, bool culledOnce, bool widenViewFrustum, bool wantDeltaFrustums) : thisVoxelSystem(voxelSystem), tree(tree), @@ -1898,7 +1909,8 @@ public: nodesIntersectInside(0), nodesOutsideInside(0), nodesInsideOutside(0), - nodesOutsideOutside(0) + nodesOutsideOutside(0), + nodesShown(0) { // Widen the FOV for trimming if (widenViewFrustum) { @@ -1916,13 +1928,14 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) { if (_inhideOutOfView) { return; } - + _inhideOutOfView = true; bool showDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showDebugDetails, "hideOutOfView()"); bool widenFrustum = true; + // When using "delta" view frustums and only hide/show items that are in the difference // between the two view frustums. There are some potential problems with this mode. // @@ -1932,7 +1945,7 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) { // // 2) Also, voxels will arrive from the network that are OUTSIDE of the view // frustum... these won't get hidden... and so we can't assume they are correctly - // hidden... + // hidden... // // Both these problems are solved by intermittently calling this with forceFullFrustum set // to true. This will essentially clean up the improperly hidden or shown voxels. @@ -1947,13 +1960,13 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) { args.lastViewFrustum.printDebugDetails(); } } - + if (!forceFullFrustum && _culledOnce && args.lastViewFrustum.isVerySimilar(args.thisViewFrustum)) { _inhideOutOfView = false; return; } - lockTree(); + lockTree(); _tree->recurseTreeWithOperation(hideOutOfViewOperation,(void*)&args); unlockTree(); _lastCulledViewFrustum = args.thisViewFrustum; // save last stable @@ -1963,16 +1976,19 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) { _tree->setDirtyBit(); setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY); } - - bool extraDebugDetails = Application::getInstance()->getLogger()->extraDebugging(); + + bool extraDebugDetails = false; // Application::getInstance()->getLogger()->extraDebugging(); if (extraDebugDetails) { - qDebug("hideOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld\n", - args.nodesScanned, args.nodesRemoved, args.nodesInside, + qDebug("hideOutOfView() scanned=%ld removed=%ld show=%ld inside=%ld intersect=%ld outside=%ld\n", + args.nodesScanned, args.nodesRemoved, args.nodesShown, args.nodesInside, args.nodesIntersect, args.nodesOutside ); - qDebug(" inside/inside=%ld intersect/inside=%ld outside/outside=%ld\n", + qDebug(" inside/inside=%ld intersect/inside=%ld outside/outside=%ld\n", args.nodesInsideInside, args.nodesIntersectInside, args.nodesOutsideOutside ); + + qDebug() << "args.thisViewFrustum....\n"; + args.thisViewFrustum.printDebugDetails(); } _inhideOutOfView = false; } @@ -1985,10 +2001,10 @@ bool VoxelSystem::hideAllSubTreeOperation(OctreeElement* element, void* extraDat // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not // consider that case. ViewFrustum::location inLastCulledFrustum; - + if (args->culledOnce && args->wantDeltaFrustums) { inLastCulledFrustum = voxel->inFrustum(args->lastViewFrustum); - + // if this node is fully OUTSIDE our last culled view frustum, then we don't need to recurse further if (inLastCulledFrustum == ViewFrustum::OUTSIDE) { args->nodesOutsideOutside++; @@ -2009,7 +2025,7 @@ bool VoxelSystem::hideAllSubTreeOperation(OctreeElement* element, void* extraDat } } - + return true; } @@ -2021,10 +2037,10 @@ bool VoxelSystem::showAllSubTreeOperation(OctreeElement* element, void* extraDat // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not // consider that case. ViewFrustum::location inLastCulledFrustum; - + if (args->culledOnce && args->wantDeltaFrustums) { inLastCulledFrustum = voxel->inFrustum(args->lastViewFrustum); - + // if this node is fully inside our last culled view frustum, then we don't need to recurse further if (inLastCulledFrustum == ViewFrustum::INSIDE) { args->nodesInsideInside++; @@ -2047,35 +2063,36 @@ bool VoxelSystem::showAllSubTreeOperation(OctreeElement* element, void* extraDat // These are both needed to force redraw... voxel->setDirtyBit(); voxel->markWithChangedTime(); + args->nodesShown++; } return true; // keep recursing! } -// "hide" voxels in the VBOs that are still in the tree that but not in view. +// "hide" voxels in the VBOs that are still in the tree that but not in view. // We don't remove them from the tree, we don't delete them, we do remove them // from the VBOs and mark them as such in the tree. bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; hideOutOfViewArgs* args = (hideOutOfViewArgs*)extraData; - // If we're still recursing the tree using this operator, then we don't know if we're inside or outside... + // If we're still recursing the tree using this operator, then we don't know if we're inside or outside... // so before we move forward we need to determine our frustum location ViewFrustum::location inFrustum = voxel->inFrustum(args->thisViewFrustum); - + // If we've culled at least once, then we will use the status of this voxel in the last culled frustum to determine // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not // consider that case. ViewFrustum::location inLastCulledFrustum; - + if (args->culledOnce && args->wantDeltaFrustums) { inLastCulledFrustum = voxel->inFrustum(args->lastViewFrustum); } - + // ok, now do some processing for this node... switch (inFrustum) { case ViewFrustum::OUTSIDE: { - + // If this node is outside the current view, then we might want to hide it... unless it was previously OUTSIDE, // if it was previously outside, then we can safely assume it's already hidden, and we can also safely assume // that all of it's children are outside both of our views, in which case we can just stop recursing... @@ -2084,17 +2101,17 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData args->nodesOutsideOutside++; return false; // stop recursing this branch! } - + // if this node is fully OUTSIDE the view, but previously intersected and/or was inside the last view, then - // we need to hide it. Additionally we know that ALL of it's children are also fully OUTSIDE so we can recurse + // we need to hide it. Additionally we know that ALL of it's children are also fully OUTSIDE so we can recurse // the children and simply mark them as hidden args->tree->recurseNodeWithOperation(voxel, hideAllSubTreeOperation, args ); - + return false; - + } break; case ViewFrustum::INSIDE: { - + // If this node is INSIDE the current view, then we might want to show it... unless it was previously INSIDE, // if it was previously INSIDE, then we can safely assume it's already shown, and we can also safely assume // that all of it's children are INSIDE both of our views, in which case we can just stop recursing... @@ -2103,18 +2120,18 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData args->nodesInsideInside++; return false; // stop recursing this branch! } - + // if this node is fully INSIDE the view, but previously INTERSECTED and/or was OUTSIDE the last view, then - // we need to show it. Additionally we know that ALL of it's children are also fully INSIDE so we can recurse + // we need to show it. Additionally we know that ALL of it's children are also fully INSIDE so we can recurse // the children and simply mark them as visible (as appropriate based on LOD) args->tree->recurseNodeWithOperation(voxel, showAllSubTreeOperation, args); - return false; + return false; } break; case ViewFrustum::INTERSECT: { args->nodesScanned++; - // If this node INTERSECTS the current view, then we might want to show it... unless it was previously INSIDE + // If this node INTERSECTS the current view, then we might want to show it... unless it was previously INSIDE // the last known view, in which case it will already be visible, and we know that all it's children are also // previously INSIDE and visible. So in this case stop recursing if (args->culledOnce && args->wantDeltaFrustums && inLastCulledFrustum == ViewFrustum::INSIDE) { @@ -2125,21 +2142,22 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData args->nodesIntersect++; // if the child node INTERSECTs the view, then we want to check to see if it thinks it should render - // if it should render but is missing it's VBO index, then we want to flip it on, and we can stop recursing from + // if it should render but is missing it's VBO index, then we want to flip it on, and we can stop recursing from // here because we know will block any children anyway if (voxel->getShouldRender() && !voxel->isKnownBufferIndex()) { voxel->setDirtyBit(); // will this make it draw? + args->nodesShown++; return false; } // If it INTERSECTS but shouldn't be displayed, then it's probably a parent and it is at least partially in view. - // So we DO want to recurse the children because some of them may not be in view... nothing specifically to do, + // So we DO want to recurse the children because some of them may not be in view... nothing specifically to do, // just keep iterating the children - return true; + return true; } break; } // switch - + return true; // keep going! } @@ -2147,7 +2165,7 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData bool VoxelSystem::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, VoxelDetail& detail, float& distance, BoxFace& face) { - lockTree(); + lockTree(); OctreeElement* element; if (!_tree->findRayIntersection(origin, direction, element, distance, face)) { unlockTree(); @@ -2206,7 +2224,7 @@ bool VoxelSystem::falseColorizeRandomEveryOtherOperation(OctreeElement* element, void VoxelSystem::falseColorizeRandomEveryOther() { falseColorizeRandomEveryOtherArgs args; _tree->recurseTreeWithOperation(falseColorizeRandomEveryOtherOperation,&args); - qDebug("randomized false color for every other node: total %ld, colorable %ld, colored %ld\n", + qDebug("randomized false color for every other node: total %ld, colorable %ld, colored %ld\n", args.totalNodes, args.colorableNodes, args.coloredNodes); _tree->setDirtyBit(); setupNewVoxelsForDrawing(); @@ -2214,9 +2232,9 @@ void VoxelSystem::falseColorizeRandomEveryOther() { class collectStatsForTreesAndVBOsArgs { public: - collectStatsForTreesAndVBOsArgs(int maxVoxels) : - totalNodes(0), - dirtyNodes(0), + collectStatsForTreesAndVBOsArgs(int maxVoxels) : + totalNodes(0), + dirtyNodes(0), shouldRenderNodes(0), coloredNodes(0), nodesInVBO(0), @@ -2228,7 +2246,7 @@ public: hasIndexFound = new bool[maxVoxels]; memset(hasIndexFound, false, maxVoxels * sizeof(bool)); }; - + ~collectStatsForTreesAndVBOsArgs() { delete[] hasIndexFound; } @@ -2244,13 +2262,13 @@ public: unsigned long leafNodes; unsigned long expectedMax; - + bool* hasIndexFound; }; bool VoxelSystem::collectStatsForTreesAndVBOsOperation(OctreeElement* element, void* extraData) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; - + collectStatsForTreesAndVBOsArgs* args = (collectStatsForTreesAndVBOsArgs*)extraData; args->totalNodes++; @@ -2273,17 +2291,17 @@ bool VoxelSystem::collectStatsForTreesAndVBOsOperation(OctreeElement* element, v if (voxel->isKnownBufferIndex()) { args->nodesInVBO++; unsigned long nodeIndex = voxel->getBufferIndex(); - + const bool extraDebugging = false; // enable for extra debugging if (extraDebugging) { - qDebug("node In VBO... [%f,%f,%f] %f ... index=%ld, isDirty=%s, shouldRender=%s \n", - voxel->getCorner().x, voxel->getCorner().y, voxel->getCorner().z, voxel->getScale(), + qDebug("node In VBO... [%f,%f,%f] %f ... index=%ld, isDirty=%s, shouldRender=%s \n", + voxel->getCorner().x, voxel->getCorner().y, voxel->getCorner().z, voxel->getScale(), nodeIndex, debug::valueOf(voxel->isDirty()), debug::valueOf(voxel->getShouldRender())); } if (args->hasIndexFound[nodeIndex]) { args->duplicateVBOIndex++; - qDebug("duplicateVBO found... index=%ld, isDirty=%s, shouldRender=%s \n", nodeIndex, + qDebug("duplicateVBO found... index=%ld, isDirty=%s, shouldRender=%s \n", nodeIndex, debug::valueOf(voxel->isDirty()), debug::valueOf(voxel->getShouldRender())); } else { args->hasIndexFound[nodeIndex] = true; @@ -2291,7 +2309,7 @@ bool VoxelSystem::collectStatsForTreesAndVBOsOperation(OctreeElement* element, v if (nodeIndex > args->expectedMax) { args->nodesInVBOOverExpectedMax++; } - + // if it's in VBO but not-shouldRender, track that also... if (!voxel->getShouldRender()) { args->nodesInVBONotShouldRender++; @@ -2316,9 +2334,9 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { collectStatsForTreesAndVBOsArgs args(_maxVoxels); args.expectedMax = _voxelsInWriteArrays; - + qDebug("CALCULATING Local Voxel Tree Statistics >>>>>>>>>>>>\n"); - + _tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args); qDebug("Local Voxel Tree Statistics:\n total nodes %ld \n leaves %ld \n dirty %ld \n colored %ld \n shouldRender %ld \n", @@ -2327,7 +2345,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { qDebug(" _voxelsDirty=%s \n _voxelsInWriteArrays=%ld \n minDirty=%ld \n maxDirty=%ld \n", debug::valueOf(_voxelsDirty), _voxelsInWriteArrays, minDirty, maxDirty); - qDebug(" inVBO %ld \n nodesInVBOOverExpectedMax %ld \n duplicateVBOIndex %ld \n nodesInVBONotShouldRender %ld \n", + qDebug(" inVBO %ld \n nodesInVBOOverExpectedMax %ld \n duplicateVBOIndex %ld \n nodesInVBONotShouldRender %ld \n", args.nodesInVBO, args.nodesInVBOOverExpectedMax, args.duplicateVBOIndex, args.nodesInVBONotShouldRender); glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN; @@ -2340,10 +2358,10 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { } } - qDebug(" minInVBO=%ld \n maxInVBO=%ld \n _voxelsInWriteArrays=%ld \n _voxelsInReadArrays=%ld \n", + qDebug(" minInVBO=%ld \n maxInVBO=%ld \n _voxelsInWriteArrays=%ld \n _voxelsInReadArrays=%ld \n", minInVBO, maxInVBO, _voxelsInWriteArrays, _voxelsInReadArrays); - qDebug(" _freeIndexes.size()=%ld \n", + qDebug(" _freeIndexes.size()=%ld \n", _freeIndexes.size()); qDebug("DONE WITH Local Voxel Tree Statistics >>>>>>>>>>>>\n"); @@ -2354,36 +2372,36 @@ void VoxelSystem::deleteVoxelAt(float x, float y, float z, float s) { lockTree(); _tree->deleteVoxelAt(x, y, z, s); unlockTree(); - + // redraw! setupNewVoxelsForDrawing(); // do we even need to do this? Or will the next network receive kick in? - + }; -VoxelTreeElement* VoxelSystem::getVoxelAt(float x, float y, float z, float s) const { - return _tree->getVoxelAt(x, y, z, s); +VoxelTreeElement* VoxelSystem::getVoxelAt(float x, float y, float z, float s) const { + return _tree->getVoxelAt(x, y, z, s); }; -void VoxelSystem::createVoxel(float x, float y, float z, float s, +void VoxelSystem::createVoxel(float x, float y, float z, float s, unsigned char red, unsigned char green, unsigned char blue, bool destructive) { - + //qDebug("VoxelSystem::createVoxel(%f,%f,%f,%f)\n",x,y,z,s); lockTree(); - _tree->createVoxel(x, y, z, s, red, green, blue, destructive); + _tree->createVoxel(x, y, z, s, red, green, blue, destructive); unlockTree(); - setupNewVoxelsForDrawing(); + setupNewVoxelsForDrawing(); }; -void VoxelSystem::createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color, bool destructive) { - _tree->createLine(point1, point2, unitSize, color, destructive); - setupNewVoxelsForDrawing(); +void VoxelSystem::createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color, bool destructive) { + _tree->createLine(point1, point2, unitSize, color, destructive); + setupNewVoxelsForDrawing(); }; -void VoxelSystem::createSphere(float r,float xc, float yc, float zc, float s, bool solid, - creationMode mode, bool destructive, bool debug) { - _tree->createSphere(r, xc, yc, zc, s, solid, mode, destructive, debug); - setupNewVoxelsForDrawing(); +void VoxelSystem::createSphere(float r,float xc, float yc, float zc, float s, bool solid, + creationMode mode, bool destructive, bool debug) { + _tree->createSphere(r, xc, yc, zc, s, solid, mode, destructive, debug); + setupNewVoxelsForDrawing(); }; void VoxelSystem::copySubTreeIntoNewTree(VoxelTreeElement* startNode, VoxelSystem* destination, bool rebaseToRoot) { @@ -2431,7 +2449,7 @@ bool VoxelSystem::falseColorizeSubTreeOperation(OctreeElement* element, void* ex voxel->setFalseColor(args->color[0], args->color[1], args->color[2]); args->voxelsTouched++; } - return true; + return true; } bool VoxelSystem::falseColorizeOccludedOperation(OctreeElement* element, void* extraData) { @@ -2459,18 +2477,18 @@ bool VoxelSystem::falseColorizeOccludedOperation(OctreeElement* element, void* e if (result == OCCLUDED) { args->nonLeavesOccluded++; delete voxelPolygon; - + FalseColorizeSubTreeOperationArgs subArgs; subArgs.color[0] = 0; subArgs.color[1] = 255; subArgs.color[2] = 0; subArgs.voxelsTouched = 0; - + args->tree->recurseNodeWithOperation(voxel, falseColorizeSubTreeOperation, &subArgs ); - + args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); args->totalVoxels += (subArgs.voxelsTouched - 1); - + return false; } @@ -2509,10 +2527,10 @@ bool VoxelSystem::falseColorizeOccludedOperation(OctreeElement* element, void* e void VoxelSystem::falseColorizeOccluded() { PerformanceWarning warn(true, "falseColorizeOccluded()",true); myCoverageMap.erase(); - + FalseColorizeOccludedArgs args; args.viewFrustum = _viewFrustum; - args.map = &myCoverageMap; + args.map = &myCoverageMap; args.totalVoxels = 0; args.coloredVoxels = 0; args.occludedVoxels = 0; @@ -2527,15 +2545,15 @@ void VoxelSystem::falseColorizeOccluded() { OctreeProjectedPolygon::pointInside_calls = 0; OctreeProjectedPolygon::occludes_calls = 0; OctreeProjectedPolygon::intersects_calls = 0; - + glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE); _tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args); - qDebug("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n", + qDebug("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n", position.x, position.y, - args.totalVoxels, args.coloredVoxels, args.occludedVoxels, - args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, + args.totalVoxels, args.coloredVoxels, args.occludedVoxels, + args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded, OctreeProjectedPolygon::pointInside_calls, OctreeProjectedPolygon::occludes_calls, @@ -2574,18 +2592,18 @@ bool VoxelSystem::falseColorizeOccludedV2Operation(OctreeElement* element, void* if (result == V2_OCCLUDED) { args->nonLeavesOccluded++; delete voxelPolygon; - + FalseColorizeSubTreeOperationArgs subArgs; subArgs.color[0] = 0; subArgs.color[1] = 255; subArgs.color[2] = 0; subArgs.voxelsTouched = 0; - + args->tree->recurseNodeWithOperation(voxel, falseColorizeSubTreeOperation, &subArgs ); - + args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); args->totalVoxels += (subArgs.voxelsTouched - 1); - + return false; } @@ -2632,10 +2650,10 @@ void VoxelSystem::falseColorizeOccludedV2() { OctreeProjectedPolygon::pointInside_calls = 0; OctreeProjectedPolygon::occludes_calls = 0; OctreeProjectedPolygon::intersects_calls = 0; - + FalseColorizeOccludedArgs args; args.viewFrustum = _viewFrustum; - args.mapV2 = &myCoverageMapV2; + args.mapV2 = &myCoverageMapV2; args.totalVoxels = 0; args.coloredVoxels = 0; args.occludedVoxels = 0; @@ -2646,15 +2664,15 @@ void VoxelSystem::falseColorizeOccludedV2() { args.nonLeavesOutOfView = 0; args.nonLeavesOccluded = 0; args.tree = _tree; - + glm::vec3 position = args.viewFrustum->getPosition() * (1.0f/TREE_SCALE); _tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedV2Operation, position, (void*)&args); - qDebug("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n", + qDebug("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n", position.x, position.y, - args.totalVoxels, args.coloredVoxels, args.occludedVoxels, - args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, + args.totalVoxels, args.coloredVoxels, args.occludedVoxels, + args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped, args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded, OctreeProjectedPolygon::pointInside_calls, OctreeProjectedPolygon::occludes_calls, @@ -2717,7 +2735,7 @@ unsigned long VoxelSystem::getFreeMemoryGPU() { const GLenum VBO_FREE_MEMORY_ATI = 0x87FB; glGetIntegerv(VBO_FREE_MEMORY_ATI, &results[0]); GLenum errorATI = glGetError(); - + if (errorATI == GL_NO_ERROR) { _hasMemoryUsageGPU = true; freeMemory = results[0]; @@ -2730,19 +2748,19 @@ unsigned long VoxelSystem::getFreeMemoryGPU() { //const GLenum GPU_MEMORY_INFO_EVICTION_COUNT_NVX = 0x904A; //const GLenum GPU_MEMORY_INFO_EVICTED_MEMORY_NVX = 0x904B; //const GLenum GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX = 0x9048; - + const GLenum GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX = 0x9049; results[0] = 0; glGetIntegerv(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &results[0]); - freeMemory += results[0]; + freeMemory += results[0]; GLenum errorNVIDIA = glGetError(); - + if (errorNVIDIA == GL_NO_ERROR) { _hasMemoryUsageGPU = true; freeMemory = results[0]; } } - + const unsigned long BYTES_PER_KBYTE = 1024; return freeMemory * BYTES_PER_KBYTE; // API results in KB, we want it in bytes } @@ -2763,4 +2781,22 @@ void VoxelSystem::unlockTree() { } +void VoxelSystem::localVoxelCacheLoaded() { + qDebug() << "localVoxelCacheLoaded()\n"; + + // Make sure that the application has properly set up the view frustum for our loaded state + Application::getInstance()->initAvatarAndViewFrustum(); + + _tree->setDirtyBit(); + setupNewVoxelsForDrawing(); + _inhideOutOfView = false; // reenable hideOutOfView behavior +} + +void VoxelSystem::beginLoadingLocalVoxelCache() { + qDebug() << "beginLoadingLocalVoxelCache()\n"; + _writeRenderFullVBO = true; // this will disable individual node updates + _inhideOutOfView = true; // this will disable hidOutOfView which we want to do until local cache is loaded +} + + diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 28e66d11dc..8173652e55 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "Camera.h" #include "Util.h" @@ -36,7 +37,7 @@ struct VoxelShaderVBOData }; -class VoxelSystem : public NodeData, public OctreeElementDeleteHook, public OctreeElementUpdateHook, +class VoxelSystem : public NodeData, public OctreeElementDeleteHook, public OctreeElementUpdateHook, public NodeListHook, public DomainChangeListener { Q_OBJECT @@ -48,9 +49,9 @@ public: void setDataSourceUUID(const QUuid& dataSourceUUID) { _dataSourceUUID = dataSourceUUID; } const QUuid& getDataSourceUUID() const { return _dataSourceUUID; } - + int parseData(unsigned char* sourceBuffer, int numBytes); - + virtual void init(); void simulate(float deltaTime) { } void render(bool texture); @@ -85,19 +86,19 @@ public: virtual void hideOutOfView(bool forceFullFrustum = false); bool hasViewChanged(); bool isViewChanging(); - + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, VoxelDetail& detail, float& distance, BoxFace& face); - + bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration); bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration); void deleteVoxelAt(float x, float y, float z, float s); VoxelTreeElement* getVoxelAt(float x, float y, float z, float s) const; - void createVoxel(float x, float y, float z, float s, + void createVoxel(float x, float y, float z, float s, unsigned char red, unsigned char green, unsigned char blue, bool destructive = false); void createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color, bool destructive = false); - void createSphere(float r,float xc, float yc, float zc, float s, bool solid, + void createSphere(float r,float xc, float yc, float zc, float s, bool solid, creationMode mode, bool destructive = false, bool debug = false); void copySubTreeIntoNewTree(VoxelTreeElement* startNode, VoxelSystem* destinationTree, bool rebaseToRoot); @@ -114,18 +115,18 @@ public: virtual void nodeAdded(Node* node); virtual void nodeKilled(Node* node); virtual void domainChanged(QString domain); - + bool treeIsBusy() const { return _treeIsBusy; } - + VoxelTreeElement* getVoxelEnclosing(const glm::vec3& point); - + signals: void importSize(float x, float y, float z); void importProgress(int progress); public slots: void collectStatsForTreesAndVBOs(); - + // Methods that recurse tree void showAllLocalVoxels(); void randomizeVoxelColors(); @@ -141,24 +142,27 @@ public slots: void clearAllNodesBufferIndex(); void cancelImport(); - + void setDisableFastVoxelPipeline(bool disableFastVoxelPipeline); void setUseVoxelShader(bool useVoxelShader); void setVoxelsAsPoints(bool voxelsAsPoints); - + + void localVoxelCacheLoaded(); + void beginLoadingLocalVoxelCache(); + protected: - float _treeScale; - int _maxVoxels; + float _treeScale; + int _maxVoxels; VoxelTree* _tree; void setupNewVoxelsForDrawing(); static const bool DONT_BAIL_EARLY; // by default we will bail early, if you want to force not bailing, then use this void setupNewVoxelsForDrawingSingleNode(bool allowBailEarly = true); void checkForCulling(); - + glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const; - + virtual void updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex, float voxelScale, const nodeColor& color); virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd); @@ -170,7 +174,7 @@ private: // disallow copying of VoxelSystem objects VoxelSystem(const VoxelSystem&); VoxelSystem& operator= (const VoxelSystem&); - + bool _initialized; int _callsToTreesToArrays; OctreeElementBag _removedVoxels; @@ -223,10 +227,10 @@ private: unsigned long _voxelsInReadArrays; unsigned long _voxelsInWriteArrays; unsigned long _abandonedVBOSlots; - + bool _writeRenderFullVBO; bool _readRenderFullVBO; - + int _setupNewVoxelsForDrawingLastElapsed; uint64_t _setupNewVoxelsForDrawingLastFinished; uint64_t _lastViewCulling; @@ -234,7 +238,7 @@ private: uint64_t _lastAudit; int _lastViewCullingElapsed; bool _hasRecentlyChanged; - + void initVoxelMemory(); void cleanupVoxelMemory(); @@ -246,7 +250,7 @@ private: GLuint _vboVoxelsIndicesID; /// when using voxel shader, we'll use this VBO for our indexes VoxelShaderVBOData* _writeVoxelShaderData; VoxelShaderVBOData* _readVoxelShaderData; - + GLuint _vboVerticesID; GLuint _vboColorsID; @@ -269,11 +273,11 @@ private: void setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndices[]); - int newTreeToArrays(VoxelTreeElement *currentNode); + int newTreeToArrays(VoxelTreeElement* currentNode); void cleanupRemovedVoxels(); void copyWrittenDataToReadArrays(bool fullVBOs); - + void updateFullVBOs(); // all voxels in the VBO void updatePartialVBOs(); // multiple segments, only dirty voxels @@ -281,7 +285,7 @@ private: static ProgramObject _perlinModulateProgram; static ProgramObject _shadowMapProgram; - + int _hookID; std::vector _freeIndexes; pthread_mutex_t _freeIndexLock; @@ -289,22 +293,22 @@ private: void freeBufferIndex(glBufferIndex index); void clearFreeBufferIndexes(); glBufferIndex getNextBufferIndex(); - + bool _falseColorizeBySource; QUuid _dataSourceUUID; - + int _voxelServerCount; unsigned long _memoryUsageRAM; unsigned long _memoryUsageVBO; unsigned long _initialMemoryUsageGPU; bool _hasMemoryUsageGPU; - + bool _inSetupNewVoxelsForDrawing; bool _useFastVoxelPipeline; - + bool _inhideOutOfView; bool _treeIsBusy; // is the tree mutex locked? if so, it's busy, and if you can avoid it, don't access the tree - + void lockTree(); void unlockTree(); }; diff --git a/libraries/octree-server/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp similarity index 94% rename from libraries/octree-server/src/OctreePersistThread.cpp rename to libraries/octree/src/OctreePersistThread.cpp index 07466f9c43..08a7cffbb8 100644 --- a/libraries/octree-server/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -13,7 +13,6 @@ #include #include "OctreePersistThread.h" -#include "OctreeServer.h" OctreePersistThread::OctreePersistThread(Octree* tree, const char* filename, int persistInterval) : _tree(tree), @@ -41,41 +40,45 @@ bool OctreePersistThread::process() { _loadCompleted = time(0); uint64_t loadDone = usecTimestampNow(); _loadTimeUSecs = loadDone - loadStarted; - + _tree->clearDirtyBit(); // the tree is clean since we just loaded it qDebug("DONE loading Octrees from file... fileRead=%s\n", debug::valueOf(persistantFileRead)); - + unsigned long nodeCount = OctreeElement::getNodeCount(); unsigned long internalNodeCount = OctreeElement::getInternalNodeCount(); unsigned long leafNodeCount = OctreeElement::getLeafNodeCount(); qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount); double usecPerGet = (double)OctreeElement::getGetChildAtIndexTime() / (double)OctreeElement::getGetChildAtIndexCalls(); - qDebug() << "getChildAtIndexCalls=" << OctreeElement::getGetChildAtIndexCalls() + qDebug() << "getChildAtIndexCalls=" << OctreeElement::getGetChildAtIndexCalls() << " getChildAtIndexTime=" << OctreeElement::getGetChildAtIndexTime() << " perGet=" << usecPerGet << " \n"; - + double usecPerSet = (double)OctreeElement::getSetChildAtIndexTime() / (double)OctreeElement::getSetChildAtIndexCalls(); - qDebug() << "setChildAtIndexCalls=" << OctreeElement::getSetChildAtIndexCalls() + qDebug() << "setChildAtIndexCalls=" << OctreeElement::getSetChildAtIndexCalls() << " setChildAtIndexTime=" << OctreeElement::getSetChildAtIndexTime() << " perset=" << usecPerSet << " \n"; _initialLoadComplete = true; _lastCheck = usecTimestampNow(); // we just loaded, no need to save again + +qDebug() << "about to emit loadCompleted();\n"; + emit loadCompleted(); +qDebug() << "after emit loadCompleted();\n"; } - + if (isStillRunning()) { uint64_t MSECS_TO_USECS = 1000; uint64_t USECS_TO_SLEEP = 10 * MSECS_TO_USECS; // every 10ms usleep(USECS_TO_SLEEP); - + // do our updates then check to save... _tree->lockForWrite(); _tree->update(); _tree->unlock(); - + uint64_t now = usecTimestampNow(); uint64_t sinceLastSave = now - _lastCheck; uint64_t intervalToCheck = _persistInterval * MSECS_TO_USECS; - + if (sinceLastSave > intervalToCheck) { // check the dirty bit and persist here... _lastCheck = usecTimestampNow(); @@ -86,6 +89,6 @@ bool OctreePersistThread::process() { qDebug("DONE saving Octrees to file...\n"); } } - } + } return isStillRunning(); // keep running till they terminate us } diff --git a/libraries/octree-server/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h similarity index 89% rename from libraries/octree-server/src/OctreePersistThread.h rename to libraries/octree/src/OctreePersistThread.h index e5f261960b..5032bcbf02 100644 --- a/libraries/octree-server/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -12,20 +12,24 @@ #define __Octree_server__OctreePersistThread__ #include -#include +#include "Octree.h" -/// Generalized threaded processor for handling received inbound packets. -class OctreePersistThread : public virtual GenericThread { +/// Generalized threaded processor for handling received inbound packets. +class OctreePersistThread : public GenericThread { + Q_OBJECT public: static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds OctreePersistThread(Octree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL); - + bool isInitialLoadComplete() const { return _initialLoadComplete; } time_t* getLoadCompleted() { return &_loadCompleted; } uint64_t getLoadElapsedTime() const { return _loadTimeUSecs; } +signals: + void loadCompleted(); + protected: /// Implements generic processing behavior for this thread. virtual bool process(); diff --git a/libraries/shared/src/GenericThread.h b/libraries/shared/src/GenericThread.h index 2de9112204..6dea7d8cc8 100644 --- a/libraries/shared/src/GenericThread.h +++ b/libraries/shared/src/GenericThread.h @@ -11,22 +11,25 @@ #ifndef __shared__GenericThread__ #define __shared__GenericThread__ +#include + #include /// A basic generic "thread" class. Handles a single thread of control within the application. Can operate in non-threaded /// mode but caller must regularly call threadRoutine() method. -class GenericThread { +class GenericThread : public QObject { + Q_OBJECT public: GenericThread(); virtual ~GenericThread(); - /// Call to start the thread. + /// Call to start the thread. /// \param bool isThreaded true by default. false for non-threaded mode and caller must call threadRoutine() regularly. void initialize(bool isThreaded = true); /// Call to stop the thread void terminate(); - + /// If you're running in non-threaded mode, you must call this regularly void* threadRoutine(); @@ -42,7 +45,7 @@ protected: /// Unlocks all the resources of the thread. void unlock() { pthread_mutex_unlock(&_mutex); } - + bool isStillRunning() const { return !_stopThread; } private: diff --git a/libraries/voxels/src/VoxelTreeElement.cpp b/libraries/voxels/src/VoxelTreeElement.cpp index 4641cabdf7..e3d0bf3f18 100644 --- a/libraries/voxels/src/VoxelTreeElement.cpp +++ b/libraries/voxels/src/VoxelTreeElement.cpp @@ -14,7 +14,7 @@ #include "VoxelTreeElement.h" #include "VoxelTree.h" -VoxelTreeElement::VoxelTreeElement(unsigned char* octalCode) : OctreeElement() { +VoxelTreeElement::VoxelTreeElement(unsigned char* octalCode) : OctreeElement() { init(octalCode); }; @@ -23,7 +23,7 @@ VoxelTreeElement::~VoxelTreeElement() { } // This will be called primarily on addChildAt(), which means we're adding a child of our -// own type to our own tree. This means we should initialize that child with any tree and type +// own type to our own tree. This means we should initialize that child with any tree and type // specific settings that our children must have. One example is out VoxelSystem, which // we know must match ours. OctreeElement* VoxelTreeElement::createNewElement(unsigned char* octalCode) const { @@ -63,10 +63,10 @@ bool VoxelTreeElement::appendElementData(OctreePacketData* packetData) const { } -int VoxelTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args) { - const int BYTES_PER_COLOR = 3; - +int VoxelTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args) { + const int BYTES_PER_COLOR = 3; + // pull the color for this child nodeColor newColor = { 128, 128, 128, 1}; if (args.includeColor) { @@ -82,11 +82,11 @@ uint8_t VoxelTreeElement::_nextIndex = INDEX_FOR_NULL + 1; // start at 1, 0 is r std::map VoxelTreeElement::_mapVoxelSystemPointersToIndex; std::map VoxelTreeElement::_mapIndexToVoxelSystemPointers; -VoxelSystem* VoxelTreeElement::getVoxelSystem() const { +VoxelSystem* VoxelTreeElement::getVoxelSystem() const { if (_voxelSystemIndex > INDEX_FOR_NULL) { if (_mapIndexToVoxelSystemPointers.end() != _mapIndexToVoxelSystemPointers.find(_voxelSystemIndex)) { - VoxelSystem* voxelSystem = _mapIndexToVoxelSystemPointers[_voxelSystemIndex]; + VoxelSystem* voxelSystem = _mapIndexToVoxelSystemPointers[_voxelSystemIndex]; return voxelSystem; } } @@ -129,7 +129,7 @@ void VoxelTreeElement::setFalseColored(bool isFalseColored) { if (_falseColored && !isFalseColored) { memcpy(&_currentColor,&_trueColor,sizeof(nodeColor)); } - _falseColored = isFalseColored; + _falseColored = isFalseColored; _isDirty = true; _density = 1.0f; // If color set, assume leaf, re-averaging will update density if needed. markWithChangedTime(); @@ -167,7 +167,7 @@ void VoxelTreeElement::calculateAverageFromChildren() { density += childAt->getDensity(); } } - density /= (float) NUMBER_OF_CHILDREN; + density /= (float) NUMBER_OF_CHILDREN; // // The VISIBLE_ABOVE_DENSITY sets the density of matter above which an averaged color voxel will // be set. It is an important physical constant in our universe. A number below 0.5 will cause @@ -175,9 +175,9 @@ void VoxelTreeElement::calculateAverageFromChildren() { // less data, which is (probably) going to be preferable because it gives a sense that there is // something out there to go investigate. A number above 0.5 would cause the world to become // more 'empty' at a distance. Exactly 0.5 would match the physical world, at least for materials - // that are not shiny and have equivalent ambient reflectance. + // that are not shiny and have equivalent ambient reflectance. // - const float VISIBLE_ABOVE_DENSITY = 0.10f; + const float VISIBLE_ABOVE_DENSITY = 0.10f; nodeColor newColor = { 0, 0, 0, 0}; if (density > VISIBLE_ABOVE_DENSITY) { // The density of material in the space of the voxel sets whether it is actually colored @@ -188,14 +188,14 @@ void VoxelTreeElement::calculateAverageFromChildren() { // set the alpha to 1 to indicate that this isn't transparent newColor[3] = 1; } - // Set the color from the average of the child colors, and update the density + // Set the color from the average of the child colors, and update the density setColor(newColor); setDensity(density); } // will detect if children are leaves AND the same color // and in that case will delete the children and make this node -// a leaf, returns TRUE if all the leaves are collapsed into a +// a leaf, returns TRUE if all the leaves are collapsed into a // single node bool VoxelTreeElement::collapseChildren() { // scan children, verify that they are ALL present and accounted for @@ -213,15 +213,15 @@ bool VoxelTreeElement::collapseChildren() { red = childAt->getColor()[0]; green = childAt->getColor()[1]; blue = childAt->getColor()[2]; - } else if (red != childAt->getColor()[0] || + } else if (red != childAt->getColor()[0] || green != childAt->getColor()[1] || blue != childAt->getColor()[2]) { allChildrenMatch=false; break; } } } - - + + if (allChildrenMatch) { //qDebug("allChildrenMatch: pruning tree\n"); for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -230,9 +230,9 @@ bool VoxelTreeElement::collapseChildren() { setChildAtIndex(i, NULL); // set it to NULL } nodeColor collapsedColor; - collapsedColor[0]=red; - collapsedColor[1]=green; - collapsedColor[2]=blue; + collapsedColor[0]=red; + collapsedColor[1]=green; + collapsedColor[2]=blue; collapsedColor[3]=1; // color is set setColor(collapsedColor); } @@ -240,7 +240,7 @@ bool VoxelTreeElement::collapseChildren() { } -bool VoxelTreeElement::findSpherePenetration(const glm::vec3& center, float radius, +bool VoxelTreeElement::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const { if (_box.findSpherePenetration(center, radius, penetration)) { @@ -254,7 +254,7 @@ bool VoxelTreeElement::findSpherePenetration(const glm::vec3& center, float radi voxelDetails->red = getTrueColor()[RED_INDEX]; voxelDetails->green = getTrueColor()[GREEN_INDEX]; voxelDetails->blue = getTrueColor()[BLUE_INDEX]; - + *penetratedObject = (void*)voxelDetails; } return true;