diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 6508f09f72..f37e8cf3f7 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -266,3 +266,72 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio tree->setWantEditLogging(wantEditLogging); tree->setWantTerseEditLogging(wantTerseEditLogging); } + + +// FIXME - this stats tracking is somewhat temporary to debug the Whiteboard issues. It's not a bad +// set of stats to have, but we'd probably want a different data structure if we keep it very long. +// Since this version uses a single shared QMap for all senders, there could be some lock contention +// on this QWriteLocker +void EntityServer::trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& viewerNode) { + QWriteLocker locker(&_viewerSendingStatsLock); + _viewerSendingStats[viewerNode][dataID] = { usecTimestampNow(), dataLastEdited }; +} + +void EntityServer::trackViewerGone(const QUuid& viewerNode) { + QWriteLocker locker(&_viewerSendingStatsLock); + _viewerSendingStats.remove(viewerNode); +} + +QString EntityServer::serverSubclassStats() { + QLocale locale(QLocale::English); + QString statsString; + + // display memory usage stats + statsString += "Entity Server Memory Statistics\r\n"; + statsString += QString().sprintf("EntityTreeElement size... %ld bytes\r\n", sizeof(EntityTreeElement)); + statsString += QString().sprintf(" EntityItem size... %ld bytes\r\n", sizeof(EntityItem)); + statsString += "\r\n\r\n"; + + statsString += "Entity Server Sending to Viewer Statistics\r\n"; + statsString += "----- Viewer Node ID ----------------- ----- Entity ID ---------------------- " + "---------- Last Sent To ---------- ---------- Last Edited -----------\r\n"; + + int viewers = 0; + const int COLUMN_WIDTH = 24; + + { + QReadLocker locker(&_viewerSendingStatsLock); + quint64 now = usecTimestampNow(); + + for (auto viewerID : _viewerSendingStats.keys()) { + statsString += viewerID.toString() + "\r\n"; + + auto viewerData = _viewerSendingStats[viewerID]; + for (auto entityID : viewerData.keys()) { + ViewerSendingStats stats = viewerData[entityID]; + + quint64 elapsedSinceSent = now - stats.lastSent; + double sentMsecsAgo = (double)(elapsedSinceSent / USECS_PER_MSEC); + + quint64 elapsedSinceEdit = now - stats.lastEdited; + double editMsecsAgo = (double)(elapsedSinceEdit / USECS_PER_MSEC); + + statsString += " "; // the viewerID spacing + statsString += entityID.toString(); + statsString += " "; + statsString += QString("%1 msecs ago") + .arg(locale.toString((double)sentMsecsAgo).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString("%1 msecs ago") + .arg(locale.toString((double)editMsecsAgo).rightJustified(COLUMN_WIDTH, ' ')); + statsString += "\r\n"; + } + viewers++; + } + } + if (viewers < 1) { + statsString += " no viewers... \r\n"; + } + statsString += "\r\n\r\n"; + + return statsString; +} diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index d9795316c4..89b445c449 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -21,6 +21,12 @@ #include "EntityTree.h" /// Handles assignments of type EntityServer - sending entities to various clients. + +struct ViewerSendingStats { + quint64 lastSent; + quint64 lastEdited; +}; + class EntityServer : public OctreeServer, public NewlyCreatedEntityHook { Q_OBJECT public: @@ -44,6 +50,10 @@ public: virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) override; virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject) override; + virtual QString serverSubclassStats(); + + virtual void trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& viewerNode); + virtual void trackViewerGone(const QUuid& viewerNode); public slots: void pruneDeletedEntities(); @@ -57,6 +67,9 @@ private slots: private: EntitySimulation* _entitySimulation; QTimer* _pruneDeletedEntitiesTimer = nullptr; + + QReadWriteLock _viewerSendingStatsLock; + QMap> _viewerSendingStats; }; #endif // hifi_EntityServer_h diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index cff2c7ee2e..cafba8c083 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -179,15 +179,9 @@ void OctreeQueryNode::resetOctreePacket() { // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use // the clients requested color state. - _currentPacketIsColor = getWantColor(); - _currentPacketIsCompressed = getWantCompression(); OCTREE_PACKET_FLAGS flags = 0; - if (_currentPacketIsColor) { - setAtBit(flags, PACKET_IS_COLOR_BIT); - } - if (_currentPacketIsCompressed) { - setAtBit(flags, PACKET_IS_COMPRESSED_BIT); - } + setAtBit(flags, PACKET_IS_COLOR_BIT); + setAtBit(flags, PACKET_IS_COMPRESSED_BIT); _octreePacket->reset(); diff --git a/assignment-client/src/octree/OctreeQueryNode.h b/assignment-client/src/octree/OctreeQueryNode.h index 0c691a06a2..67298296e9 100644 --- a/assignment-client/src/octree/OctreeQueryNode.h +++ b/assignment-client/src/octree/OctreeQueryNode.h @@ -14,7 +14,6 @@ #include -#include #include #include #include @@ -55,7 +54,6 @@ public: void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; } OctreeElementBag elementBag; - CoverageMap map; OctreeElementExtraEncodeData extraEncodeData; ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; } @@ -79,9 +77,7 @@ public: bool getCurrentPacketIsColor() const { return _currentPacketIsColor; } bool getCurrentPacketIsCompressed() const { return _currentPacketIsCompressed; } - bool getCurrentPacketFormatMatches() { - return (getCurrentPacketIsColor() == getWantColor() && getCurrentPacketIsCompressed() == getWantCompression()); - } + bool getCurrentPacketFormatMatches() { return (getCurrentPacketIsCompressed() == true); } // FIXME bool hasLodChanged() const { return _lodChanged; } diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 94d82b463e..d01117dff6 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -321,8 +321,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // If we're starting a fresh packet, then... // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use // the clients requested color state. - bool wantColor = nodeData->getWantColor(); - bool wantCompression = nodeData->getWantCompression(); // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color // then let's just send that waiting packet. @@ -333,10 +331,8 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus nodeData->resetOctreePacket(); } int targetSize = MAX_OCTREE_PACKET_DATA_SIZE; - if (wantCompression) { - targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); - } - _packetData.changeSettings(wantCompression, targetSize); + targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); + _packetData.changeSettings(targetSize); } const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; @@ -350,7 +346,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) { nodeData->dumpOutOfView(); } - nodeData->map.erase(); } if (!viewFrustumChanged && !nodeData->getWantDelta()) { @@ -451,22 +446,25 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus } */ - bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); - CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; - float octreeSizeScale = nodeData->getOctreeSizeScale(); int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, + EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, - wantOcclusionCulling, coverageMap, boundaryLevelAdjust, octreeSizeScale, + boundaryLevelAdjust, octreeSizeScale, nodeData->getLastTimeBagEmpty(), isFullScene, &nodeData->stats, _myServer->getJurisdiction(), &nodeData->extraEncodeData); + // Our trackSend() function is implemented by the server subclass, and will be called back + // during the encodeTreeBitstream() as new entities/data elements are sent + params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) { + _myServer->trackSend(dataID, dataEdited, _nodeUUID); + }; + // TODO: should this include the lock time or not? This stat is sent down to the client, // it seems like it may be a good idea to include the lock time as part of the encode time // are reported to client. Since you can encode without the lock @@ -550,10 +548,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent); quint64 packetSendingEnd = usecTimestampNow(); packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart); - - if (wantCompression) { - targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); - } + targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); } else { // If we're in compressed mode, then we want to see if we have room for more in this wire packet. // but we've finalized the _packetData, so we want to start a new section, we will do that by @@ -563,7 +558,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // a larger compressed size then uncompressed size targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING; } - _packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset + _packetData.changeSettings(targetSize); // will do reset } OctreeServer::trackTreeWaitTime(lockWaitElapsedUsec); @@ -628,7 +623,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus if (nodeData->elementBag.isEmpty()) { nodeData->updateLastKnownViewFrustum(); nodeData->setViewSent(true); - nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes } } // end if bag wasn't empty, and so we sent stuff... diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 7cd3e59edf..dead61d65a 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -821,6 +821,11 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url .arg(locale.toString((uint)checkSum).rightJustified(16, ' ')); statsString += "\r\n\r\n"; + + statsString += serverSubclassStats(); + + statsString += "\r\n\r\n"; + statsString += "\r\n"; statsString += ""; @@ -1179,6 +1184,8 @@ void OctreeServer::nodeKilled(SharedNodePointer node) { if (usecsElapsed > 1000) { qDebug() << qPrintable(_safeServerName) << "server nodeKilled() took: " << usecsElapsed << " usecs for node:" << *node; } + + trackViewerGone(node->getUUID()); } void OctreeServer::forceNodeShutdown(SharedNodePointer node) { diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 1aea9c960e..f3a5191335 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -79,6 +79,9 @@ public: virtual void beforeRun() { } virtual bool hasSpecialPacketsToSend(const SharedNodePointer& node) { return false; } virtual int sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { return 0; } + virtual QString serverSubclassStats() { return QString(); } + virtual void trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& viewerNode) { } + virtual void trackViewerGone(const QUuid& viewerNode) { } static float SKIP_TIME; // use this for trackXXXTime() calls for non-times diff --git a/examples/example/entities/butterflies.js b/examples/example/entities/butterflies.js index 3a78a2fc1c..9d53fc0ebf 100644 --- a/examples/example/entities/butterflies.js +++ b/examples/example/entities/butterflies.js @@ -32,7 +32,7 @@ function randVector(a, b) { var startTimeInSeconds = new Date().getTime() / 1000; -var NATURAL_SIZE_OF_BUTTERFLY = { x: 1.0, y: 0.4, z: 0.2 }; +var NATURAL_SIZE_OF_BUTTERFLY = { x:0.5, y: 0.2, z: 0.1 }; var lifeTime = 3600; // One hour lifespan var range = 7.0; // Over what distance in meters do you want the flock to fly around @@ -65,8 +65,8 @@ function addButterfly() { var color = { red: 100, green: 100, blue: 100 }; var size = 0; - var MINSIZE = 0.06; - var RANGESIZE = 0.2; + var MINSIZE = 0.01; + var RANGESIZE = 0.05; var maxSize = MINSIZE + RANGESIZE; size = MINSIZE + Math.random() * RANGESIZE; @@ -74,7 +74,7 @@ function addButterfly() { var dimensions = Vec3.multiply(NATURAL_SIZE_OF_BUTTERFLY, (size / maxSize)); var GRAVITY = -0.2; - var newFrameRate = 20 + Math.random() * 30; + var newFrameRate = 29 + Math.random() * 30; var properties = { type: "Model", lifetime: lifeTime, @@ -86,17 +86,13 @@ function addButterfly() { dimensions: dimensions, color: color, animation: { - url: "http://public.highfidelity.io/models/content/butterfly/butterfly.fbx", - firstFrame: 0, + url: "http://hifi-content.s3.amazonaws.com/james/butterfly/butterfly.fbx", fps: newFrameRate, - currentFrame: 0, - hold: false, - lastFrame: 10000, loop: true, running: true, startAutomatically:false }, - modelURL: "http://public.highfidelity.io/models/content/butterfly/butterfly.fbx" + modelURL: "http://hifi-content.s3.amazonaws.com/james/butterfly/butterfly.fbx" }; butterflies.push(Entities.addEntity(properties)); } diff --git a/examples/toybox/basketball/createSingleBasketball.js b/examples/toybox/basketball/createSingleBasketball.js index a1e0140553..6765e5e075 100644 --- a/examples/toybox/basketball/createSingleBasketball.js +++ b/examples/toybox/basketball/createSingleBasketball.js @@ -47,7 +47,12 @@ function makeBasketball() { modelURL: basketballURL, restitution: 1.0, damping: 0.00001, - shapeType: "sphere" + shapeType: "sphere", + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true + } + }) }); originalPosition = position; } diff --git a/examples/toybox/doll/createDoll.js b/examples/toybox/doll/createDoll.js index ffd840f4ea..52ba5a5291 100644 --- a/examples/toybox/doll/createDoll.js +++ b/examples/toybox/doll/createDoll.js @@ -15,12 +15,20 @@ function createDoll() { var scriptURL = Script.resolvePath("doll.js"); - var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 }), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); + var center = Vec3.sum(Vec3.sum(MyAvatar.position, { + x: 0, + y: 0.5, + z: 0 + }), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); - var naturalDimensions = { x: 1.63, y: 1.67, z: 0.26}; + var naturalDimensions = { + x: 1.63, + y: 1.67, + z: 0.26 + }; var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); - + var doll = Entities.addEntity({ type: "Model", name: "doll", @@ -39,7 +47,12 @@ function createDoll() { y: 0, z: 0 }, - collisionsWillMove: true + collisionsWillMove: true, + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true + } + }) }); return doll; } diff --git a/examples/toybox/flashlight/createFlashlight.js b/examples/toybox/flashlight/createFlashlight.js index b049d2632e..108d519d3e 100644 --- a/examples/toybox/flashlight/createFlashlight.js +++ b/examples/toybox/flashlight/createFlashlight.js @@ -18,14 +18,27 @@ var scriptURL = Script.resolvePath('flashlight.js'); var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; -var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); +var center = Vec3.sum(Vec3.sum(MyAvatar.position, { + x: 0, + y: 0.5, + z: 0 +}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); var flashlight = Entities.addEntity({ type: "Model", modelURL: modelURL, position: center, - dimensions: { x: 0.08, y: 0.30, z: 0.08}, + dimensions: { + x: 0.08, + y: 0.30, + z: 0.08 + }, collisionsWillMove: true, shapeType: 'box', - script: scriptURL -}); + script: scriptURL, + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true + } + }) +}); \ No newline at end of file diff --git a/examples/toybox/ping_pong_gun/createPingPongGun.js b/examples/toybox/ping_pong_gun/createPingPongGun.js index 705671e784..4b93842896 100644 --- a/examples/toybox/ping_pong_gun/createPingPongGun.js +++ b/examples/toybox/ping_pong_gun/createPingPongGun.js @@ -35,7 +35,12 @@ var pingPongGun = Entities.addEntity({ z: 0.47 }, collisionsWillMove: true, - collisionSoundURL: COLLISION_SOUND_URL + collisionSoundURL: COLLISION_SOUND_URL, + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true + } + }) }); function cleanUp() { diff --git a/interface/resources/controllers/hydra.json b/interface/resources/controllers/hydra.json index b517d1bad5..0193612d27 100644 --- a/interface/resources/controllers/hydra.json +++ b/interface/resources/controllers/hydra.json @@ -13,12 +13,11 @@ { "from": "Hydra.RB", "to": "Standard.RB" }, { "from": "Hydra.RS", "to": "Standard.RS" }, - { "from": [ "Hydra.L3", "Hydra.L4" ], "to": "Standard.LeftPrimaryThumb" }, - { "from": [ "Hydra.L1", "Hydra.L2" ], "to": "Standard.LeftSecondaryThumb" }, - - { "from": [ "Hydra.R3", "Hydra.R4" ], "to": "Standard.RightPrimaryThumb" }, - { "from": [ "Hydra.R1", "Hydra.R2" ], "to": "Standard.RightSecondaryThumb" }, + { "from": [ "Hydra.L1", "Hydra.L2", "Hydra.L3", "Hydra.L4" ], "to": "Standard.LeftPrimaryThumb" }, + { "from": [ "Hydra.L0" ], "to": "Standard.LeftSecondaryThumb" }, + { "from": [ "Hydra.R1", "Hydra.R2", "Hydra.R3", "Hydra.R4" ], "to": "Standard.RightPrimaryThumb" }, + { "from": [ "Hydra.R0" ], "to": "Standard.RightSecondaryThumb" }, { "from": "Hydra.LeftHand", "to": "Standard.LeftHand" }, { "from": "Hydra.RightHand", "to": "Standard.RightHand" } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 921c126ce6..725606a8ee 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -78,7 +78,7 @@ #include #include #include -#include +#include #include #include #include @@ -616,7 +616,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); - _offscreenContext = new OffscreenGlCanvas(); + _offscreenContext = new OffscreenGLCanvas(); _offscreenContext->create(_glWidget->context()->contextHandle()); _offscreenContext->makeCurrent(); initializeGL(); @@ -3074,10 +3074,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node // These will be the same for all servers, so we can set them up once and then reuse for each server we send to. _octreeQuery.setWantLowResMoving(true); - _octreeQuery.setWantColor(true); _octreeQuery.setWantDelta(true); - _octreeQuery.setWantOcclusionCulling(false); - _octreeQuery.setWantCompression(true); _octreeQuery.setCameraPosition(_viewFrustum.getPosition()); _octreeQuery.setCameraOrientation(_viewFrustum.getOrientation()); diff --git a/interface/src/Application.h b/interface/src/Application.h index 5c952f462e..d2f896e240 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -65,7 +65,7 @@ #include "ui/ToolWindow.h" #include "UndoStackScriptingInterface.h" -class OffscreenGlCanvas; +class OffscreenGLCanvas; class GLCanvas; class FaceTracker; class MainWindow; @@ -420,7 +420,7 @@ private: bool _dependencyManagerIsSetup; - OffscreenGlCanvas* _offscreenContext { nullptr }; + OffscreenGLCanvas* _offscreenContext { nullptr }; DisplayPluginPointer _displayPlugin; InputPluginList _activeInputPlugins; diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp index d9cde868a9..0b4c6dde3d 100644 --- a/interface/src/GLCanvas.cpp +++ b/interface/src/GLCanvas.cpp @@ -9,54 +9,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// FIXME ordering of headers #include "Application.h" #include "GLCanvas.h" -#include -#include #include #include "MainWindow.h" #include "Menu.h" -static QGLFormat& getDesiredGLFormat() { - // Specify an OpenGL 3.3 format using the Core profile. - // That is, no old-school fixed pipeline functionality - static QGLFormat glFormat; - static std::once_flag once; - std::call_once(once, [] { - glFormat.setVersion(4, 1); - glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0 - glFormat.setSampleBuffers(false); - glFormat.setDepth(false); - glFormat.setStencil(false); - }); - return glFormat; -} - -GLCanvas::GLCanvas() : QGLWidget(getDesiredGLFormat()) { -#ifdef Q_OS_LINUX - // Cause GLCanvas::eventFilter to be called. - // It wouldn't hurt to do this on Mac and PC too; but apparently it's only needed on linux. - qApp->installEventFilter(this); -#endif -} - -int GLCanvas::getDeviceWidth() const { - return width() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f); -} - -int GLCanvas::getDeviceHeight() const { - return height() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f); -} - -void GLCanvas::initializeGL() { - setAttribute(Qt::WA_AcceptTouchEvents); - setAcceptDrops(true); - // Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate. - setAutoBufferSwap(false); -} - void GLCanvas::paintGL() { PROFILE_RANGE(__FUNCTION__); @@ -74,68 +35,8 @@ void GLCanvas::resizeGL(int width, int height) { } bool GLCanvas::event(QEvent* event) { - switch (event->type()) { - case QEvent::MouseMove: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::KeyPress: - case QEvent::KeyRelease: - case QEvent::FocusIn: - case QEvent::FocusOut: - case QEvent::Resize: - case QEvent::TouchBegin: - case QEvent::TouchEnd: - case QEvent::TouchUpdate: - case QEvent::Wheel: - case QEvent::DragEnter: - case QEvent::Drop: - if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) { - return true; - } - break; - case QEvent::Paint: - // Ignore paint events that occur after we've decided to quit - if (qApp->isAboutToQuit()) { - return true; - } - break; - - default: - break; + if (QEvent::Paint == event->type() && qApp->isAboutToQuit()) { + return true; } - return QGLWidget::event(event); -} - - -// Pressing Alt (and Meta) key alone activates the menubar because its style inherits the -// SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to -// receive keyPress events for the Alt (and Meta) key in a reliable manner. -// -// This filter catches events before QMenuBar can steal the keyboard focus. -// The idea was borrowed from -// http://www.archivum.info/qt-interest@trolltech.com/2006-09/00053/Re-(Qt4)-Alt-key-focus-QMenuBar-(solved).html - -bool GLCanvas::eventFilter(QObject*, QEvent* event) { - switch (event->type()) { - case QEvent::KeyPress: - case QEvent::KeyRelease: - case QEvent::ShortcutOverride: - { - QKeyEvent* keyEvent = static_cast(event); - if (keyEvent->key() == Qt::Key_Alt || keyEvent->key() == Qt::Key_Meta) { - if (event->type() == QEvent::KeyPress) { - keyPressEvent(keyEvent); - } else if (event->type() == QEvent::KeyRelease) { - keyReleaseEvent(keyEvent); - } else { - QGLWidget::event(event); - } - return true; - } - } - default: - break; - } - return false; + return GLWidget::event(event); } diff --git a/interface/src/GLCanvas.h b/interface/src/GLCanvas.h index 73c5b5e8bf..0442159eeb 100644 --- a/interface/src/GLCanvas.h +++ b/interface/src/GLCanvas.h @@ -12,31 +12,15 @@ #ifndef hifi_GLCanvas_h #define hifi_GLCanvas_h -#include -#include -#include +#include /// customized canvas that simply forwards requests/events to the singleton application -class GLCanvas : public QGLWidget { +class GLCanvas : public GLWidget { Q_OBJECT - -public: - GLCanvas(); - - int getDeviceWidth() const; - int getDeviceHeight() const; - QSize getDeviceSize() const { return QSize(getDeviceWidth(), getDeviceHeight()); } - protected: - - virtual void initializeGL(); - virtual void paintGL(); - virtual void resizeGL(int width, int height); - virtual bool event(QEvent* event); - -private slots: - bool eventFilter(QObject*, QEvent* event); - + virtual void paintGL() override; + virtual void resizeGL(int width, int height) override; + virtual bool event(QEvent* event) override; }; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 0ee07d15ec..959e1162e6 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -77,9 +77,9 @@ AvatarManager::AvatarManager(QObject* parent) : } const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters -Setting::Handle avatarRenderDistanceHighLimit("avatarRenderDistanceHighLimit", 1.0f / SMALLEST_REASONABLE_HORIZON); -void AvatarManager::setRenderDistanceHighLimit(float newValue) { - avatarRenderDistanceHighLimit.set(newValue); +Setting::Handle avatarRenderDistanceInverseHighLimit("avatarRenderDistanceHighLimit", 1.0f / SMALLEST_REASONABLE_HORIZON); +void AvatarManager::setRenderDistanceInverseHighLimit(float newValue) { + avatarRenderDistanceInverseHighLimit.set(newValue); _renderDistanceController.setControlledValueHighLimit(newValue); } @@ -101,7 +101,7 @@ void AvatarManager::init() { const float target_fps = qApp->getTargetFrameRate(); _renderDistanceController.setMeasuredValueSetpoint(target_fps); - _renderDistanceController.setControlledValueHighLimit(avatarRenderDistanceHighLimit.get()); + _renderDistanceController.setControlledValueHighLimit(avatarRenderDistanceInverseHighLimit.get()); _renderDistanceController.setControlledValueLowLimit(1.0f / (float) TREE_SCALE); // Advice for tuning parameters: // See PIDController.h. There's a section on tuning in the reference. diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index ddc40d8490..84a4bc44b8 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -70,16 +70,16 @@ public: // Expose results and parameter-tuning operations to other systems, such as stats and javascript. Q_INVOKABLE float getRenderDistance() { return _renderDistance; } - Q_INVOKABLE float getRenderDistanceLowLimit() { return _renderDistanceController.getControlledValueLowLimit(); } - Q_INVOKABLE float getRenderDistanceHighLimit() { return _renderDistanceController.getControlledValueHighLimit(); } + Q_INVOKABLE float getRenderDistanceInverseLowLimit() { return _renderDistanceController.getControlledValueLowLimit(); } + Q_INVOKABLE float getRenderDistanceInverseHighLimit() { return _renderDistanceController.getControlledValueHighLimit(); } Q_INVOKABLE int getNumberInRenderRange() { return _renderedAvatarCount; } Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); } Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); } Q_INVOKABLE void setRenderDistanceKP(float newValue) { _renderDistanceController.setKP(newValue); } Q_INVOKABLE void setRenderDistanceKI(float newValue) { _renderDistanceController.setKI(newValue); } Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); } - Q_INVOKABLE void setRenderDistanceLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); } - Q_INVOKABLE void setRenderDistanceHighLimit(float newValue); + Q_INVOKABLE void setRenderDistanceInverseLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); } + Q_INVOKABLE void setRenderDistanceInverseHighLimit(float newValue); public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 29ec781f23..aa2d268f3d 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -43,9 +43,11 @@ Head::Head(Avatar* owningAvatar) : _longTermAverageLoudness(-1.0f), _audioAttack(0.0f), _audioJawOpen(0.0f), + _trailingAudioJawOpen(0.0f), _mouth2(0.0f), _mouth3(0.0f), _mouth4(0.0f), + _mouthTime(0.0f), _renderLookatVectors(false), _renderLookatTarget(false), _saccade(0.0f, 0.0f, 0.0f), @@ -246,6 +248,16 @@ void Head::calculateMouthShapes() { const float JAW_OPEN_SCALE = 0.015f; const float JAW_OPEN_RATE = 0.9f; const float JAW_CLOSE_RATE = 0.90f; + const float TIMESTEP_CONSTANT = 0.0032f; + const float MMMM_POWER = 0.10f; + const float SMILE_POWER = 0.10f; + const float FUNNEL_POWER = 0.35f; + const float MMMM_SPEED = 2.685f; + const float SMILE_SPEED = 1.0f; + const float FUNNEL_SPEED = 2.335f; + const float STOP_GAIN = 5.0f; + + // From the change in loudness, decide how much to open or close the jaw float audioDelta = sqrtf(glm::max(_averageLoudness - _longTermAverageLoudness, 0.0f)) * JAW_OPEN_SCALE; if (audioDelta > _audioJawOpen) { _audioJawOpen += (audioDelta - _audioJawOpen) * JAW_OPEN_RATE; @@ -253,21 +265,14 @@ void Head::calculateMouthShapes() { _audioJawOpen *= JAW_CLOSE_RATE; } _audioJawOpen = glm::clamp(_audioJawOpen, 0.0f, 1.0f); + _trailingAudioJawOpen = glm::mix(_trailingAudioJawOpen, _audioJawOpen, 0.99f); - // _mouth2 = "mmmm" shape - // _mouth3 = "funnel" shape - // _mouth4 = "smile" shape - const float FUNNEL_PERIOD = 0.985f; - const float FUNNEL_RANDOM_PERIOD = 0.01f; - const float MMMM_POWER = 0.25f; - const float MMMM_PERIOD = 0.91f; - const float MMMM_RANDOM_PERIOD = 0.15f; - const float SMILE_PERIOD = 0.925f; - const float SMILE_RANDOM_PERIOD = 0.05f; - - _mouth3 = glm::mix(_audioJawOpen, _mouth3, FUNNEL_PERIOD + randFloat() * FUNNEL_RANDOM_PERIOD); - _mouth2 = glm::mix(_audioJawOpen * MMMM_POWER, _mouth2, MMMM_PERIOD + randFloat() * MMMM_RANDOM_PERIOD); - _mouth4 = glm::mix(_audioJawOpen, _mouth4, SMILE_PERIOD + randFloat() * SMILE_RANDOM_PERIOD); + // Advance time at a rate proportional to loudness, and move the mouth shapes through + // a cycle at differing speeds to create a continuous random blend of shapes. + _mouthTime += sqrtf(_averageLoudness) * TIMESTEP_CONSTANT; + _mouth2 = (sinf(_mouthTime * MMMM_SPEED) + 1.0f) * MMMM_POWER * glm::min(1.0f, _trailingAudioJawOpen * STOP_GAIN); + _mouth3 = (sinf(_mouthTime * FUNNEL_SPEED) + 1.0f) * FUNNEL_POWER * glm::min(1.0f, _trailingAudioJawOpen * STOP_GAIN); + _mouth4 = (sinf(_mouthTime * SMILE_SPEED) + 1.0f) * SMILE_POWER * glm::min(1.0f, _trailingAudioJawOpen * STOP_GAIN); } void Head::applyEyelidOffset(glm::quat headOrientation) { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 1fbfceca92..ec88b295f7 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -124,9 +124,11 @@ private: float _longTermAverageLoudness; float _audioAttack; float _audioJawOpen; + float _trailingAudioJawOpen; float _mouth2; float _mouth3; float _mouth4; + float _mouthTime; bool _renderLookatVectors; bool _renderLookatTarget; glm::vec3 _saccade; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 69ece359e9..5d01893873 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -204,7 +204,7 @@ void PreferencesDialog::loadPreferences() { auto lodManager = DependencyManager::get(); ui.desktopMinimumFPSSpin->setValue(lodManager->getDesktopLODDecreaseFPS()); ui.hmdMinimumFPSSpin->setValue(lodManager->getHMDLODDecreaseFPS()); - ui.avatarRenderSmallestReasonableHorizon->setValue(1.0f / DependencyManager::get()->getRenderDistanceHighLimit()); + ui.avatarRenderSmallestReasonableHorizon->setValue(1.0f / DependencyManager::get()->getRenderDistanceInverseHighLimit()); } void PreferencesDialog::savePreferences() { @@ -295,5 +295,5 @@ void PreferencesDialog::savePreferences() { auto lodManager = DependencyManager::get(); lodManager->setDesktopLODDecreaseFPS(ui.desktopMinimumFPSSpin->value()); lodManager->setHMDLODDecreaseFPS(ui.hmdMinimumFPSSpin->value()); - DependencyManager::get()->setRenderDistanceHighLimit(1.0f / ui.avatarRenderSmallestReasonableHorizon->value()); + DependencyManager::get()->setRenderDistanceInverseHighLimit(1.0f / ui.avatarRenderSmallestReasonableHorizon->value()); } diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index 5914031a3a..0c6af2d5bd 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -11,6 +11,7 @@ #include "AnimPose.h" #include #include +#include const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), glm::quat(), @@ -18,7 +19,9 @@ const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), AnimPose::AnimPose(const glm::mat4& mat) { scale = extractScale(mat); - rot = glmExtractRotation(mat); + // quat_cast doesn't work so well with scaled matrices, so cancel it out. + glm::mat4 tmp = glm::scale(mat, 1.0f / scale); + rot = glm::normalize(glm::quat_cast(tmp)); trans = extractTranslation(mat); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 3ef882fe76..01cb0961e4 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -10,7 +10,6 @@ #include #include -#include #include diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index bb39c7bb7a..4278165e25 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp index 6e14a158d4..72921b4f90 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp @@ -8,15 +8,6 @@ #include "InterleavedStereoDisplayPlugin.h" -#include -#include - -#include -#include -#include - -#include - static const char * INTERLEAVED_TEXTURED_VS = R"VS(#version 410 core #pragma line __LINE__ @@ -81,4 +72,4 @@ void InterleavedStereoDisplayPlugin::display( _program->Bind(); Uniform(*_program, "textureSize").SetValue(sceneSize); WindowOpenGLDisplayPlugin::display(finalTexture, sceneSize); -} \ No newline at end of file +} diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index 5ba113420d..12865cf4cd 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -7,17 +7,7 @@ // #include "SideBySideStereoDisplayPlugin.h" - -#include -#include -#include - -#include -#include -#include - -#include -#include +#include const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo"); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index a70a8c0428..0120b7c0ca 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -311,6 +311,11 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet entityTreeElementExtraEncodeData->entities.insert(getEntityItemID(), propertiesDidntFit); } + // if any part of our entity was sent, call trackSend + if (appendState != OctreeElement::NONE) { + params.trackSend(getID(), getLastEdited()); + } + return appendState; } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 78a4f3e8b6..30bee83482 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -790,7 +790,9 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue // bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties, QByteArray& buffer) { - OctreePacketData ourDataPacket(false, buffer.size()); // create a packetData object to add out packet details too. + + // FIXME - remove non-compressed OctreePacketData and handle compressed edit packets + OctreePacketData ourDataPacket(buffer.size(), false); // create a packetData object to add out packet details too. OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro bool success = true; // assume the best diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index b3299b6fe6..ca67aadbd6 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -257,7 +257,7 @@ inline glmQuat glmQuat_convertFromScriptValue(const QScriptValue& v, bool& isVal } inline xColor xColor_convertFromScriptValue(const QScriptValue& v, bool& isValid) { - xColor newValue; + xColor newValue { 255, 255, 255 }; isValid = false; /// assume it can't be converted QScriptValue r = v.property("red"); QScriptValue g = v.property("green"); diff --git a/libraries/gl/src/gl/GLEscrow.h b/libraries/gl/src/gl/GLEscrow.h index 54b124ae3c..db9f033de8 100644 --- a/libraries/gl/src/gl/GLEscrow.h +++ b/libraries/gl/src/gl/GLEscrow.h @@ -87,6 +87,11 @@ public: _recycler = recycler; } + size_t depth() { + Lock lock(_mutex); + return _submits.size(); + } + // Submit a new resource from the producer context // returns the number of prior submissions that were // never consumed before becoming available. @@ -124,7 +129,7 @@ public: } return result; } - + // If fetch returns a non-zero value, it's the responsibility of the // client to release it at some point void release(T t, GLsync readSync = 0) { @@ -175,6 +180,7 @@ private: // May be called on any thread, but must be inside a locked section void pop(Deque& deque) { + Lock lock(_mutex); auto& item = deque.front(); _trash.push_front(item); deque.pop_front(); diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index f67f9e9120..6ad7f816b8 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -1,15 +1,37 @@ #include "GLHelpers.h" +#include -QSurfaceFormat getDefaultOpenGlSurfaceFormat() { - QSurfaceFormat format; - // Qt Quick may need a depth and stencil buffer. Always make sure these are available. - format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); - format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); - format.setVersion(4, 1); +#include +#include + +const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { + static QSurfaceFormat format; + static std::once_flag once; + std::call_once(once, [] { + // Qt Quick may need a depth and stencil buffer. Always make sure these are available. + format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); + format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); + format.setVersion(4, 1); #ifdef DEBUG - format.setOption(QSurfaceFormat::DebugContext); + format.setOption(QSurfaceFormat::DebugContext); #endif - format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); - return format; + format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); + }); + return format; +} + +const QGLFormat& getDefaultGLFormat() { + // Specify an OpenGL 3.3 format using the Core profile. + // That is, no old-school fixed pipeline functionality + static QGLFormat glFormat; + static std::once_flag once; + std::call_once(once, [] { + glFormat.setVersion(4, 1); + glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0 + glFormat.setSampleBuffers(false); + glFormat.setDepth(false); + glFormat.setStencil(false); + }); + return glFormat; } diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h index dc9f0f3140..335272d991 100644 --- a/libraries/gl/src/gl/GLHelpers.h +++ b/libraries/gl/src/gl/GLHelpers.h @@ -10,14 +10,15 @@ #ifndef hifi_GLHelpers_h #define hifi_GLHelpers_h -#include - // 16 bits of depth precision #define DEFAULT_GL_DEPTH_BUFFER_BITS 16 // 8 bits of stencil buffer (typically you really only need 1 bit for functionality // but GL implementations usually just come with buffer sizes in multiples of 8) #define DEFAULT_GL_STENCIL_BUFFER_BITS 8 -QSurfaceFormat getDefaultOpenGlSurfaceFormat(); +class QSurfaceFormat; +class QGLFormat; +const QSurfaceFormat& getDefaultOpenGLSurfaceFormat(); +const QGLFormat& getDefaultGLFormat(); #endif diff --git a/libraries/gl/src/gl/GLWidget.cpp b/libraries/gl/src/gl/GLWidget.cpp new file mode 100644 index 0000000000..310675c01f --- /dev/null +++ b/libraries/gl/src/gl/GLWidget.cpp @@ -0,0 +1,114 @@ +// +// +// Created by Bradley Austin Davis on 2015/12/03 +// Derived from interface/src/GLCanvas.cpp created by Stephen Birarda on 8/14/13. +// Copyright 2013-2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GLWidget.h" + +#include + +#include +#include +#include + +#include +#include + +#include "GLHelpers.h" + +GLWidget::GLWidget() : QGLWidget(getDefaultGLFormat()) { +#ifdef Q_OS_LINUX + // Cause GLWidget::eventFilter to be called. + // It wouldn't hurt to do this on Mac and PC too; but apparently it's only needed on linux. + qApp->installEventFilter(this); +#endif +} + +int GLWidget::getDeviceWidth() const { + return width() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f); +} + +int GLWidget::getDeviceHeight() const { + return height() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f); +} + +void GLWidget::initializeGL() { + setAttribute(Qt::WA_AcceptTouchEvents); + setAcceptDrops(true); + // Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate. + setAutoBufferSwap(false); +} + +void GLWidget::paintEvent(QPaintEvent* event) { + QWidget::paintEvent(event); +} + +void GLWidget::resizeEvent(QResizeEvent* event) { + QWidget::resizeEvent(event); +} + +bool GLWidget::event(QEvent* event) { + switch (event->type()) { + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::FocusIn: + case QEvent::FocusOut: + case QEvent::Resize: + case QEvent::TouchBegin: + case QEvent::TouchEnd: + case QEvent::TouchUpdate: + case QEvent::Wheel: + case QEvent::DragEnter: + case QEvent::Drop: + if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) { + return true; + } + break; + + default: + break; + } + return QGLWidget::event(event); +} + + +// Pressing Alt (and Meta) key alone activates the menubar because its style inherits the +// SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to +// receive keyPress events for the Alt (and Meta) key in a reliable manner. +// +// This filter catches events before QMenuBar can steal the keyboard focus. +// The idea was borrowed from +// http://www.archivum.info/qt-interest@trolltech.com/2006-09/00053/Re-(Qt4)-Alt-key-focus-QMenuBar-(solved).html + +bool GLWidget::eventFilter(QObject*, QEvent* event) { + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::ShortcutOverride: + { + QKeyEvent* keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Alt || keyEvent->key() == Qt::Key_Meta) { + if (event->type() == QEvent::KeyPress) { + keyPressEvent(keyEvent); + } else if (event->type() == QEvent::KeyRelease) { + keyReleaseEvent(keyEvent); + } else { + QGLWidget::event(event); + } + return true; + } + } + default: + break; + } + return false; +} diff --git a/libraries/gl/src/gl/GLWidget.h b/libraries/gl/src/gl/GLWidget.h new file mode 100644 index 0000000000..df4a73ac50 --- /dev/null +++ b/libraries/gl/src/gl/GLWidget.h @@ -0,0 +1,36 @@ +// +// Created by Bradley Austin Davis on 2015/12/03 +// Derived from interface/src/GLCanvas.h created by Stephen Birarda on 8/14/13. +// Copyright 2013-2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_GLWidget_h +#define hifi_GLWidget_h + +#include + +/// customized canvas that simply forwards requests/events to the singleton application +class GLWidget : public QGLWidget { + Q_OBJECT + +public: + GLWidget(); + int getDeviceWidth() const; + int getDeviceHeight() const; + QSize getDeviceSize() const { return QSize(getDeviceWidth(), getDeviceHeight()); } + +protected: + virtual void initializeGL() override; + virtual bool event(QEvent* event) override; + virtual void paintEvent(QPaintEvent* event) override; + virtual void resizeEvent(QResizeEvent* event) override; + +private slots: + virtual bool eventFilter(QObject*, QEvent* event) override; +}; + + +#endif // hifi_GLCanvas_h diff --git a/libraries/gl/src/gl/GlWindow.cpp b/libraries/gl/src/gl/GLWindow.cpp similarity index 69% rename from libraries/gl/src/gl/GlWindow.cpp rename to libraries/gl/src/gl/GLWindow.cpp index 40a5bedf7e..78c7666d25 100644 --- a/libraries/gl/src/gl/GlWindow.cpp +++ b/libraries/gl/src/gl/GLWindow.cpp @@ -6,17 +6,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "GlWindow.h" +#include "GLWindow.h" #include #include #include "GLHelpers.h" -GlWindow::GlWindow(QOpenGLContext* shareContext) : GlWindow(getDefaultOpenGlSurfaceFormat(), shareContext) { +void GLWindow::createContext(QOpenGLContext* shareContext) { + createContext(getDefaultOpenGLSurfaceFormat(), shareContext); } -GlWindow::GlWindow(const QSurfaceFormat& format, QOpenGLContext* shareContext) { +void GLWindow::createContext(const QSurfaceFormat& format, QOpenGLContext* shareContext) { setSurfaceType(QSurface::OpenGLSurface); setFormat(format); _context = new QOpenGLContext; @@ -27,13 +28,15 @@ GlWindow::GlWindow(const QSurfaceFormat& format, QOpenGLContext* shareContext) { _context->create(); } -GlWindow::~GlWindow() { - _context->doneCurrent(); - _context->deleteLater(); - _context = nullptr; +GLWindow::~GLWindow() { + if (_context) { + _context->doneCurrent(); + _context->deleteLater(); + _context = nullptr; + } } -bool GlWindow::makeCurrent() { +bool GLWindow::makeCurrent() { bool makeCurrentResult = _context->makeCurrent(this); Q_ASSERT(makeCurrentResult); @@ -49,11 +52,16 @@ bool GlWindow::makeCurrent() { return makeCurrentResult; } -void GlWindow::doneCurrent() { +void GLWindow::doneCurrent() { _context->doneCurrent(); } -void GlWindow::swapBuffers() { +void GLWindow::swapBuffers() { _context->swapBuffers(this); } +QOpenGLContext* GLWindow::context() const { + return _context; +} + + diff --git a/libraries/gl/src/gl/GlWindow.h b/libraries/gl/src/gl/GLWindow.h similarity index 63% rename from libraries/gl/src/gl/GlWindow.h rename to libraries/gl/src/gl/GLWindow.h index 4956177725..bccfcd7320 100644 --- a/libraries/gl/src/gl/GlWindow.h +++ b/libraries/gl/src/gl/GLWindow.h @@ -7,8 +7,8 @@ // #pragma once -#ifndef hifi_GlWindow_h -#define hifi_GlWindow_h +#ifndef hifi_GLWindow_h +#define hifi_GLWindow_h #include #include @@ -16,14 +16,15 @@ class QOpenGLContext; class QOpenGLDebugLogger; -class GlWindow : public QWindow { +class GLWindow : public QWindow { public: - GlWindow(QOpenGLContext* shareContext = nullptr); - GlWindow(const QSurfaceFormat& format, QOpenGLContext* shareContext = nullptr); - virtual ~GlWindow(); + virtual ~GLWindow(); + void createContext(QOpenGLContext* shareContext = nullptr); + void createContext(const QSurfaceFormat& format, QOpenGLContext* shareContext = nullptr); bool makeCurrent(); void doneCurrent(); void swapBuffers(); + QOpenGLContext* context() const; private: std::once_flag _reportOnce; QOpenGLContext* _context{ nullptr }; diff --git a/libraries/gl/src/gl/OffscreenGlCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp similarity index 84% rename from libraries/gl/src/gl/OffscreenGlCanvas.cpp rename to libraries/gl/src/gl/OffscreenGLCanvas.cpp index e5c1ee4c4a..640c8ed5f5 100644 --- a/libraries/gl/src/gl/OffscreenGlCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -1,5 +1,5 @@ // -// OffscreenGlCanvas.cpp +// OffscreenGLCanvas.cpp // interface/src/renderer // // Created by Bradley Austin Davis on 2014/04/09. @@ -10,7 +10,7 @@ // -#include "OffscreenGlCanvas.h" +#include "OffscreenGLCanvas.h" #include #include @@ -18,10 +18,10 @@ #include "GLHelpers.h" -OffscreenGlCanvas::OffscreenGlCanvas() : _context(new QOpenGLContext), _offscreenSurface(new QOffscreenSurface){ +OffscreenGLCanvas::OffscreenGLCanvas() : _context(new QOpenGLContext), _offscreenSurface(new QOffscreenSurface){ } -OffscreenGlCanvas::~OffscreenGlCanvas() { +OffscreenGLCanvas::~OffscreenGLCanvas() { #ifdef DEBUG if (_logger) { makeCurrent(); @@ -32,12 +32,12 @@ OffscreenGlCanvas::~OffscreenGlCanvas() { _context->doneCurrent(); } -void OffscreenGlCanvas::create(QOpenGLContext* sharedContext) { +void OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { if (nullptr != sharedContext) { sharedContext->doneCurrent(); _context->setShareContext(sharedContext); } - _context->setFormat(getDefaultOpenGlSurfaceFormat()); + _context->setFormat(getDefaultOpenGLSurfaceFormat()); _context->create(); _offscreenSurface->setFormat(_context->format()); @@ -45,7 +45,7 @@ void OffscreenGlCanvas::create(QOpenGLContext* sharedContext) { } -bool OffscreenGlCanvas::makeCurrent() { +bool OffscreenGLCanvas::makeCurrent() { bool result = _context->makeCurrent(_offscreenSurface); Q_ASSERT(result); @@ -72,7 +72,7 @@ bool OffscreenGlCanvas::makeCurrent() { return result; } -void OffscreenGlCanvas::doneCurrent() { +void OffscreenGLCanvas::doneCurrent() { _context->doneCurrent(); } diff --git a/libraries/gl/src/gl/OffscreenGlCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h similarity index 77% rename from libraries/gl/src/gl/OffscreenGlCanvas.h rename to libraries/gl/src/gl/OffscreenGLCanvas.h index 94014adf98..e278f550f0 100644 --- a/libraries/gl/src/gl/OffscreenGlCanvas.h +++ b/libraries/gl/src/gl/OffscreenGLCanvas.h @@ -1,5 +1,5 @@ // -// OffscreenGlCanvas.h +// OffscreenGLCanvas.h // interface/src/renderer // // Created by Bradley Austin Davis on 2014/04/09. @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #pragma once -#ifndef hifi_OffscreenGlCanvas_h -#define hifi_OffscreenGlCanvas_h +#ifndef hifi_OffscreenGLCanvas_h +#define hifi_OffscreenGLCanvas_h #include #include @@ -19,10 +19,10 @@ class QOpenGLContext; class QOffscreenSurface; class QOpenGLDebugLogger; -class OffscreenGlCanvas : public QObject { +class OffscreenGLCanvas : public QObject { public: - OffscreenGlCanvas(); - ~OffscreenGlCanvas(); + OffscreenGLCanvas(); + ~OffscreenGLCanvas(); void create(QOpenGLContext* sharedContext = nullptr); bool makeCurrent(); void doneCurrent(); @@ -40,4 +40,4 @@ protected: }; -#endif // hifi_OffscreenGlCanvas_h +#endif // hifi_OffscreenGLCanvas_h diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index e8a950a16b..f2eb112dc7 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -23,7 +23,7 @@ #include #include "GLEscrow.h" -#include "OffscreenGlCanvas.h" +#include "OffscreenGLCanvas.h" // FIXME move to threaded rendering with Qt 5.5 //#define QML_THREADED @@ -64,12 +64,12 @@ static const QEvent::Type STOP = QEvent::Type(QEvent::User + 4); static const QEvent::Type UPDATE = QEvent::Type(QEvent::User + 5); #endif -class OffscreenQmlRenderer : public OffscreenGlCanvas { +class OffscreenQmlRenderer : public OffscreenGLCanvas { friend class OffscreenQmlSurface; public: OffscreenQmlRenderer(OffscreenQmlSurface* surface, QOpenGLContext* shareContext) : _surface(surface) { - OffscreenGlCanvas::create(shareContext); + OffscreenGLCanvas::create(shareContext); #ifdef QML_THREADED // Qt 5.5 _renderControl->prepareThread(_renderThread); diff --git a/libraries/octree/src/CoverageMap.cpp b/libraries/octree/src/CoverageMap.cpp deleted file mode 100644 index 626d4bcf1a..0000000000 --- a/libraries/octree/src/CoverageMap.cpp +++ /dev/null @@ -1,542 +0,0 @@ -// -// CoverageMap.cpp -// libraries/octree/src -// -// Created by Brad Hefta-Gaub on 06/11/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include - -#include - -#include "OctreeLogging.h" -#include "CoverageMap.h" - -int CoverageMap::_mapCount = 0; -int CoverageMap::_checkMapRootCalls = 0; -int CoverageMap::_notAllInView = 0; -bool CoverageMap::wantDebugging = false; - -const int MAX_POLYGONS_PER_REGION = 50; - -const BoundingBox CoverageMap::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.0f,-1.0f), glm::vec2(2.0f,2.0f)); - -// Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space. -// -// (0,0) (windowWidth, 0) -// -1,1 1,1 -// +-----------------------+ -// | | | -// | | | -// | -1,0 | | -// |-----------+-----------| -// | 0,0 | -// | | | -// | | | -// | | | -// +-----------------------+ -// -1,-1 1,-1 -// (0,windowHeight) (windowWidth,windowHeight) -// - -// Choosing a minimum sized polygon. Since we know a typical window is approximately 1500 pixels wide -// then a pixel on our screen will be ~ 2.0/1500 or 0.0013 "units" wide, similarly pixels are typically -// about that tall as well. If we say that polygons should be at least 10x10 pixels to be considered "big enough" -// then we can calculate a reasonable polygon area -const int TYPICAL_SCREEN_WIDTH_IN_PIXELS = 1500; -const int MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS = 10; -const float TYPICAL_SCREEN_PIXEL_WIDTH = (2.0f / TYPICAL_SCREEN_WIDTH_IN_PIXELS); -const float CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE = (TYPICAL_SCREEN_PIXEL_WIDTH * MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS) * - (TYPICAL_SCREEN_PIXEL_WIDTH * MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS); - -CoverageMap::CoverageMap(BoundingBox boundingBox, bool isRoot, bool managePolygons) : - _isRoot(isRoot), - _myBoundingBox(boundingBox), - _managePolygons(managePolygons), - _topHalf (boundingBox.topHalf() , false, managePolygons, TOP_HALF ), - _bottomHalf (boundingBox.bottomHalf(), false, managePolygons, BOTTOM_HALF ), - _leftHalf (boundingBox.leftHalf() , false, managePolygons, LEFT_HALF ), - _rightHalf (boundingBox.rightHalf() , false, managePolygons, RIGHT_HALF ), - _remainder (boundingBox, isRoot, managePolygons, REMAINDER ) -{ - _mapCount++; - init(); -}; - -CoverageMap::~CoverageMap() { - erase(); -}; - -void CoverageMap::printStats() { - qCDebug(octree, "CoverageMap::printStats()..."); - qCDebug(octree, "MINIMUM_POLYGON_AREA_TO_STORE=%f", (double)MINIMUM_POLYGON_AREA_TO_STORE); - qCDebug(octree, "_mapCount=%d",_mapCount); - qCDebug(octree, "_checkMapRootCalls=%d",_checkMapRootCalls); - qCDebug(octree, "_notAllInView=%d",_notAllInView); - qCDebug(octree, "_maxPolygonsUsed=%d",CoverageRegion::_maxPolygonsUsed); - qCDebug(octree, "_totalPolygons=%d",CoverageRegion::_totalPolygons); - qCDebug(octree, "_occlusionTests=%d",CoverageRegion::_occlusionTests); - qCDebug(octree, "_regionSkips=%d",CoverageRegion::_regionSkips); - qCDebug(octree, "_tooSmallSkips=%d",CoverageRegion::_tooSmallSkips); - qCDebug(octree, "_regionFullSkips=%d",CoverageRegion::_regionFullSkips); - qCDebug(octree, "_outOfOrderPolygon=%d",CoverageRegion::_outOfOrderPolygon); - qCDebug(octree, "_clippedPolygons=%d",CoverageRegion::_clippedPolygons); -} - -void CoverageMap::erase() { - // tell our regions to erase() - _topHalf.erase(); - _bottomHalf.erase(); - _leftHalf.erase(); - _rightHalf.erase(); - _remainder.erase(); - - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (_childMaps[i]) { - delete _childMaps[i]; - _childMaps[i] = NULL; - } - } - - if (_isRoot && wantDebugging) { - qCDebug(octree, "CoverageMap last to be deleted..."); - printStats(); - - CoverageRegion::_maxPolygonsUsed = 0; - CoverageRegion::_totalPolygons = 0; - CoverageRegion::_occlusionTests = 0; - CoverageRegion::_regionSkips = 0; - CoverageRegion::_tooSmallSkips = 0; - CoverageRegion::_regionFullSkips = 0; - CoverageRegion::_outOfOrderPolygon = 0; - CoverageRegion::_clippedPolygons = 0; - _mapCount = 0; - _checkMapRootCalls = 0; - _notAllInView = 0; - } -} - -void CoverageMap::init() { - memset(_childMaps,0,sizeof(_childMaps)); -} - -// 0 = bottom, right -// 1 = bottom, left -// 2 = top, right -// 3 = top, left -BoundingBox CoverageMap::getChildBoundingBox(int childIndex) { - const int LEFT_BIT = 1; - const int TOP_BIT = 2; - // initialize to our corner, and half our size - BoundingBox result(_myBoundingBox.corner,_myBoundingBox.size/2.0f); - // if our "left" bit is set, then add size.x to the corner - if ((childIndex & LEFT_BIT) == LEFT_BIT) { - result.corner.x += result.size.x; - } - // if our "top" bit is set, then add size.y to the corner - if ((childIndex & TOP_BIT) == TOP_BIT) { - result.corner.y += result.size.y; - } - return result; -} - -int CoverageMap::getPolygonCount() const { - return (_topHalf.getPolygonCount() + - _bottomHalf.getPolygonCount() + - _leftHalf.getPolygonCount() + - _rightHalf.getPolygonCount() + - _remainder.getPolygonCount()); -} - -OctreeProjectedPolygon* CoverageMap::getPolygon(int index) const { - int base = 0; - if ((index - base) < _topHalf.getPolygonCount()) { - return _topHalf.getPolygon((index - base)); - } - base += _topHalf.getPolygonCount(); - - if ((index - base) < _bottomHalf.getPolygonCount()) { - return _bottomHalf.getPolygon((index - base)); - } - base += _bottomHalf.getPolygonCount(); - - if ((index - base) < _leftHalf.getPolygonCount()) { - return _leftHalf.getPolygon((index - base)); - } - base += _leftHalf.getPolygonCount(); - - if ((index - base) < _rightHalf.getPolygonCount()) { - return _rightHalf.getPolygon((index - base)); - } - base += _rightHalf.getPolygonCount(); - - if ((index - base) < _remainder.getPolygonCount()) { - return _remainder.getPolygon((index - base)); - } - return NULL; -} - - - -// possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT -CoverageMapStorageResult CoverageMap::checkMap(OctreeProjectedPolygon* polygon, bool storeIt) { - - if (_isRoot) { - _checkMapRootCalls++; - } - - // short circuit: we don't handle polygons that aren't all in view, so, if the polygon in question is - // not in view, then we just discard it with a DOESNT_FIT, this saves us time checking values later. - if (!polygon->getAllInView()) { - _notAllInView++; - return DOESNT_FIT; - } - - BoundingBox polygonBox(polygon->getBoundingBox()); - if (_isRoot || _myBoundingBox.contains(polygonBox)) { - - CoverageMapStorageResult result = NOT_STORED; - CoverageRegion* storeIn = &_remainder; - - // Check each half of the box independently - const bool useRegions = true; // for now we will continue to use regions - if (useRegions) { - if (_topHalf.contains(polygonBox)) { - result = _topHalf.checkRegion(polygon, polygonBox, storeIt); - storeIn = &_topHalf; - } else if (_bottomHalf.contains(polygonBox)) { - result = _bottomHalf.checkRegion(polygon, polygonBox, storeIt); - storeIn = &_bottomHalf; - } else if (_leftHalf.contains(polygonBox)) { - result = _leftHalf.checkRegion(polygon, polygonBox, storeIt); - storeIn = &_leftHalf; - } else if (_rightHalf.contains(polygonBox)) { - result = _rightHalf.checkRegion(polygon, polygonBox, storeIt); - storeIn = &_rightHalf; - } - } - - // if we got this far, there are one of two possibilities, either a polygon doesn't fit - // in one of the halves, or it did fit, but it wasn't occluded by anything only in that - // half. In either of these cases, we want to check our remainder region to see if its - // occluded by anything there - if (!(result == STORED || result == OCCLUDED)) { - result = _remainder.checkRegion(polygon, polygonBox, storeIt); - } - - // It's possible that this first set of checks might have resulted in an out of order polygon - // in which case we just return.. - if (result == STORED || result == OCCLUDED) { - - /* - if (result == STORED) - qCDebug(octree, "CoverageMap2::checkMap()... STORED\n"); - else - qCDebug(octree, "CoverageMap2::checkMap()... OCCLUDED\n"); - */ - - return result; - } - - // if we made it here, then it means the polygon being stored is not occluded - // at this level of the quad tree, so we can continue to insert it into the map. - // First we check to see if it fits in any of our sub maps - const bool useChildMaps = true; // for now we will continue to use child maps - if (useChildMaps) { - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - BoundingBox childMapBoundingBox = getChildBoundingBox(i); - if (childMapBoundingBox.contains(polygon->getBoundingBox())) { - // if no child map exists yet, then create it - if (!_childMaps[i]) { - _childMaps[i] = new CoverageMap(childMapBoundingBox, NOT_ROOT, _managePolygons); - } - result = _childMaps[i]->checkMap(polygon, storeIt); - - /* - switch (result) { - case STORED: - qCDebug(octree, "checkMap() = STORED\n"); - break; - case NOT_STORED: - qCDebug(octree, "checkMap() = NOT_STORED\n"); - break; - case OCCLUDED: - qCDebug(octree, "checkMap() = OCCLUDED\n"); - break; - default: - qCDebug(octree, "checkMap() = ????? \n"); - break; - } - */ - - return result; - } - } - } - // if we got this far, then the polygon is in our bounding box, but doesn't fit in - // any of our child bounding boxes, so we should add it here. - if (storeIt) { - if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { - if (storeIn->getPolygonCount() < MAX_POLYGONS_PER_REGION) { - storeIn->storeInArray(polygon); - return STORED; - } else { - CoverageRegion::_regionFullSkips++; - return NOT_STORED; - } - } else { - CoverageRegion::_tooSmallSkips++; - return NOT_STORED; - } - } else { - return NOT_STORED; - } - } - return DOESNT_FIT; -} - - -CoverageRegion::CoverageRegion(BoundingBox boundingBox, bool isRoot, bool managePolygons, RegionName regionName) : - _isRoot(isRoot), - _myBoundingBox(boundingBox), - _managePolygons(managePolygons), - _regionName(regionName) -{ - init(); -}; - -CoverageRegion::~CoverageRegion() { - erase(); -}; - -void CoverageRegion::init() { - _polygonCount = 0; - _polygonArraySize = 0; - _polygons = NULL; - _polygonDistances = NULL; - _polygonSizes = NULL; -} - - -void CoverageRegion::erase() { - -/** - if (_polygonCount) { - qCDebug(octree, "CoverageRegion::erase()...\n"); - qCDebug(octree, "_polygonCount=%d\n",_polygonCount); - _myBoundingBox.printDebugDetails(getRegionName()); - //for (int i = 0; i < _polygonCount; i++) { - // qCDebug(octree, "_polygons[%d]=",i); - // _polygons[i]->getBoundingBox().printDebugDetails(); - //} - } -**/ - // If we're in charge of managing the polygons, then clean them up first - if (_polygons && _managePolygons) { - for (int i = 0; i < _polygonCount; i++) { - delete _polygons[i]; - _polygons[i] = NULL; // do we need to do this? - } - } - - // Now, clean up our local storage - _polygonCount = 0; - _polygonArraySize = 0; - if (_polygons) { - delete[] _polygons; - _polygons = NULL; - } - if (_polygonDistances) { - delete[] _polygonDistances; - _polygonDistances = NULL; - } - if (_polygonSizes) { - delete[] _polygonSizes; - _polygonSizes = NULL; - } -} - -void CoverageRegion::growPolygonArray() { - OctreeProjectedPolygon** newPolygons = new OctreeProjectedPolygon*[_polygonArraySize + DEFAULT_GROW_SIZE]; - float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE]; - float* newSizes = new float[_polygonArraySize + DEFAULT_GROW_SIZE]; - - - if (_polygons) { - memcpy(newPolygons, _polygons, sizeof(OctreeProjectedPolygon*) * _polygonCount); - delete[] _polygons; - memcpy(newDistances, _polygonDistances, sizeof(float) * _polygonCount); - delete[] _polygonDistances; - memcpy(newSizes, _polygonSizes, sizeof(float) * _polygonCount); - delete[] _polygonSizes; - } - _polygons = newPolygons; - _polygonDistances = newDistances; - _polygonSizes = newSizes; - _polygonArraySize = _polygonArraySize + DEFAULT_GROW_SIZE; -} - -const char* CoverageRegion::getRegionName() const { - switch (_regionName) { - case TOP_HALF: - return "TOP_HALF"; - case BOTTOM_HALF: - return "BOTTOM_HALF"; - case LEFT_HALF: - return "LEFT_HALF"; - case RIGHT_HALF: - return "RIGHT_HALF"; - default: - case REMAINDER: - return "REMAINDER"; - } - return "REMAINDER"; -} - -int CoverageRegion::_maxPolygonsUsed = 0; -int CoverageRegion::_totalPolygons = 0; -int CoverageRegion::_occlusionTests = 0; -int CoverageRegion::_regionSkips = 0; -int CoverageRegion::_tooSmallSkips = 0; -int CoverageRegion::_regionFullSkips = 0; -int CoverageRegion::_outOfOrderPolygon = 0; -int CoverageRegion::_clippedPolygons = 0; - - -bool CoverageRegion::mergeItemsInArray(OctreeProjectedPolygon* seed, bool seedInArray) { - for (int i = 0; i < _polygonCount; i++) { - OctreeProjectedPolygon* otherPolygon = _polygons[i]; - if (otherPolygon->canMerge(*seed)) { - otherPolygon->merge(*seed); - - if (seedInArray) { - int* IGNORED_ADDRESS = NULL; - // remove this otherOtherPolygon for our polygon array - _polygonCount = removeFromSortedArrays((void*)seed, - (void**)_polygons, _polygonDistances, IGNORED_ADDRESS, - _polygonCount, _polygonArraySize); - _totalPolygons--; - } - - // clean up - if (_managePolygons) { - delete seed; - } - - // Now run again using our newly merged polygon as the seed - mergeItemsInArray(otherPolygon, true); - - return true; - } - } - return false; -} - -// just handles storage in the array, doesn't test for occlusion or -// determining if this is the correct map to store in! -void CoverageRegion::storeInArray(OctreeProjectedPolygon* polygon) { - - _currentCoveredBounds.explandToInclude(polygon->getBoundingBox()); - - - // Before we actually store this polygon in the array, check to see if this polygon can be merged to any of the existing - // polygons already in our array. - if (mergeItemsInArray(polygon, false)) { - return; // exit early - } - - // only after we attempt to merge! - _totalPolygons++; - - if (_polygonArraySize < _polygonCount + 1) { - growPolygonArray(); - } - - // As an experiment we're going to see if we get an improvement by storing the polygons in coverage area sorted order - // this means the bigger polygons are earlier in the array. We should have a higher probability of being occluded earlier - // in the list. We still check to see if the polygon is "in front" of the target polygon before we test occlusion. Since - // sometimes things come out of order. - const bool SORT_BY_SIZE = false; - const int IGNORED = 0; - int* IGNORED_ADDRESS = NULL; - if (SORT_BY_SIZE) { - // This old code assumes that polygons will always be added in z-buffer order, but that doesn't seem to - // be a good assumption. So instead, we will need to sort this by distance. Use a binary search to find the - // insertion point in this array, and shift the array accordingly - float area = polygon->getBoundingBox().area(); - float reverseArea = 4.0f - area; - _polygonCount = insertIntoSortedArrays((void*)polygon, reverseArea, IGNORED, - (void**)_polygons, _polygonSizes, IGNORED_ADDRESS, - _polygonCount, _polygonArraySize); - } else { - _polygonCount = insertIntoSortedArrays((void*)polygon, polygon->getDistance(), IGNORED, - (void**)_polygons, _polygonDistances, IGNORED_ADDRESS, - _polygonCount, _polygonArraySize); - } - - // Debugging and Optimization Tuning code. - if (_polygonCount > _maxPolygonsUsed) { - _maxPolygonsUsed = _polygonCount; - } -} - - - -CoverageMapStorageResult CoverageRegion::checkRegion(OctreeProjectedPolygon* polygon, const BoundingBox& polygonBox, bool storeIt) { - - CoverageMapStorageResult result = DOESNT_FIT; - - if (_isRoot || _myBoundingBox.contains(polygonBox)) { - result = NOT_STORED; // if we got here, then we DO fit... - - // only actually check the polygons if this polygon is in the covered bounds for this region - if (!_currentCoveredBounds.contains(polygonBox)) { - _regionSkips += _polygonCount; - } else { - // check to make sure this polygon isn't occluded by something at this level - for (int i = 0; i < _polygonCount; i++) { - OctreeProjectedPolygon* polygonAtThisLevel = _polygons[i]; - - // Check to make sure that the polygon in question is "behind" the polygon in the list - // otherwise, we don't need to test it's occlusion (although, it means we've potentially - // added an item previously that may be occluded??? Is that possible? Maybe not, because two - // voxels can't have the exact same outline. So one occludes the other, they can't both occlude - // each other. - - _occlusionTests++; - if (polygonAtThisLevel->occludes(*polygon)) { - // if the polygonAtThisLevel is actually behind the one we're inserting, then we don't - // want to report our inserted one as occluded, but we do want to add our inserted one. - if (polygonAtThisLevel->getDistance() >= polygon->getDistance()) { - _outOfOrderPolygon++; - if (storeIt) { - if (polygon->getBoundingBox().area() > CoverageMap::MINIMUM_POLYGON_AREA_TO_STORE) { - if (getPolygonCount() < MAX_POLYGONS_PER_REGION) { - storeInArray(polygon); - return STORED; - } else { - CoverageRegion::_regionFullSkips++; - return NOT_STORED; - } - } else { - _tooSmallSkips++; - return NOT_STORED; - } - } else { - return NOT_STORED; - } - } - // this polygon is occluded by a closer polygon, so don't store it, and let the caller know - return OCCLUDED; - } - } - } - } - return result; -} diff --git a/libraries/octree/src/CoverageMap.h b/libraries/octree/src/CoverageMap.h deleted file mode 100644 index bff6bb1078..0000000000 --- a/libraries/octree/src/CoverageMap.h +++ /dev/null @@ -1,120 +0,0 @@ -// -// CoverageMap.h -// libraries/octree/src -// -// Created by Brad Hefta-Gaub on 06/11/13. -// Copyright 2013 High Fidelity, Inc. -// -// 2D CoverageMap Quad tree for storage of OctreeProjectedPolygons -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CoverageMap_h -#define hifi_CoverageMap_h - -#include -#include "OctreeProjectedPolygon.h" - -typedef enum {STORED, OCCLUDED, DOESNT_FIT, NOT_STORED} CoverageMapStorageResult; -typedef enum {TOP_HALF, BOTTOM_HALF, LEFT_HALF, RIGHT_HALF, REMAINDER} RegionName; - -class CoverageRegion { - -public: - - CoverageRegion(BoundingBox boundingBox, bool isRoot, bool managePolygons = true, RegionName regionName = REMAINDER); - ~CoverageRegion(); - - CoverageMapStorageResult checkRegion(OctreeProjectedPolygon* polygon, const BoundingBox& polygonBox, bool storeIt); - void storeInArray(OctreeProjectedPolygon* polygon); - - bool contains(const BoundingBox& box) const { return _myBoundingBox.contains(box); }; - void erase(); // erase the coverage region - - static int _maxPolygonsUsed; - static int _totalPolygons; - static int _occlusionTests; - static int _regionSkips; - static int _tooSmallSkips; - static int _regionFullSkips; - static int _outOfOrderPolygon; - static int _clippedPolygons; - - - const char* getRegionName() const; - - int getPolygonCount() const { return _polygonCount; }; - OctreeProjectedPolygon* getPolygon(int index) const { return _polygons[index]; }; - -private: - void init(); - - bool _isRoot; // is this map the root, if so, it never returns DOESNT_FIT - BoundingBox _myBoundingBox; - BoundingBox _currentCoveredBounds; // area in this region currently covered by some polygon - bool _managePolygons; // will the coverage map delete the polygons on destruct - RegionName _regionName; - int _polygonCount; // how many polygons at this level - int _polygonArraySize; // how much room is there to store polygons at this level - OctreeProjectedPolygon** _polygons; - - // we will use one or the other of these depending on settings in the code. - float* _polygonDistances; - float* _polygonSizes; - void growPolygonArray(); - static const int DEFAULT_GROW_SIZE = 100; - - bool mergeItemsInArray(OctreeProjectedPolygon* seed, bool seedInArray); - -}; - -class CoverageMap { - -public: - static const int NUMBER_OF_CHILDREN = 4; - static const bool NOT_ROOT=false; - static const bool IS_ROOT=true; - static const BoundingBox ROOT_BOUNDING_BOX; - static const float MINIMUM_POLYGON_AREA_TO_STORE; - - CoverageMap(BoundingBox boundingBox = ROOT_BOUNDING_BOX, bool isRoot = IS_ROOT, bool managePolygons = true); - ~CoverageMap(); - - CoverageMapStorageResult checkMap(OctreeProjectedPolygon* polygon, bool storeIt = true); - - BoundingBox getChildBoundingBox(int childIndex); - - void erase(); // erase the coverage map - void printStats(); - - static bool wantDebugging; - - int getPolygonCount() const; - OctreeProjectedPolygon* getPolygon(int index) const; - CoverageMap* getChild(int childIndex) const { return _childMaps[childIndex]; }; - -private: - void init(); - - bool _isRoot; // is this map the root, if so, it never returns DOESNT_FIT - BoundingBox _myBoundingBox; - CoverageMap* _childMaps[NUMBER_OF_CHILDREN]; - bool _managePolygons; // will the coverage map delete the polygons on destruct - - // We divide the map into 5 regions representing each possible half of the map, and the whole map - // this allows us to keep the list of polygons shorter - CoverageRegion _topHalf; - CoverageRegion _bottomHalf; - CoverageRegion _leftHalf; - CoverageRegion _rightHalf; - CoverageRegion _remainder; - - static int _mapCount; - static int _checkMapRootCalls; - static int _notAllInView; -}; - - -#endif // hifi_CoverageMap_h diff --git a/libraries/octree/src/CoverageMapV2.cpp b/libraries/octree/src/CoverageMapV2.cpp deleted file mode 100644 index 8467ea1ee9..0000000000 --- a/libraries/octree/src/CoverageMapV2.cpp +++ /dev/null @@ -1,251 +0,0 @@ -// -// CoverageMapV2.cpp -// libraries/octree/src -// -// Created by Brad Hefta-Gaub on 06/11/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include - -#include - -#include - -#include "OctreeLogging.h" -#include "CoverageMapV2.h" - -int CoverageMapV2::_mapCount = 0; -int CoverageMapV2::_checkMapRootCalls = 0; -int CoverageMapV2::_notAllInView = 0; -bool CoverageMapV2::wantDebugging = false; - -const BoundingBox CoverageMapV2::ROOT_BOUNDING_BOX = BoundingBox(glm::vec2(-1.0f,-1.0f), glm::vec2(2.0f,2.0f)); - -// Coverage Map's polygon coordinates are from -1 to 1 in the following mapping to screen space. -// -// (0,0) (windowWidth, 0) -// -1,1 1,1 -// +-----------------------+ -// | | | -// | | | -// | -1,0 | | -// |-----------+-----------| -// | 0,0 | -// | | | -// | | | -// | | | -// +-----------------------+ -// -1,-1 1,-1 -// (0,windowHeight) (windowWidth,windowHeight) -// - -// Choosing a minimum sized polygon. Since we know a typical window is approximately 1500 pixels wide -// then a pixel on our screen will be ~ 2.0/1500 or 0.0013 "units" wide, similarly pixels are typically -// about that tall as well. If we say that polygons should be at least 10x10 pixels to be considered "big enough" -// then we can calculate a reasonable polygon area -const int TYPICAL_SCREEN_WIDTH_IN_PIXELS = 1500; -const int MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS = 10; -const float TYPICAL_SCREEN_PIXEL_WIDTH = (2.0f / TYPICAL_SCREEN_WIDTH_IN_PIXELS); -const float CoverageMapV2::MINIMUM_POLYGON_AREA_TO_STORE = (TYPICAL_SCREEN_PIXEL_WIDTH * MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS) * - (TYPICAL_SCREEN_PIXEL_WIDTH * MINIMUM_POLYGON_AREA_SIDE_IN_PIXELS); -const float CoverageMapV2::NOT_COVERED = FLT_MAX; -const float CoverageMapV2::MINIMUM_OCCLUSION_CHECK_AREA = MINIMUM_POLYGON_AREA_TO_STORE/10.0f; // one quarter the size of poly - - -CoverageMapV2::CoverageMapV2(BoundingBox boundingBox, bool isRoot, bool isCovered, float coverageDistance) : - _isRoot(isRoot), - _myBoundingBox(boundingBox), - _isCovered(isCovered), - _coveredDistance(coverageDistance) -{ - _mapCount++; - init(); -}; - -CoverageMapV2::~CoverageMapV2() { - erase(); -}; - -void CoverageMapV2::erase() { - - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (_childMaps[i]) { - delete _childMaps[i]; - _childMaps[i] = NULL; - } - } - - if (_isRoot && wantDebugging) { - qCDebug(octree, "CoverageMapV2 last to be deleted..."); - qCDebug(octree, "MINIMUM_POLYGON_AREA_TO_STORE=%f", (double)MINIMUM_POLYGON_AREA_TO_STORE); - qCDebug(octree, "_mapCount=%d",_mapCount); - qCDebug(octree, "_checkMapRootCalls=%d",_checkMapRootCalls); - qCDebug(octree, "_notAllInView=%d",_notAllInView); - _mapCount = 0; - _checkMapRootCalls = 0; - _notAllInView = 0; - } -} - -void CoverageMapV2::init() { - memset(_childMaps,0,sizeof(_childMaps)); -} - -// 0 = bottom, left -// 1 = bottom, right -// 2 = top, left -// 3 = top, right -BoundingBox CoverageMapV2::getChildBoundingBox(int childIndex) { - const int RIGHT_BIT = 1; - const int TOP_BIT = 2; - // initialize to our corner, and half our size - BoundingBox result(_myBoundingBox.corner,_myBoundingBox.size/2.0f); - // if our "right" bit is set, then add size.x to the corner - if ((childIndex & RIGHT_BIT) == RIGHT_BIT) { - result.corner.x += result.size.x; - } - // if our "top" bit is set, then add size.y to the corner - if ((childIndex & TOP_BIT) == TOP_BIT) { - result.corner.y += result.size.y; - } - return result; -} - -// possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT -CoverageMapV2StorageResult CoverageMapV2::checkMap(const OctreeProjectedPolygon* polygon, bool storeIt) { - assert(_isRoot); // you can only call this on the root map!!! - _checkMapRootCalls++; - - // short circuit: if we're the root node (only case we're here), and we're covered, and this polygon is deeper than our - // covered depth, then this polygon is occluded! - if (_isCovered && _coveredDistance < polygon->getDistance()) { - return V2_OCCLUDED; - } - - // short circuit: we don't handle polygons that aren't all in view, so, if the polygon in question is - // not in view, then we just discard it with a DOESNT_FIT, this saves us time checking values later. - if (!polygon->getAllInView()) { - _notAllInView++; - return V2_DOESNT_FIT; - } - - // Here's where we recursively check the polygon against the coverage map. We need to maintain two pieces of state. - // The first state is: have we seen at least one "fully occluded" map items. If we haven't then we don't track the covered - // state of the polygon. - // The second piece of state is: Are all of our "fully occluded" map items "covered". If even one of these occluded map - // items is not covered, then our polygon is not covered. - bool seenOccludedMapNodes = false; - bool allOccludedMapNodesCovered = false; - - recurseMap(polygon, storeIt, seenOccludedMapNodes, allOccludedMapNodesCovered); - - // Ok, no matter how we were called, if all our occluded map nodes are covered, then we know this polygon - // is occluded, otherwise, we will report back to the caller about whether or not we stored the polygon - if (allOccludedMapNodesCovered) { - return V2_OCCLUDED; - } - if (storeIt) { - return V2_STORED; // otherwise report that we STORED it - } - return V2_NOT_STORED; // unless we weren't asked to store it, then we didn't -} - -void CoverageMapV2::recurseMap(const OctreeProjectedPolygon* polygon, bool storeIt, - bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered) { - - // if we are really small, then we act like we don't intersect, this allows us to stop - // recusing as we get to the smalles edge of the polygon - if (_myBoundingBox.area() < MINIMUM_OCCLUSION_CHECK_AREA) { - return; // stop recursion, we're done! - } - - // Determine if this map node intersects the polygon and/or is fully covered by the polygon - // There are a couple special cases: If we're the root, we are assumed to intersect with all - // polygons. Also, any map node that is fully occluded also intersects. - bool nodeIsCoveredByPolygon = polygon->occludes(_myBoundingBox); - bool nodeIsIntersectedByPolygon = nodeIsCoveredByPolygon || _isRoot || polygon->intersects(_myBoundingBox); - - // If we don't intersect, then we can just return, we're done recursing - if (!nodeIsIntersectedByPolygon) { - return; // stop recursion, we're done! - } - - // At this point, we know our node intersects with the polygon. If this node is covered, then we want to treat it - // as if the node was fully covered, because this allows us to short circuit further recursion... - if (_isCovered && _coveredDistance < polygon->getDistance()) { - nodeIsCoveredByPolygon = true; // fake it till you make it - } - - // If this node in the map is fully covered by our polygon, then we don't need to recurse any further, but - // we do need to do some bookkeeping. - if (nodeIsCoveredByPolygon) { - // If this is the very first fully covered node we've seen, then we're initialize our allOccludedMapNodesCovered - // to be our current covered state. This has the following effect: if this node isn't already covered, then by - // definition, we know that at least one node for this polygon isn't covered, and therefore we aren't fully covered. - if (!seenOccludedMapNodes) { - allOccludedMapNodesCovered = (_isCovered && _coveredDistance < polygon->getDistance()); - // We need to mark that we've seen at least one node of our polygon! ;) - seenOccludedMapNodes = true; - } else { - // If this is our second or later node of our polygon, then we need to track our allOccludedMapNodesCovered state - allOccludedMapNodesCovered = allOccludedMapNodesCovered && - (_isCovered && _coveredDistance < polygon->getDistance()); - } - - // if we're in store mode then we want to record that this node is covered. - if (storeIt) { - _isCovered = true; - // store the minimum distance of our previous known distance, or our current polygon's distance. This is because - // we know that we're at least covered at this distance, but if we had previously identified that we're covered - // at a shallower distance, then we want to maintain that distance - _coveredDistance = std::min(polygon->getDistance(), _coveredDistance); - - // Note: this might be a good chance to delete child maps, but we're not going to do that at this point because - // we're trying to maintain the known distances in the lower portion of the tree. - } - - // and since this node of the quad map is covered, we can safely stop recursion. because we know all smaller map - // nodes will also be covered. - return; - } - - // If we got here, then it means we know that this node is not fully covered by the polygon, but it does intersect - // with the polygon. - - // Another case is that we aren't yet marked as covered, and so we should recurse and process smaller quad tree nodes. - // Note: we use this to determine if we can collapse the child quad trees and mark this node as covered - bool allChildrenOccluded = true; - float maxChildCoveredDepth = NOT_COVERED; - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - BoundingBox childMapBoundingBox = getChildBoundingBox(i); - // if no child map exists yet, then create it - if (!_childMaps[i]) { - // children get created with the coverage state of their parent. - _childMaps[i] = new CoverageMapV2(childMapBoundingBox, NOT_ROOT, _isCovered, _coveredDistance); - } - - _childMaps[i]->recurseMap(polygon, storeIt, seenOccludedMapNodes, allOccludedMapNodesCovered); - - // if so far, all of our children are covered, then record our furthest coverage distance - if (allChildrenOccluded && _childMaps[i]->_isCovered) { - maxChildCoveredDepth = std::max(maxChildCoveredDepth, _childMaps[i]->_coveredDistance); - } else { - // otherwise, at least one of our children is not covered, so not all are covered - allChildrenOccluded = false; - } - } - // if all the children are covered, this makes our quad tree "shallower" because it records that - // entire quad is covered, it uses the "furthest" z-order so that if a shalower polygon comes through - // we won't assume its occluded - if (allChildrenOccluded && storeIt) { - _isCovered = true; - _coveredDistance = maxChildCoveredDepth; - } - - // normal exit case... return... -} diff --git a/libraries/octree/src/CoverageMapV2.h b/libraries/octree/src/CoverageMapV2.h deleted file mode 100644 index fc9a3ea70e..0000000000 --- a/libraries/octree/src/CoverageMapV2.h +++ /dev/null @@ -1,72 +0,0 @@ -// -// CoverageMapV2.h -// libraries/octree/src -// -// Created by Brad Hefta-Gaub on 06/11/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CoverageMapV2_h -#define hifi_CoverageMapV2_h - -#include - -#include "OctreeProjectedPolygon.h" - -typedef enum { - V2_DOESNT_FIT, V2_STORED, V2_NOT_STORED, - V2_INTERSECT, V2_NO_INTERSECT, - V2_OCCLUDED, V2_NOT_OCCLUDED -} CoverageMapV2StorageResult; - -class CoverageMapV2 { - -public: - static const int NUMBER_OF_CHILDREN = 4; - static const bool NOT_ROOT = false; - static const bool IS_ROOT = true; - static const BoundingBox ROOT_BOUNDING_BOX; - static const float MINIMUM_POLYGON_AREA_TO_STORE; - static const float NOT_COVERED; - static const float MINIMUM_OCCLUSION_CHECK_AREA; - static bool wantDebugging; - - CoverageMapV2(BoundingBox boundingBox = ROOT_BOUNDING_BOX, bool isRoot = IS_ROOT, - bool isCovered = false, float coverageDistance = NOT_COVERED); - ~CoverageMapV2(); - - CoverageMapV2StorageResult checkMap(const OctreeProjectedPolygon* polygon, bool storeIt = true); - - BoundingBox getChildBoundingBox(int childIndex); - const BoundingBox& getBoundingBox() const { return _myBoundingBox; }; - CoverageMapV2* getChild(int childIndex) const { return _childMaps[childIndex]; }; - bool isCovered() const { return _isCovered; }; - - void erase(); // erase the coverage map - - void render(); - - -private: - void recurseMap(const OctreeProjectedPolygon* polygon, bool storeIt, - bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered); - - void init(); - - bool _isRoot; - BoundingBox _myBoundingBox; - CoverageMapV2* _childMaps[NUMBER_OF_CHILDREN]; - - bool _isCovered; - float _coveredDistance; - - static int _mapCount; - static int _checkMapRootCalls; - static int _notAllInView; -}; - - -#endif // hifi_CoverageMapV2_h diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index c02a034778..6da59f00a3 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -41,7 +41,6 @@ #include #include -#include "CoverageMap.h" #include "OctreeConstants.h" #include "OctreeElementBag.h" #include "Octree.h" @@ -951,9 +950,9 @@ int Octree::encodeTreeBitstream(OctreeElementPointer element, // if childBytesWritten == 1 then something went wrong... that's not possible assert(childBytesWritten != 1); - // if includeColor and childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some + // if childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some // reason couldn't be written... so reset them here... This isn't true for the non-color included case - if (suppressEmptySubtrees() && params.includeColor && childBytesWritten == 2) { + if (suppressEmptySubtrees() && childBytesWritten == 2) { childBytesWritten = 0; //params.stopReason = EncodeBitstreamParams::UNKNOWN; // possibly should be DIDNT_FIT... } @@ -1103,31 +1102,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, params.stopReason = EncodeBitstreamParams::NO_CHANGE; return bytesAtThisLevel; } - - // If the user also asked for occlusion culling, check if this element is occluded, but only if it's not a leaf. - // leaf occlusion is handled down below when we check child nodes - if (params.wantOcclusionCulling && !element->isLeaf()) { - OctreeProjectedPolygon* voxelPolygon = - new OctreeProjectedPolygon(params.viewFrustum->getProjectedPolygon(element->getAACube())); - - // In order to check occlusion culling, the shadow has to be "all in view" otherwise, we will ignore occlusion - // culling and proceed as normal - if (voxelPolygon->getAllInView()) { - CoverageMapStorageResult result = params.map->checkMap(voxelPolygon, false); - delete voxelPolygon; // cleanup - if (result == OCCLUDED) { - if (params.stats) { - params.stats->skippedOccluded(element); - } - params.stopReason = EncodeBitstreamParams::OCCLUDED; - return bytesAtThisLevel; - } - } else { - // If this shadow wasn't "all in view" then we ignored it for occlusion culling, but - // we do need to clean up memory and proceed as normal... - delete voxelPolygon; - } - } } bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more! @@ -1190,20 +1164,10 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, } } - if (params.wantOcclusionCulling) { - if (childElement) { - float distance = params.viewFrustum ? childElement->distanceToCamera(*params.viewFrustum) : 0; - - currentCount = insertOctreeElementIntoSortedArrays(childElement, distance, i, - sortedChildren, (float*)&distancesToChildren, - (int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN); - } - } else { - sortedChildren[i] = childElement; - indexOfChildren[i] = i; - distancesToChildren[i] = 0.0f; - currentCount++; - } + sortedChildren[i] = childElement; + indexOfChildren[i] = i; + distancesToChildren[i] = 0.0f; + currentCount++; // track stats // must check childElement here, because it could be we got here with no childElement @@ -1255,36 +1219,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, bool childIsOccluded = false; // assume it's not occluded - // If the user also asked for occlusion culling, check if this element is occluded - if (params.wantOcclusionCulling && childElement->isLeaf()) { - // Don't check occlusion here, just add them to our distance ordered array... - - // FIXME params.ViewFrustum is used here, but later it is checked against nullptr. - OctreeProjectedPolygon* voxelPolygon = new OctreeProjectedPolygon( - params.viewFrustum->getProjectedPolygon(childElement->getAACube())); - - // In order to check occlusion culling, the shadow has to be "all in view" otherwise, we ignore occlusion - // culling and proceed as normal - if (voxelPolygon->getAllInView()) { - CoverageMapStorageResult result = params.map->checkMap(voxelPolygon, true); - - // In all cases where the shadow wasn't stored, we need to free our own memory. - // In the case where it is stored, the CoverageMap will free memory for us later. - if (result != STORED) { - delete voxelPolygon; - } - - // If while attempting to add this voxel's shadow, we determined it was occluded, then - // we don't need to process it further and we can exit early. - if (result == OCCLUDED) { - childIsOccluded = true; - } - } else { - delete voxelPolygon; - } - } // wants occlusion culling & isLeaf() - - bool shouldRender = !params.viewFrustum ? true : childElement->calculateShouldRender(params.viewFrustum, @@ -1359,7 +1293,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, // NOW might be a good time to give our tree subclass and this element a chance to set up and check any extra encode data element->initializeExtraEncodeData(params); - // write the child element data... NOTE: includeColor means include element data + // write the child element data... // NOTE: the format of the bitstream is generally this: // [octalcode] // [bitmask for existence of child data] @@ -1369,65 +1303,63 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, // N x [ ... tree for children ...] // // This section of the code, is writing the "N x [child data]" portion of this bitstream - if (params.includeColor) { - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (oneAtBit(childrenDataBits, i)) { - OctreeElementPointer childElement = element->getChildAtIndex(i); + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + if (oneAtBit(childrenDataBits, i)) { + OctreeElementPointer childElement = element->getChildAtIndex(i); - // the childrenDataBits were set up by the in view/LOD logic, it may contain children that we've already - // processed and sent the data bits for. Let our tree subclass determine if it really wants to send the - // data for this child at this point - if (childElement && element->shouldIncludeChildData(i, params)) { + // the childrenDataBits were set up by the in view/LOD logic, it may contain children that we've already + // processed and sent the data bits for. Let our tree subclass determine if it really wants to send the + // data for this child at this point + if (childElement && element->shouldIncludeChildData(i, params)) { - int bytesBeforeChild = packetData->getUncompressedSize(); + int bytesBeforeChild = packetData->getUncompressedSize(); - // a childElement may "partially" write it's data. for example, the model server where the entire - // contents of the element may be larger than can fit in a single MTU/packetData. In this case, - // we want to allow the appendElementData() to respond that it produced partial data, which should be - // written, but that the childElement needs to be reprocessed in an additional pass or passes - // to be completed. - LevelDetails childDataLevelKey = packetData->startLevel(); + // a childElement may "partially" write it's data. for example, the model server where the entire + // contents of the element may be larger than can fit in a single MTU/packetData. In this case, + // we want to allow the appendElementData() to respond that it produced partial data, which should be + // written, but that the childElement needs to be reprocessed in an additional pass or passes + // to be completed. + LevelDetails childDataLevelKey = packetData->startLevel(); - OctreeElement::AppendState childAppendState = childElement->appendElementData(packetData, params); + OctreeElement::AppendState childAppendState = childElement->appendElementData(packetData, params); - // allow our tree subclass to do any additional bookkeeping it needs to do with encoded data state - element->updateEncodedData(i, childAppendState, params); + // allow our tree subclass to do any additional bookkeeping it needs to do with encoded data state + element->updateEncodedData(i, childAppendState, params); - // Continue this level so long as some part of this child element was appended. - bool childFit = (childAppendState != OctreeElement::NONE); + // Continue this level so long as some part of this child element was appended. + bool childFit = (childAppendState != OctreeElement::NONE); - // some datatypes (like Voxels) assume that all child data will fit, if it doesn't fit - // the data type wants to bail on this element level completely - if (!childFit && mustIncludeAllChildData()) { - continueThisLevel = false; - break; - } + // some datatypes (like Voxels) assume that all child data will fit, if it doesn't fit + // the data type wants to bail on this element level completely + if (!childFit && mustIncludeAllChildData()) { + continueThisLevel = false; + break; + } - // If the child was partially or fully appended, then mark the actualChildrenDataBits as including - // this child data - if (childFit) { - actualChildrenDataBits += (1 << (7 - i)); - continueThisLevel = packetData->endLevel(childDataLevelKey); - } else { - packetData->discardLevel(childDataLevelKey); - elementAppendState = OctreeElement::PARTIAL; - params.stopReason = EncodeBitstreamParams::DIDNT_FIT; - } + // If the child was partially or fully appended, then mark the actualChildrenDataBits as including + // this child data + if (childFit) { + actualChildrenDataBits += (1 << (7 - i)); + continueThisLevel = packetData->endLevel(childDataLevelKey); + } else { + packetData->discardLevel(childDataLevelKey); + elementAppendState = OctreeElement::PARTIAL; + params.stopReason = EncodeBitstreamParams::DIDNT_FIT; + } - // If this child was partially appended, then consider this element to be partially appended - if (childAppendState == OctreeElement::PARTIAL) { - elementAppendState = OctreeElement::PARTIAL; - params.stopReason = EncodeBitstreamParams::DIDNT_FIT; - } + // If this child was partially appended, then consider this element to be partially appended + if (childAppendState == OctreeElement::PARTIAL) { + elementAppendState = OctreeElement::PARTIAL; + params.stopReason = EncodeBitstreamParams::DIDNT_FIT; + } - int bytesAfterChild = packetData->getUncompressedSize(); + int bytesAfterChild = packetData->getUncompressedSize(); - bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child + bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child - // don't need to check childElement here, because we can't get here with no childElement - if (params.stats && (childAppendState != OctreeElement::NONE)) { - params.stats->colorSent(childElement); - } + // don't need to check childElement here, because we can't get here with no childElement + if (params.stats && (childAppendState != OctreeElement::NONE)) { + params.stats->colorSent(childElement); } } } @@ -1506,15 +1438,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, // we know the last thing we wrote to the packet was our childrenExistInPacketBits. Let's remember where that was! int childExistsPlaceHolder = packetData->getUncompressedByteOffset(sizeof(childrenExistInPacketBits)); - // we are also going to recurse these child trees in "distance" sorted order, but we need to pack them in the - // final packet in standard order. So what we're going to do is keep track of how big each subtree was in bytes, - // and then later reshuffle these sections of our output buffer back into normal order. This allows us to make - // a single recursive pass in distance sorted order, but retain standard order in our encoded packet - int recursiveSliceSizes[NUMBER_OF_CHILDREN]; - const unsigned char* recursiveSliceStarts[NUMBER_OF_CHILDREN]; - int firstRecursiveSliceOffset = packetData->getUncompressedByteOffset(); - int allSlicesSize = 0; - // for each child element in Distance sorted order..., check to see if they exist, are colored, and in view, and if so // add them to our distance ordered array of children for (int indexByDistance = 0; indexByDistance < currentCount; indexByDistance++) { @@ -1524,9 +1447,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, if (oneAtBit(childrenExistInPacketBits, originalIndex)) { int thisLevel = currentEncodeLevel; - // remember this for reshuffling - recursiveSliceStarts[originalIndex] = packetData->getUncompressedData() + packetData->getUncompressedSize(); - int childTreeBytesOut = 0; // NOTE: some octree styles (like models and particles) will store content in parent elements, and child @@ -1546,10 +1466,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, } } - // remember this for reshuffling - recursiveSliceSizes[originalIndex] = childTreeBytesOut; - allSlicesSize += childTreeBytesOut; - // 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. @@ -1566,17 +1482,10 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, // so, if the child returns 2 bytes out, we can actually consider that an empty tree also!! // // we can make this act like no bytes out, by just resetting the bytes out in this case - if (suppressEmptySubtrees() && params.includeColor && !params.includeExistsBits && childTreeBytesOut == 2) { + if (suppressEmptySubtrees() && !params.includeExistsBits && childTreeBytesOut == 2) { childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees } - // We used to try to collapse trees that didn't contain any data, but this does appear to create a problem - // in detecting element deletion. So, I've commented this out but left it in here as a warning to anyone else - // about not attempting to add this optimization back in, without solving the element deletion case. - // We need to send these bitMasks in case the exists in tree bitmask is indicating the deletion of a tree - //if (params.includeColor && params.includeExistsBits && childTreeBytesOut == 3) { - // childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees - //} bytesAtThisLevel += childTreeBytesOut; @@ -1596,7 +1505,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, // If this is the last of the child exists bits, then we're actually be rolling out the entire tree if (params.stats && childrenExistInPacketBits == 0) { - params.stats->childBitsRemoved(params.includeExistsBits, params.includeColor); + params.stats->childBitsRemoved(params.includeExistsBits); } if (!continueThisLevel) { @@ -1613,33 +1522,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element, } // end if (childTreeBytesOut == 0) } // end if (oneAtBit(childrenExistInPacketBits, originalIndex)) } // end for - - // reshuffle here... - if (continueThisLevel && params.wantOcclusionCulling) { - unsigned char tempReshuffleBuffer[MAX_OCTREE_UNCOMRESSED_PACKET_SIZE]; - - unsigned char* tempBufferTo = &tempReshuffleBuffer[0]; // this is our temporary destination - - // iterate through our childrenExistInPacketBits, these will be the sections of the packet that we copied subTree - // details into. Unfortunately, they're in distance sorted order, not original index order. we need to put them - // back into original distance order - for (int originalIndex = 0; originalIndex < NUMBER_OF_CHILDREN; originalIndex++) { - if (oneAtBit(childrenExistInPacketBits, originalIndex)) { - int thisSliceSize = recursiveSliceSizes[originalIndex]; - const unsigned char* thisSliceStarts = recursiveSliceStarts[originalIndex]; - - memcpy(tempBufferTo, thisSliceStarts, thisSliceSize); - tempBufferTo += thisSliceSize; - } - } - - // now that all slices are back in the correct order, copy them to the correct output buffer - continueThisLevel = packetData->updatePriorBytes(firstRecursiveSliceOffset, &tempReshuffleBuffer[0], allSlicesSize); - if (!continueThisLevel) { - qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to update recursive slice!!!"; - qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE...."; - } - } } // end keepDiggingDeeper // If we made it this far, then we've written all of our child data... if this element is the root @@ -1918,7 +1800,7 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr unsigned char* dataAt = entireFileDataSection; - ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, + ReadBitstreamToTreeParams args(NO_EXISTS_BITS, NULL, 0, SharedNodePointer(), wantImportProgress, gotVersion); readBitstreamToTree(dataAt, dataLength, args); @@ -1957,7 +1839,7 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr unsigned char* dataAt = fileChunk; unsigned long dataLength = chunkLength; - ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, + ReadBitstreamToTreeParams args(NO_EXISTS_BITS, NULL, 0, SharedNodePointer(), wantImportProgress, gotVersion); readBitstreamToTree(dataAt, dataLength, args); @@ -2105,7 +1987,7 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElementPointer element) bool lastPacketWritten = false; while (OctreeElementPointer subTree = elementBag.extract()) { - EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS); + EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, NO_EXISTS_BITS); withReadLock([&] { params.extraEncodeData = &extraEncodeData; bytesWritten = encodeTreeBitstream(subTree, &packetData, elementBag, params); diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index d9cf17d7de..3ca8528a2f 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -28,7 +28,6 @@ #include "OctreePacketData.h" #include "OctreeSceneStats.h" -class CoverageMap; class ReadBitstreamToTreeParams; class Octree; class OctreeElement; @@ -53,12 +52,8 @@ typedef QHash CubeList; const bool NO_EXISTS_BITS = false; const bool WANT_EXISTS_BITS = true; -const bool NO_COLOR = false; -const bool WANT_COLOR = true; const bool COLLAPSE_EMPTY_TREE = true; const bool DONT_COLLAPSE = false; -const bool NO_OCCLUSION_CULLING = false; -const bool WANT_OCCLUSION_CULLING = true; const int DONT_CHOP = 0; const int NO_BOUNDARY_ADJUST = 0; @@ -75,18 +70,15 @@ public: int maxEncodeLevel; int maxLevelReached; const ViewFrustum* viewFrustum; - bool includeColor; bool includeExistsBits; int chopLevels; bool deltaViewFrustum; const ViewFrustum* lastViewFrustum; - bool wantOcclusionCulling; int boundaryLevelAdjust; float octreeElementSizeScale; quint64 lastViewFrustumSent; bool forceSendScene; OctreeSceneStats* stats; - CoverageMap* map; JurisdictionMap* jurisdictionMap; OctreeElementExtraEncodeData* extraEncodeData; @@ -108,13 +100,10 @@ public: EncodeBitstreamParams( int maxEncodeLevel = INT_MAX, const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM, - bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS, int chopLevels = 0, bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM, - bool wantOcclusionCulling = NO_OCCLUSION_CULLING, - CoverageMap* map = IGNORE_COVERAGE_MAP, int boundaryLevelAdjust = NO_BOUNDARY_ADJUST, float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE, quint64 lastViewFrustumSent = IGNORE_LAST_SENT, @@ -125,18 +114,15 @@ public: maxEncodeLevel(maxEncodeLevel), maxLevelReached(0), viewFrustum(viewFrustum), - includeColor(includeColor), includeExistsBits(includeExistsBits), chopLevels(chopLevels), deltaViewFrustum(deltaViewFrustum), lastViewFrustum(lastViewFrustum), - wantOcclusionCulling(wantOcclusionCulling), boundaryLevelAdjust(boundaryLevelAdjust), octreeElementSizeScale(octreeElementSizeScale), lastViewFrustumSent(lastViewFrustumSent), forceSendScene(forceSendScene), stats(stats), - map(map), jurisdictionMap(jurisdictionMap), extraEncodeData(extraEncodeData), stopReason(UNKNOWN) @@ -176,6 +162,8 @@ public: case OCCLUDED: return QString("OCCLUDED"); break; } } + + std::function trackSend { [](const QUuid&, quint64){} }; }; class ReadElementBufferToTreeArgs { @@ -188,7 +176,6 @@ public: class ReadBitstreamToTreeParams { public: - bool includeColor; bool includeExistsBits; OctreeElementPointer destinationElement; QUuid sourceUUID; @@ -199,14 +186,12 @@ public: int entitiesPerPacket = 0; ReadBitstreamToTreeParams( - bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS, OctreeElementPointer destinationElement = NULL, QUuid sourceUUID = QUuid(), SharedNodePointer sourceNode = SharedNodePointer(), bool wantImportProgress = false, PacketVersion bitstreamVersion = 0) : - includeColor(includeColor), includeExistsBits(includeExistsBits), destinationElement(destinationElement), sourceUUID(sourceUUID), @@ -313,7 +298,7 @@ public: Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); // Note: this assumes the fileFormat is the HIO individual voxels code files - void loadOctreeFile(const char* fileName, bool wantColorRandomizer); + void loadOctreeFile(const char* fileName); // Octree exporters void writeToFile(const char* filename, OctreeElementPointer element = NULL, QString persistAsFileType = "svo"); diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/libraries/octree/src/OctreeHeadlessViewer.cpp index 88a77a4c53..7b80d315f1 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.cpp +++ b/libraries/octree/src/OctreeHeadlessViewer.cpp @@ -51,10 +51,7 @@ void OctreeHeadlessViewer::queryOctree() { // These will be the same for all servers, so we can set them up once and then reuse for each server we send to. _octreeQuery.setWantLowResMoving(true); - _octreeQuery.setWantColor(true); _octreeQuery.setWantDelta(true); - _octreeQuery.setWantOcclusionCulling(false); - _octreeQuery.setWantCompression(true); // TODO: should be on by default _octreeQuery.setCameraPosition(_viewFrustum.getPosition()); _octreeQuery.setCameraOrientation(_viewFrustum.getOrientation()); diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 811e96fcf4..f4fbf9bd97 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -23,12 +23,13 @@ AtomicUIntStat OctreePacketData::_totalBytesOfValues { 0 }; AtomicUIntStat OctreePacketData::_totalBytesOfPositions { 0 }; AtomicUIntStat OctreePacketData::_totalBytesOfRawData { 0 }; -OctreePacketData::OctreePacketData(bool enableCompression, int targetSize) { - changeSettings(enableCompression, targetSize); // does reset... +OctreePacketData::OctreePacketData(int targetSize, bool enableCompression) { + changeSettings(targetSize); // does reset... + _enableCompression = enableCompression; // FIXME } -void OctreePacketData::changeSettings(bool enableCompression, unsigned int targetSize) { - _enableCompression = enableCompression; +void OctreePacketData::changeSettings(unsigned int targetSize) { + _enableCompression = true; // FIXME _targetSize = std::min(MAX_OCTREE_UNCOMRESSED_PACKET_SIZE, targetSize); reset(); } diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 2c86d518ad..fb53b3472f 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -83,11 +83,11 @@ private: /// Handles packing of the data portion of PacketType_OCTREE_DATA messages. class OctreePacketData { public: - OctreePacketData(bool enableCompression = false, int maxFinalizedSize = MAX_OCTREE_PACKET_DATA_SIZE); + OctreePacketData(int maxFinalizedSize = MAX_OCTREE_PACKET_DATA_SIZE, bool enableCompression = true); ~OctreePacketData(); /// change compression and target size settings - void changeSettings(bool enableCompression = false, unsigned int targetSize = MAX_OCTREE_PACKET_DATA_SIZE); + void changeSettings(unsigned int targetSize = MAX_OCTREE_PACKET_DATA_SIZE); /// reset completely, all data is discarded void reset(); @@ -262,7 +262,7 @@ private: bool append(unsigned char byte); unsigned int _targetSize; - bool _enableCompression; + bool _enableCompression { true }; // FIXME - these will always be compressed, so remove this option unsigned char _uncompressed[MAX_OCTREE_UNCOMRESSED_PACKET_SIZE]; int _bytesInUse; diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index e8beb0404c..8449e3083a 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -41,10 +41,7 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) { // bitMask of less than byte wide items unsigned char bitItems = 0; if (_wantLowResMoving) { setAtBit(bitItems, WANT_LOW_RES_MOVING_BIT); } - if (_wantColor) { setAtBit(bitItems, WANT_COLOR_AT_BIT); } if (_wantDelta) { setAtBit(bitItems, WANT_DELTA_AT_BIT); } - if (_wantOcclusionCulling) { setAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); } - if (_wantCompression) { setAtBit(bitItems, WANT_COMPRESSION); } *destinationBuffer++ = bitItems; @@ -84,10 +81,7 @@ int OctreeQuery::parseData(NLPacket& packet) { unsigned char bitItems = 0; bitItems = (unsigned char)*sourceBuffer++; _wantLowResMoving = oneAtBit(bitItems, WANT_LOW_RES_MOVING_BIT); - _wantColor = oneAtBit(bitItems, WANT_COLOR_AT_BIT); _wantDelta = oneAtBit(bitItems, WANT_DELTA_AT_BIT); - _wantOcclusionCulling = oneAtBit(bitItems, WANT_OCCLUSION_CULLING_BIT); - _wantCompression = oneAtBit(bitItems, WANT_COMPRESSION); // desired Max Octree PPS memcpy(&_maxQueryPPS, sourceBuffer, sizeof(_maxQueryPPS)); diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index 86474ffc02..962a8e1425 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -35,10 +35,10 @@ typedef unsigned long long quint64; // First bitset const int WANT_LOW_RES_MOVING_BIT = 0; -const int WANT_COLOR_AT_BIT = 1; +const int UNUSED_BIT_1 = 1; // unused... available for new feature const int WANT_DELTA_AT_BIT = 2; -const int WANT_OCCLUSION_CULLING_BIT = 3; -const int WANT_COMPRESSION = 4; // 5th bit +const int UNUSED_BIT_3 = 3; // unused... available for new feature +const int UNUSED_BIT_4 = 4; // 5th bit, unused... available for new feature class OctreeQuery : public NodeData { Q_OBJECT @@ -71,21 +71,15 @@ public: void setCameraEyeOffsetPosition(const glm::vec3& eyeOffsetPosition) { _cameraEyeOffsetPosition = eyeOffsetPosition; } // related to Octree Sending strategies - bool getWantColor() const { return _wantColor; } bool getWantDelta() const { return _wantDelta; } bool getWantLowResMoving() const { return _wantLowResMoving; } - bool getWantOcclusionCulling() const { return _wantOcclusionCulling; } - bool getWantCompression() const { return _wantCompression; } int getMaxQueryPacketsPerSecond() const { return _maxQueryPPS; } float getOctreeSizeScale() const { return _octreeElementSizeScale; } int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } public slots: void setWantLowResMoving(bool wantLowResMoving) { _wantLowResMoving = wantLowResMoving; } - void setWantColor(bool wantColor) { _wantColor = wantColor; } void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; } - void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; } - void setWantCompression(bool wantCompression) { _wantCompression = wantCompression; } void setMaxQueryPacketsPerSecond(int maxQueryPPS) { _maxQueryPPS = maxQueryPPS; } void setOctreeSizeScale(float octreeSizeScale) { _octreeElementSizeScale = octreeSizeScale; } void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; } @@ -101,11 +95,8 @@ protected: glm::vec3 _cameraEyeOffsetPosition = glm::vec3(0.0f); // octree server sending items - bool _wantColor = true; bool _wantDelta = true; bool _wantLowResMoving = true; - bool _wantOcclusionCulling = false; - bool _wantCompression = false; int _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS; float _octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE; /// used for LOD calculations int _boundaryLevelAdjust = 0; /// used for LOD calculations diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index b7be4cf3e7..c65359f12f 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -115,7 +115,7 @@ void OctreeRenderer::processDatagram(NLPacket& packet, SharedNodePointer sourceN if (sectionLength) { // ask the VoxelTree to read the bitstream into the tree - ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, + ReadBitstreamToTreeParams args(WANT_EXISTS_BITS, NULL, sourceUUID, sourceNode, false, packet.getVersion()); quint64 startUncompress, startLock = usecTimestampNow(); quint64 startReadBitsteam, endReadBitsteam; diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index 22352fbe3b..c70e0e4935 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -371,14 +371,12 @@ void OctreeSceneStats::existsInPacketBitsWritten() { _existsInPacketBitsWritten++; } -void OctreeSceneStats::childBitsRemoved(bool includesExistsBits, bool includesColors) { +void OctreeSceneStats::childBitsRemoved(bool includesExistsBits) { _existsInPacketBitsWritten--; if (includesExistsBits) { _existsBitsWritten--; } - if (includesColors) { - _colorBitsWritten--; - } + _colorBitsWritten--; _treesRemoved++; } diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h index bdb4ef206a..f8ecf93106 100644 --- a/libraries/octree/src/OctreeSceneStats.h +++ b/libraries/octree/src/OctreeSceneStats.h @@ -89,7 +89,7 @@ public: void existsInPacketBitsWritten(); /// Fix up tracking statistics in case where bitmasks were removed for some reason - void childBitsRemoved(bool includesExistsBits, bool includesColors); + void childBitsRemoved(bool includesExistsBits); /// Pack the details of the statistics into a buffer for sending as a network packet int packIntoPacket(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 0070b99591..7ab1129e2e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -928,7 +928,7 @@ void Model::simulate(float deltaTime, bool fullUpdate) { //virtual void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { _needsUpdateClusterMatrices = true; - _rig->updateAnimations(deltaTime, parentTransform); + _rig->updateAnimations(deltaTime, parentTransform); } void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 49009a3ad2..73c4b7e22b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -674,7 +674,6 @@ void ScriptEngine::run() { } _isRunning = true; - _isFinished = false; if (_wantSignals) { emit runningStateChanged(); } diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 7c5ba6bb48..257ef2f00e 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -279,18 +279,20 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) { glm::quat glmExtractRotation(const glm::mat4& matrix) { glm::vec3 scale = extractScale(matrix); - float maxScale = std::max(std::max(scale.x, scale.y), scale.z); - if (maxScale > 1.01f || maxScale <= 0.99f) { - // quat_cast doesn't work so well with scaled matrices, so cancel it out. - glm::mat4 tmp = glm::scale(matrix, 1.0f / scale); - return glm::normalize(glm::quat_cast(tmp)); - } else { - return glm::normalize(glm::quat_cast(matrix)); - } + // quat_cast doesn't work so well with scaled matrices, so cancel it out. + glm::mat4 tmp = glm::scale(matrix, 1.0f / scale); + return glm::normalize(glm::quat_cast(tmp)); } glm::vec3 extractScale(const glm::mat4& matrix) { - return glm::vec3(glm::length(matrix[0]), glm::length(matrix[1]), glm::length(matrix[2])); + glm::mat3 m(matrix); + float det = glm::determinant(m); + if (det < 0) { + // left handed matrix, flip sign to compensate. + return glm::vec3(-glm::length(m[0]), glm::length(m[1]), glm::length(m[2])); + } else { + return glm::vec3(glm::length(m[0]), glm::length(m[1]), glm::length(m[2])); + } } float extractUniformScale(const glm::mat4& matrix) { diff --git a/libraries/shared/src/OctalCode.cpp b/libraries/shared/src/OctalCode.cpp index 0b77683d4c..1fa18903cb 100644 --- a/libraries/shared/src/OctalCode.cpp +++ b/libraries/shared/src/OctalCode.cpp @@ -264,29 +264,6 @@ unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLev return newCode; } -unsigned char* rebaseOctalCode(const unsigned char* originalOctalCode, const unsigned char* newParentOctalCode, - bool includeColorSpace) { - - int oldCodeLength = numberOfThreeBitSectionsInCode(originalOctalCode); - int newParentCodeLength = numberOfThreeBitSectionsInCode(newParentOctalCode); - int newCodeLength = newParentCodeLength + oldCodeLength; - int bufferLength = newCodeLength + (includeColorSpace ? SIZE_OF_COLOR_DATA : 0); - unsigned char* newCode = new unsigned char[bufferLength]; - *newCode = newCodeLength; // set the length byte - - // copy parent code section first - for (int sectionFromParent = 0; sectionFromParent < newParentCodeLength; sectionFromParent++) { - char sectionValue = getOctalCodeSectionValue(newParentOctalCode, sectionFromParent); - setOctalCodeSectionValue(newCode, sectionFromParent, sectionValue); - } - // copy original code section next - for (int sectionFromOriginal = 0; sectionFromOriginal < oldCodeLength; sectionFromOriginal++) { - char sectionValue = getOctalCodeSectionValue(originalOctalCode, sectionFromOriginal); - setOctalCodeSectionValue(newCode, sectionFromOriginal + newParentCodeLength, sectionValue); - } - return newCode; -} - bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent, int descendentsChild) { if (!possibleAncestor || !possibleDescendent) { return false; diff --git a/libraries/shared/src/OctalCode.h b/libraries/shared/src/OctalCode.h index 9229157c3d..09766b685a 100644 --- a/libraries/shared/src/OctalCode.h +++ b/libraries/shared/src/OctalCode.h @@ -36,8 +36,6 @@ const int UNKNOWN_OCTCODE_LENGTH = -2; int numberOfThreeBitSectionsInCode(const unsigned char* octalCode, int maxBytes = UNKNOWN_OCTCODE_LENGTH); unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels); -unsigned char* rebaseOctalCode(const unsigned char* originalOctalCode, const unsigned char* newParentOctalCode, - bool includeColorSpace = false); const int CHECK_NODE_ONLY = -1; bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent, diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index ddf251778f..f0398158b4 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 1b5bb4739a..64db3f6154 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -194,10 +194,7 @@ void AnimTests::testVariant() { auto floatVarNegative = AnimVariant(-1.0f); auto vec3Var = AnimVariant(glm::vec3(1.0f, -2.0f, 3.0f)); auto quatVar = AnimVariant(glm::quat(1.0f, 2.0f, -3.0f, 4.0f)); - auto mat4Var = AnimVariant(glm::mat4(glm::vec4(1.0f, 2.0f, 3.0f, 4.0f), - glm::vec4(5.0f, 6.0f, -7.0f, 8.0f), - glm::vec4(9.0f, 10.0f, 11.0f, 12.0f), - glm::vec4(13.0f, 14.0f, 15.0f, 16.0f))); + QVERIFY(defaultVar.isBool()); QVERIFY(defaultVar.getBool() == false); @@ -232,12 +229,6 @@ void AnimTests::testVariant() { QVERIFY(q.x == 2.0f); QVERIFY(q.y == -3.0f); QVERIFY(q.z == 4.0f); - - QVERIFY(mat4Var.isMat4()); - auto m = mat4Var.getMat4(); - QVERIFY(m[0].x == 1.0f); - QVERIFY(m[1].z == -7.0f); - QVERIFY(m[3].w == 16.0f); } void AnimTests::testAccumulateTime() { @@ -323,3 +314,83 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop"); triggers.clear(); } + + +void AnimTests::testAnimPose() { + const float PI = (float)M_PI; + const glm::quat ROT_X_90 = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f)); + const glm::quat ROT_Y_180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0, 0.0f)); + const glm::quat ROT_Z_30 = glm::angleAxis(PI / 6.0f, glm::vec3(1.0f, 0.0f, 0.0f)); + + std::vector scaleVec = { + glm::vec3(1), + glm::vec3(2.0f, 1.0f, 1.0f), + glm::vec3(1.0f, 0.5f, 1.0f), + glm::vec3(1.0f, 1.0f, 1.5f), + glm::vec3(2.0f, 0.5f, 1.5f), + glm::vec3(-2.0f, 0.5f, 1.5f), + glm::vec3(2.0f, -0.5f, 1.5f), + glm::vec3(2.0f, 0.5f, -1.5f), + glm::vec3(-2.0f, -0.5f, -1.5f), + }; + + std::vector rotVec = { + glm::quat(), + ROT_X_90, + ROT_Y_180, + ROT_Z_30, + ROT_X_90 * ROT_Y_180 * ROT_Z_30, + -ROT_Y_180 + }; + + std::vector transVec = { + glm::vec3(), + glm::vec3(10.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 5.0f, 0.0f), + glm::vec3(0.0f, 0.0f, 7.5f), + glm::vec3(10.0f, 5.0f, 7.5f), + glm::vec3(-10.0f, 5.0f, 7.5f), + glm::vec3(10.0f, -5.0f, 7.5f), + glm::vec3(10.0f, 5.0f, -7.5f) + }; + + const float EPSILON = 0.001f; + + for (auto& scale : scaleVec) { + for (auto& rot : rotVec) { + for (auto& trans : transVec) { + + // build a matrix the old fashioned way. + glm::mat4 scaleMat = glm::scale(glm::mat4(), scale); + glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans); + glm::mat4 rawMat = rotTransMat * scaleMat; + + // use an anim pose to build a matrix by parts. + AnimPose pose(scale, rot, trans); + glm::mat4 poseMat = pose; + + QCOMPARE_WITH_ABS_ERROR(rawMat, poseMat, EPSILON); + } + } + } + + for (auto& scale : scaleVec) { + for (auto& rot : rotVec) { + for (auto& trans : transVec) { + + // build a matrix the old fashioned way. + glm::mat4 scaleMat = glm::scale(glm::mat4(), scale); + glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans); + glm::mat4 rawMat = rotTransMat * scaleMat; + + // use an anim pose to decompse a matrix into parts + AnimPose pose(rawMat); + + // now build a new matrix from those parts. + glm::mat4 poseMat = pose; + + QCOMPARE_WITH_ABS_ERROR(rawMat, poseMat, EPSILON); + } + } + } +} diff --git a/tests/animation/src/AnimTests.h b/tests/animation/src/AnimTests.h index 7bd05369c7..a07217b91a 100644 --- a/tests/animation/src/AnimTests.h +++ b/tests/animation/src/AnimTests.h @@ -26,6 +26,7 @@ private slots: void testLoader(); void testVariant(); void testAccumulateTime(); + void testAnimPose(); }; #endif // hifi_AnimTests_h diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index 3edff67d66..f65cd9b5aa 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -128,7 +128,7 @@ protected: public: QTestWindow() { setSurfaceType(QSurface::OpenGLSurface); - QSurfaceFormat format = getDefaultOpenGlSurfaceFormat(); + QSurfaceFormat format = getDefaultOpenGLSurfaceFormat(); setFormat(format); _context = new QOpenGLContext; _context->setFormat(format); diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 982c65fc08..7e1391780d 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -241,7 +241,6 @@ gravity: BOW_GRAVITY, shapeType: 'compound', compoundShapeURL: COLLISION_HULL_URL, - collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/bow_fall.L.wav", script: bowScriptURL, userData: JSON.stringify({ resetMe: { @@ -656,6 +655,7 @@ y: -3.5, z: 0 }, + restitution: 0, velocity: { x: 0, y: -0.01, @@ -1027,6 +1027,7 @@ y: -9.8, z: 0 }, + restitution: 0, dimensions: { x: 0.08, y: 0.21, @@ -1189,6 +1190,7 @@ collisionsWillMove: true, collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/SpryPntCnDrp1.L.wav", shapeType: 'box', + restitution: 0, gravity: { x: 0, y: -3.0, diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 6e89e12d41..2fbeb94865 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -220,7 +220,6 @@ MasterReset = function() { gravity: BOW_GRAVITY, shapeType: 'compound', compoundShapeURL: COLLISION_HULL_URL, - collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/bow_fall.L.wav", script: bowScriptURL, userData: JSON.stringify({ resetMe: { @@ -636,6 +635,7 @@ MasterReset = function() { y: -3.5, z: 0 }, + restitution: 0, velocity: { x: 0, y: -0.01, @@ -1007,6 +1007,7 @@ MasterReset = function() { y: -9.8, z: 0 }, + restitution: 0, dimensions: { x: 0.08, y: 0.21, @@ -1174,6 +1175,7 @@ MasterReset = function() { y: -3.0, z: 0 }, + restitution: 0, velocity: { x: 0, y: -1,