From 881cfb86dd9364f387ec453967003252df5f3b94 Mon Sep 17 00:00:00 2001 From: samcake Date: Sat, 8 Aug 2015 16:43:55 -0700 Subject: [PATCH 01/25] Updating the render engine dashboard --- examples/utilities/tools/cookies.js | 26 ++++ examples/utilities/tools/renderEngineDebug.js | 118 +++++++----------- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/examples/utilities/tools/cookies.js b/examples/utilities/tools/cookies.js index 751008fd99..b9d634d462 100644 --- a/examples/utilities/tools/cookies.js +++ b/examples/utilities/tools/cookies.js @@ -183,6 +183,24 @@ var CHECK_MARK_COLOR = { this.onValueChanged(resetValue); }; + + Slider.prototype.setMinValue = function(minValue) { + var currentValue = this.getValue(); + this.minValue = minValue; + this.setValue(currentValue); + }; + Slider.prototype.getMinValue = function() { + return this.minValue; + }; + Slider.prototype.setMaxValue = function(maxValue) { + var currentValue = this.getValue(); + this.maxValue = maxValue; + this.setValue(currentValue); + }; + Slider.prototype.getMaxValue = function() { + return this.maxValue; + }; + Slider.prototype.onValueChanged = function(value) {}; Slider.prototype.getHeight = function() { @@ -1396,6 +1414,14 @@ var CHECK_MARK_COLOR = { return null; }; + Panel.prototype.getWidget = function(name) { + var item = this.items[name]; + if (item != null) { + return item.widget; + } + return null; + }; + Panel.prototype.update = function(name) { var item = this.items[name]; if (item != null) { diff --git a/examples/utilities/tools/renderEngineDebug.js b/examples/utilities/tools/renderEngineDebug.js index d50a9c545c..49ac923436 100755 --- a/examples/utilities/tools/renderEngineDebug.js +++ b/examples/utilities/tools/renderEngineDebug.js @@ -12,59 +12,55 @@ Script.include("cookies.js"); var panel = new Panel(10, 100); -panel.newSlider("Num Feed Opaques", 0, 1000, - function(value) { }, - function() { return Scene.getEngineNumFeedOpaqueItems(); }, - function(value) { return (value); } +function CounterWidget(parentPanel, name, feedGetter, drawGetter, capSetter, capGetter) { + this.subPanel = panel.newSubPanel(name); + + this.subPanel.newSlider("Num Feed", 0, 1, + function(value) { }, + feedGetter, + function(value) { return (value); }); + this.subPanel.newSlider("Num Drawn", 0, 1, + function(value) { }, + drawGetter, + function(value) { return (value); }); + this.subPanel.newSlider("Max Drawn", -1, 1, + capSetter, + capGetter, + function(value) { return (value); }); + + this.update = function () { + var numFeed = this.subPanel.get("Num Feed"); + this.subPanel.set("Num Feed", numFeed); + this.subPanel.set("Num Drawn", this.subPanel.get("Num Drawn")); + + var numMax = Math.max(numFeed, 1); + this.subPanel.getWidget("Num Feed").setMaxValue(numMax); + this.subPanel.getWidget("Num Drawn").setMaxValue(numMax); + this.subPanel.getWidget("Max Drawn").setMaxValue(numMax); + }; +}; + +var opaquesCounter = new CounterWidget(panel, "Opaques", + function () { return Scene.getEngineNumFeedOpaqueItems(); }, + function () { return Scene.getEngineNumDrawnOpaqueItems(); }, + function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); }, + function () { return Scene.getEngineMaxDrawnOpaqueItems(); } ); -panel.newSlider("Num Drawn Opaques", 0, 1000, - function(value) { }, - function() { return Scene.getEngineNumDrawnOpaqueItems(); }, - function(value) { return (value); } +var transparentsCounter = new CounterWidget(panel, "Transparents", + function () { return Scene.getEngineNumFeedTransparentItems(); }, + function () { return Scene.getEngineNumDrawnTransparentItems(); }, + function(value) { Scene.setEngineMaxDrawnTransparentItems(value); }, + function () { return Scene.getEngineMaxDrawnTransparentItems(); } ); -panel.newSlider("Max Drawn Opaques", -1, 1000, - function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); }, - function() { return Scene.getEngineMaxDrawnOpaqueItems(); }, - function(value) { return (value); } +var overlaysCounter = new CounterWidget(panel, "Overlays", + function () { return Scene.getEngineNumFeedOverlay3DItems(); }, + function () { return Scene.getEngineNumDrawnOverlay3DItems(); }, + function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); }, + function () { return Scene.getEngineMaxDrawnOverlay3DItems(); } ); -panel.newSlider("Num Feed Transparents", 0, 100, - function(value) { }, - function() { return Scene.getEngineNumFeedTransparentItems(); }, - function(value) { return (value); } -); - -panel.newSlider("Num Drawn Transparents", 0, 100, - function(value) { }, - function() { return Scene.getEngineNumDrawnTransparentItems(); }, - function(value) { return (value); } -); - -panel.newSlider("Max Drawn Transparents", -1, 100, - function(value) { Scene.setEngineMaxDrawnTransparentItems(value); }, - function() { return Scene.getEngineMaxDrawnTransparentItems(); }, - function(value) { return (value); } -); - -panel.newSlider("Num Feed Overlay3Ds", 0, 100, - function(value) { }, - function() { return Scene.getEngineNumFeedOverlay3DItems(); }, - function(value) { return (value); } -); - -panel.newSlider("Num Drawn Overlay3Ds", 0, 100, - function(value) { }, - function() { return Scene.getEngineNumDrawnOverlay3DItems(); }, - function(value) { return (value); } -); - -panel.newSlider("Max Drawn Overlay3Ds", -1, 100, - function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); }, - function() { return Scene.getEngineMaxDrawnOverlay3DItems(); }, - function(value) { return (value); } -); panel.newCheckbox("Display status", function(value) { Scene.setEngineDisplayItemStatus(value); }, @@ -75,31 +71,9 @@ panel.newCheckbox("Display status", var tickTackPeriod = 500; function updateCounters() { - var numFeedOpaques = panel.get("Num Feed Opaques"); - var numFeedTransparents = panel.get("Num Feed Transparents"); - var numFeedOverlay3Ds = panel.get("Num Feed Overlay3Ds"); - - panel.set("Num Feed Opaques", numFeedOpaques); - panel.set("Num Drawn Opaques", panel.get("Num Drawn Opaques")); - panel.set("Num Feed Transparents", numFeedTransparents); - panel.set("Num Drawn Transparents", panel.get("Num Drawn Transparents")); - panel.set("Num Feed Overlay3Ds", numFeedOverlay3Ds); - panel.set("Num Drawn Overlay3Ds", panel.get("Num Drawn Overlay3Ds")); - - var numMax = Math.max(numFeedOpaques * 1.2, 1); - panel.getWidget("Num Feed Opaques").setMaxValue(numMax); - panel.getWidget("Num Drawn Opaques").setMaxValue(numMax); - panel.getWidget("Max Drawn Opaques").setMaxValue(numMax); - - numMax = Math.max(numFeedTransparents * 1.2, 1); - panel.getWidget("Num Feed Transparents").setMaxValue(numMax); - panel.getWidget("Num Drawn Transparents").setMaxValue(numMax); - panel.getWidget("Max Drawn Transparents").setMaxValue(numMax); - - numMax = Math.max(numFeedOverlay3Ds * 1.2, 1); - panel.getWidget("Num Feed Overlay3Ds").setMaxValue(numMax); - panel.getWidget("Num Drawn Overlay3Ds").setMaxValue(numMax); - panel.getWidget("Max Drawn Overlay3Ds").setMaxValue(numMax); + opaquesCounter.update(); + transparentsCounter.update(); + overlaysCounter.update(); } Script.setInterval(updateCounters, tickTackPeriod); From 2056f588e3575e0dae35b66c288f0147fe7bbe39 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 19 Aug 2015 18:03:16 -0700 Subject: [PATCH 02/25] if environment variable HIFI_MEMORY_DEBUGGING is defined when cmake is run, enable -fsanitize=address on linux --- assignment-client/CMakeLists.txt | 10 +++++++++- domain-server/CMakeLists.txt | 8 ++++++++ interface/CMakeLists.txt | 8 ++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index edd68e12bf..7e3b2e6af9 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -18,4 +18,12 @@ if (UNIX) endif (UNIX) include_application_version() -copy_dlls_beside_windows_executable() \ No newline at end of file +copy_dlls_beside_windows_executable() + +if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + if (UNIX) + MESSAGE("-- assignment-client memory debugging is enabled.") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") + endif (UNIX) +endif () diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index e4fa1d874d..0d56e34cf2 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -33,3 +33,11 @@ target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) include_application_version() copy_dlls_beside_windows_executable() + +if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + if (UNIX) + MESSAGE("-- domain-server memory debugging is enabled.") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") + endif (UNIX) +endif () diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index acafafa006..16d8bacd23 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -206,3 +206,11 @@ else (APPLE) endif (APPLE) copy_dlls_beside_windows_executable() + +if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + if (UNIX) + MESSAGE("-- interface memory debugging is enabled.") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") + endif (UNIX) +endif () From 2738f65c10b624a7e49d3edc7d935b8df1ba8d96 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Aug 2015 18:23:11 -0700 Subject: [PATCH 03/25] have domain-server report non-skewed uptime --- domain-server/resources/web/index.shtml | 2 +- domain-server/resources/web/js/tables.js | 4 ++-- domain-server/src/DomainServer.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/domain-server/resources/web/index.shtml b/domain-server/resources/web/index.shtml index 0f720ebe79..ea941a73fd 100644 --- a/domain-server/resources/web/index.shtml +++ b/domain-server/resources/web/index.shtml @@ -31,7 +31,7 @@ <%- node.username %> <%- node.public.ip %>:<%- node.public.port %> <%- node.local.ip %>:<%- node.local.port %> - <%- ((Date.now() - node.wake_timestamp) / 1000).toLocaleString() %> + <%- node.uptime %> <%- (typeof node.pending_credits == 'number' ? node.pending_credits.toLocaleString() : 'N/A') %> diff --git a/domain-server/resources/web/js/tables.js b/domain-server/resources/web/js/tables.js index 0b29d4e6c9..b2e0679d8b 100644 --- a/domain-server/resources/web/js/tables.js +++ b/domain-server/resources/web/js/tables.js @@ -9,9 +9,9 @@ $(document).ready(function(){ json.nodes.sort(function(a, b){ if (a.type === b.type) { - if (a.wake_timestamp < b.wake_timestamp) { + if (a.uptime > b.uptime) { return 1; - } else if (a.wake_timestamp > b.wake_timestamp) { + } else if (a.uptime < b.uptime) { return -1; } else { return 0; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 071626ef1e..369cb3b761 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1480,7 +1480,7 @@ const char JSON_KEY_PUBLIC_SOCKET[] = "public"; const char JSON_KEY_LOCAL_SOCKET[] = "local"; const char JSON_KEY_POOL[] = "pool"; const char JSON_KEY_PENDING_CREDITS[] = "pending_credits"; -const char JSON_KEY_WAKE_TIMESTAMP[] = "wake_timestamp"; +const char JSON_KEY_UPTIME[] = "uptime"; const char JSON_KEY_USERNAME[] = "username"; const char JSON_KEY_VERSION[] = "version"; QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { @@ -1502,7 +1502,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { nodeJson[JSON_KEY_LOCAL_SOCKET] = jsonForSocket(node->getLocalSocket()); // add the node uptime in our list - nodeJson[JSON_KEY_WAKE_TIMESTAMP] = QString::number(node->getWakeTimestamp()); + nodeJson[JSON_KEY_UPTIME] = QString::number((QDateTime::currentMSecsSinceEpoch() - node->getWakeTimestamp()) / 1000.0); // if the node has pool information, add it DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); From b386a95d0852a4284d414e26ac6bbd56d95fed44 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Aug 2015 18:31:42 -0700 Subject: [PATCH 04/25] fix sort for node uptime --- domain-server/resources/web/js/tables.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/js/tables.js b/domain-server/resources/web/js/tables.js index b2e0679d8b..09f85a7047 100644 --- a/domain-server/resources/web/js/tables.js +++ b/domain-server/resources/web/js/tables.js @@ -9,9 +9,9 @@ $(document).ready(function(){ json.nodes.sort(function(a, b){ if (a.type === b.type) { - if (a.uptime > b.uptime) { + if (a.uptime < b.uptime) { return 1; - } else if (a.uptime < b.uptime) { + } else if (a.uptime > b.uptime) { return -1; } else { return 0; From 2b26f302fd0353c668230c632b950057ecdf741d Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 20 Aug 2015 08:01:33 -0600 Subject: [PATCH 05/25] Better handling of socket binding --- .../embedded-webserver/src/HTTPManager.cpp | 30 +++++++++++++++---- .../embedded-webserver/src/HTTPManager.h | 11 ++++++- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 19443e01da..0d91672904 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include @@ -19,16 +20,18 @@ #include "EmbeddedWebserverLogging.h" #include "HTTPManager.h" +const int SOCKET_ERROR_EXIT_CODE = 2; + HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : QTcpServer(parent), _documentRoot(documentRoot), - _requestHandler(requestHandler) + _requestHandler(requestHandler), + _port(port) { - // start listening on the passed port - if (!listen(QHostAddress("0.0.0.0"), port)) { - qCDebug(embeddedwebserver) << "Failed to open HTTP server socket:" << errorString(); - return; - } + bindSocket(); + _isListeningTimer = new QTimer(this); + connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening); + _isListeningTimer->start(10000); } void HTTPManager::incomingConnection(qintptr socketDescriptor) { @@ -157,3 +160,18 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool HTTPManager::requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url) { return _requestHandler && _requestHandler->handleHTTPRequest(connection, url); } + +void HTTPManager::isTcpServerListening() { + if (!isListening()) { + qCWarning(embeddedwebserver) << "Socket on port " << QString::number(_port) << " is no longer listening"; + bindSocket(); + }} + +bool HTTPManager::bindSocket() { + qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); + if (!listen(QHostAddress::Any, _port)) { + qCWarning(embeddedwebserver) << "Failed to open HTTP server socket:" << errorString() << " can't continue"; + QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); + } + return true; +} \ No newline at end of file diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index 83c4103c15..6375b10205 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -17,6 +17,7 @@ #define hifi_HTTPManager_h #include +#include class HTTPConnection; class HTTPSConnection; @@ -35,14 +36,22 @@ public: HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0); bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); + +private slots: + void isTcpServerListening(); + +private: + bool bindSocket(); protected: /// Accepts all pending connections virtual void incomingConnection(qintptr socketDescriptor); virtual bool requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url); -protected: + QString _documentRoot; HTTPRequestHandler* _requestHandler; + QTimer* _isListeningTimer; + const quint16 _port; }; #endif // hifi_HTTPManager_h From f87f3eb03319af5b6fedff39b09a2b4f00403866 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 20 Aug 2015 08:02:45 -0600 Subject: [PATCH 06/25] Typo --- libraries/embedded-webserver/src/HTTPManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 0d91672904..c0d49e8638 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -165,7 +165,8 @@ void HTTPManager::isTcpServerListening() { if (!isListening()) { qCWarning(embeddedwebserver) << "Socket on port " << QString::number(_port) << " is no longer listening"; bindSocket(); - }} + } +} bool HTTPManager::bindSocket() { qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); From 2c23dab2e74b5333603be7ba224445135e683833 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 20 Aug 2015 08:03:37 -0600 Subject: [PATCH 07/25] More specific logging --- libraries/embedded-webserver/src/HTTPManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index c0d49e8638..6e953598de 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -171,7 +171,7 @@ void HTTPManager::isTcpServerListening() { bool HTTPManager::bindSocket() { qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); if (!listen(QHostAddress::Any, _port)) { - qCWarning(embeddedwebserver) << "Failed to open HTTP server socket:" << errorString() << " can't continue"; + qCritical() << "Failed to open HTTP server socket:" << errorString() << " can't continue"; QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); } return true; From 129761c002781c0c48341dda89d78142af765279 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 20 Aug 2015 08:10:19 -0600 Subject: [PATCH 08/25] making qtimer interval a const --- libraries/embedded-webserver/src/HTTPManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 6e953598de..72436fc55e 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -21,6 +21,7 @@ #include "HTTPManager.h" const int SOCKET_ERROR_EXIT_CODE = 2; +const int SOCKET_CHECK_INTERVAL_IN_MS = 30000; HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : QTcpServer(parent), @@ -31,7 +32,7 @@ HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestH bindSocket(); _isListeningTimer = new QTimer(this); connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening); - _isListeningTimer->start(10000); + _isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS); } void HTTPManager::incomingConnection(qintptr socketDescriptor) { From c25082d86f45c5562750ddd81e6afb77f159b903 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 20 Aug 2015 10:14:16 -0700 Subject: [PATCH 09/25] use -fsanitize=address in all code rather than just the top-level links --- CMakeLists.txt | 6 ++++++ assignment-client/CMakeLists.txt | 11 +++-------- cmake/macros/MemoryDebugger.cmake | 21 +++++++++++++++++++++ domain-server/CMakeLists.txt | 10 ++-------- gvr-interface/CMakeLists.txt | 4 +++- ice-server/CMakeLists.txt | 4 +++- interface/CMakeLists.txt | 10 ++-------- libraries/animation/CMakeLists.txt | 4 +++- libraries/audio-client/CMakeLists.txt | 4 +++- libraries/audio/CMakeLists.txt | 2 ++ libraries/auto-updater/CMakeLists.txt | 3 +++ libraries/avatars/CMakeLists.txt | 2 ++ libraries/display-plugins/CMakeLists.txt | 4 +++- libraries/embedded-webserver/CMakeLists.txt | 4 +++- libraries/entities-renderer/CMakeLists.txt | 2 ++ libraries/entities/CMakeLists.txt | 2 ++ libraries/environment/CMakeLists.txt | 4 +++- libraries/fbx/CMakeLists.txt | 4 +++- libraries/gpu/CMakeLists.txt | 2 ++ libraries/input-plugins/CMakeLists.txt | 4 +++- libraries/model/CMakeLists.txt | 4 +++- libraries/networking/CMakeLists.txt | 4 +++- libraries/octree/CMakeLists.txt | 2 ++ libraries/physics/CMakeLists.txt | 2 ++ libraries/plugins/CMakeLists.txt | 5 +++-- libraries/render-utils/CMakeLists.txt | 2 ++ libraries/render/CMakeLists.txt | 4 +++- libraries/script-engine/CMakeLists.txt | 2 ++ libraries/shared/CMakeLists.txt | 2 ++ libraries/ui/CMakeLists.txt | 5 +++-- tests/CMakeLists.txt | 2 ++ tests/animation/CMakeLists.txt | 2 ++ tests/audio/CMakeLists.txt | 4 +++- tests/entities/CMakeLists.txt | 4 +++- tests/jitter/CMakeLists.txt | 4 +++- tests/networking/CMakeLists.txt | 4 +++- tests/octree/CMakeLists.txt | 4 +++- tests/physics/CMakeLists.txt | 2 ++ tests/render-utils/CMakeLists.txt | 3 +++ tests/shaders/CMakeLists.txt | 3 +++ tests/shared/CMakeLists.txt | 4 +++- tests/ui/CMakeLists.txt | 4 +++- tools/CMakeLists.txt | 3 ++- tools/mtc/CMakeLists.txt | 4 +++- tools/scribe/CMakeLists.txt | 5 ++++- tools/vhacd-util/CMakeLists.txt | 2 ++ 46 files changed, 143 insertions(+), 50 deletions(-) create mode 100644 cmake/macros/MemoryDebugger.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 38bcb42e26..bc4c938f08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,3 +213,9 @@ endif () if (ANDROID OR DESKTOP_GVR) add_subdirectory(gvr-interface) endif () + +if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + if (UNIX) + MESSAGE("-- Memory debugging is enabled") + endif (UNIX) +endif () diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 7e3b2e6af9..315eeb6b83 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -18,12 +18,7 @@ if (UNIX) endif (UNIX) include_application_version() -copy_dlls_beside_windows_executable() -if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) - if (UNIX) - MESSAGE("-- assignment-client memory debugging is enabled.") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") - endif (UNIX) -endif () +setup_memory_debugger() + +copy_dlls_beside_windows_executable() diff --git a/cmake/macros/MemoryDebugger.cmake b/cmake/macros/MemoryDebugger.cmake new file mode 100644 index 0000000000..cb907efa96 --- /dev/null +++ b/cmake/macros/MemoryDebugger.cmake @@ -0,0 +1,21 @@ +# +# MemoryDebugger.cmake +# +# Copyright 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 +# + +macro(SETUP_MEMORY_DEBUGGER) +if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + SET( HIFI_MEMORY_DEBUGGING true ) +endif () + +if (HIFI_MEMORY_DEBUGGING) + if (UNIX) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libasan -static-libstdc++ -fsanitize=address") + endif (UNIX) +endif () +endmacro(SETUP_MEMORY_DEBUGGER) diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 0d56e34cf2..d2f30b6c25 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME domain-server) +setup_memory_debugger() + if (UPPER_CMAKE_BUILD_TYPE MATCHES DEBUG AND NOT WIN32) set(_SHOULD_SYMLINK_RESOURCES TRUE) else () @@ -33,11 +35,3 @@ target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) include_application_version() copy_dlls_beside_windows_executable() - -if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) - if (UNIX) - MESSAGE("-- domain-server memory debugging is enabled.") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") - endif (UNIX) -endif () diff --git a/gvr-interface/CMakeLists.txt b/gvr-interface/CMakeLists.txt index a986fcae0d..c4880a80b6 100644 --- a/gvr-interface/CMakeLists.txt +++ b/gvr-interface/CMakeLists.txt @@ -88,4 +88,6 @@ if (ANDROID) endif (ANDROID) -copy_dlls_beside_windows_executable() \ No newline at end of file +setup_memory_debugger() + +copy_dlls_beside_windows_executable() diff --git a/ice-server/CMakeLists.txt b/ice-server/CMakeLists.txt index 13d89fc4a2..d62192bcec 100644 --- a/ice-server/CMakeLists.txt +++ b/ice-server/CMakeLists.txt @@ -1,9 +1,11 @@ set(TARGET_NAME ice-server) +setup_memory_debugger() + # setup the project and link required Qt modules setup_hifi_project(Network) # link the shared hifi libraries link_hifi_libraries(embedded-webserver networking shared) -copy_dlls_beside_windows_executable() \ No newline at end of file +copy_dlls_beside_windows_executable() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 16d8bacd23..d858673774 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -205,12 +205,6 @@ else (APPLE) endif() endif (APPLE) -copy_dlls_beside_windows_executable() +setup_memory_debugger() -if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) - if (UNIX) - MESSAGE("-- interface memory debugging is enabled.") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") - endif (UNIX) -endif () +copy_dlls_beside_windows_executable() diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt index 8c75d5620c..fc7fa23dcc 100644 --- a/libraries/animation/CMakeLists.txt +++ b/libraries/animation/CMakeLists.txt @@ -3,4 +3,6 @@ set(TARGET_NAME animation) # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Script) -link_hifi_libraries(shared gpu model fbx) \ No newline at end of file +setup_memory_debugger() + +link_hifi_libraries(shared gpu model fbx) diff --git a/libraries/audio-client/CMakeLists.txt b/libraries/audio-client/CMakeLists.txt index 43a2016acf..c313aecbc0 100644 --- a/libraries/audio-client/CMakeLists.txt +++ b/libraries/audio-client/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME audio-client) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Multimedia) @@ -25,4 +27,4 @@ if (APPLE) find_library(CoreAudio CoreAudio) find_library(CoreFoundation CoreFoundation) target_link_libraries(${TARGET_NAME} ${CoreAudio} ${CoreFoundation}) -endif () \ No newline at end of file +endif () diff --git a/libraries/audio/CMakeLists.txt b/libraries/audio/CMakeLists.txt index c03f588d94..a0d40b1a10 100644 --- a/libraries/audio/CMakeLists.txt +++ b/libraries/audio/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME audio) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network) diff --git a/libraries/auto-updater/CMakeLists.txt b/libraries/auto-updater/CMakeLists.txt index b3665af2cb..6960d8368d 100644 --- a/libraries/auto-updater/CMakeLists.txt +++ b/libraries/auto-updater/CMakeLists.txt @@ -1,3 +1,6 @@ set(TARGET_NAME auto-updater) + +setup_memory_debugger() + setup_hifi_library(Network) link_hifi_libraries(shared networking) diff --git a/libraries/avatars/CMakeLists.txt b/libraries/avatars/CMakeLists.txt index acc939b25c..b05c667c71 100644 --- a/libraries/avatars/CMakeLists.txt +++ b/libraries/avatars/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME avatars) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Script) diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt index 321b13f191..79b41fa957 100644 --- a/libraries/display-plugins/CMakeLists.txt +++ b/libraries/display-plugins/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME display-plugins) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(OpenGL) @@ -31,4 +33,4 @@ if (WIN32) find_package(OpenVR REQUIRED) target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) -endif() \ No newline at end of file +endif() diff --git a/libraries/embedded-webserver/CMakeLists.txt b/libraries/embedded-webserver/CMakeLists.txt index 955487e540..2d8915998b 100644 --- a/libraries/embedded-webserver/CMakeLists.txt +++ b/libraries/embedded-webserver/CMakeLists.txt @@ -1,4 +1,6 @@ set(TARGET_NAME embedded-webserver) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules -setup_hifi_library(Network) \ No newline at end of file +setup_hifi_library(Network) diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index c4dddb8971..3387715348 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -26,4 +26,6 @@ find_package(PolyVox REQUIRED) target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES}) +setup_memory_debugger() + link_hifi_libraries(shared gpu script-engine render render-utils) diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index f7936ff125..368257661e 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME entities) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network Script) diff --git a/libraries/environment/CMakeLists.txt b/libraries/environment/CMakeLists.txt index a2ee9e3f55..fbdc614d26 100644 --- a/libraries/environment/CMakeLists.txt +++ b/libraries/environment/CMakeLists.txt @@ -7,4 +7,6 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(shared networking) \ No newline at end of file +setup_memory_debugger() + +link_hifi_libraries(shared networking) diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index 1ce1c74922..c06bb0efc1 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -7,4 +7,6 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(shared gpu model networking octree) \ No newline at end of file +setup_memory_debugger() + +link_hifi_libraries(shared gpu model networking octree) diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 7a88580f7f..84320297eb 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME gpu) +setup_memory_debugger() + AUTOSCRIBE_SHADER_LIB(gpu) # use setup_hifi_library macro to setup our project and link appropriate Qt modules diff --git a/libraries/input-plugins/CMakeLists.txt b/libraries/input-plugins/CMakeLists.txt index c3ded6c587..4428327deb 100644 --- a/libraries/input-plugins/CMakeLists.txt +++ b/libraries/input-plugins/CMakeLists.txt @@ -33,6 +33,8 @@ endif() #target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS}) #target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES}) +setup_memory_debugger() + # perform standard include and linking for found externals foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) @@ -69,4 +71,4 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) add_definitions(-DSIXENSE_LIB_FILENAME=\"${${${EXTERNAL}_UPPERCASE}_LIBRARY_RELEASE}\") endif () endif () -endforeach() \ No newline at end of file +endforeach() diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 563f347952..2099f83fec 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -1,7 +1,9 @@ set(TARGET_NAME model) - + AUTOSCRIBE_SHADER_LIB(gpu model) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library() diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index d79e6bde58..d0e0b850c7 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME networking) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Network) @@ -29,4 +31,4 @@ target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES} ${TBB_LIBRARIES}) # append tbb includes to our list of includes to bubble target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${TBB_INCLUDE_DIRS}) -include_application_version() \ No newline at end of file +include_application_version() diff --git a/libraries/octree/CMakeLists.txt b/libraries/octree/CMakeLists.txt index cc36aead15..8b9ff6bda2 100644 --- a/libraries/octree/CMakeLists.txt +++ b/libraries/octree/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME octree) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library() diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt index b1f9fbb79c..802665b948 100644 --- a/libraries/physics/CMakeLists.txt +++ b/libraries/physics/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME physics) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library() diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index 28b136ccf4..98fd5fdc93 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -7,6 +7,7 @@ link_hifi_libraries(shared) add_dependency_external_projects(glm) find_package(GLM REQUIRED) + +setup_memory_debugger() + target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) - - diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 0ea71e54e3..ceb1a192ab 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -40,4 +40,6 @@ add_dependency_external_projects(oglplus) find_package(OGLPLUS REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${OGLPLUS_INCLUDE_DIRS}) +setup_memory_debugger() + link_hifi_libraries(animation fbx shared gpu model render environment) diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 4d2be949e6..1f73d93519 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME render) +setup_memory_debugger() + AUTOSCRIBE_SHADER_LIB(gpu model) # use setup_hifi_library macro to setup our project and link appropriate Qt modules @@ -21,4 +23,4 @@ if (WIN32) target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") endif () endif() -endif (WIN32) \ No newline at end of file +endif (WIN32) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 99d9149c3a..6bb53389af 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME script-engine) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules setup_hifi_library(Gui Network Script Widgets) diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 00a80619bc..a80f4194ef 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -1,5 +1,7 @@ set(TARGET_NAME shared) +setup_memory_debugger() + # use setup_hifi_library macro to setup our project and link appropriate Qt modules # TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp) setup_hifi_library(Gui Network Script Widgets) diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index 1aefc99c78..68caa940c1 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -7,6 +7,7 @@ link_hifi_libraries(render-utils shared) add_dependency_external_projects(glm) find_package(GLM REQUIRED) + +setup_memory_debugger() + target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) - - diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1593b649a0..c2dc30c4bb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -35,3 +35,5 @@ set_target_properties("all-tests" PROPERTIES FOLDER "hidden/test-targets") set_target_properties("all-tests" PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE EXCLUDE_FROM_ALL TRUE) + +setup_memory_debugger() diff --git a/tests/animation/CMakeLists.txt b/tests/animation/CMakeLists.txt index 2e9dbc9424..bc1e93a94f 100644 --- a/tests/animation/CMakeLists.txt +++ b/tests/animation/CMakeLists.txt @@ -6,4 +6,6 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () +setup_memory_debugger() + setup_hifi_testcase() diff --git a/tests/audio/CMakeLists.txt b/tests/audio/CMakeLists.txt index 8e894e929e..c56ef049bd 100644 --- a/tests/audio/CMakeLists.txt +++ b/tests/audio/CMakeLists.txt @@ -6,4 +6,6 @@ macro (SETUP_TESTCASE_DEPENDENCIES) copy_dlls_beside_windows_executable() endmacro () -setup_hifi_testcase() \ No newline at end of file +setup_memory_debugger() + +setup_hifi_testcase() diff --git a/tests/entities/CMakeLists.txt b/tests/entities/CMakeLists.txt index 0077549100..f83efe7f64 100644 --- a/tests/entities/CMakeLists.txt +++ b/tests/entities/CMakeLists.txt @@ -9,4 +9,6 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries link_hifi_libraries(entities avatars shared octree gpu model fbx networking animation environment) -copy_dlls_beside_windows_executable() \ No newline at end of file +setup_memory_debugger() + +copy_dlls_beside_windows_executable() diff --git a/tests/jitter/CMakeLists.txt b/tests/jitter/CMakeLists.txt index 7b636aa87f..ba46582b02 100644 --- a/tests/jitter/CMakeLists.txt +++ b/tests/jitter/CMakeLists.txt @@ -7,4 +7,6 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro() -setup_hifi_testcase() \ No newline at end of file +setup_memory_debugger() + +setup_hifi_testcase() diff --git a/tests/networking/CMakeLists.txt b/tests/networking/CMakeLists.txt index 3be2fff027..fcf32d89c8 100644 --- a/tests/networking/CMakeLists.txt +++ b/tests/networking/CMakeLists.txt @@ -7,4 +7,6 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () -setup_hifi_testcase() \ No newline at end of file +setup_memory_debugger() + +setup_hifi_testcase() diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index a605a4088b..77511c682a 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -7,4 +7,6 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () -setup_hifi_testcase(Script Network) \ No newline at end of file +setup_memory_debugger() + +setup_hifi_testcase(Script Network) diff --git a/tests/physics/CMakeLists.txt b/tests/physics/CMakeLists.txt index 36cf21c681..1a6f49430b 100644 --- a/tests/physics/CMakeLists.txt +++ b/tests/physics/CMakeLists.txt @@ -21,4 +21,6 @@ macro (SETUP_TESTCASE_DEPENDENCIES) copy_dlls_beside_windows_executable() endmacro () +setup_memory_debugger() + setup_hifi_testcase(Script) diff --git a/tests/render-utils/CMakeLists.txt b/tests/render-utils/CMakeLists.txt index 97d3214744..1b47f85099 100644 --- a/tests/render-utils/CMakeLists.txt +++ b/tests/render-utils/CMakeLists.txt @@ -9,4 +9,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") link_hifi_libraries(render-utils gpu shared) message(${PROJECT_BINARY_DIR}) + +setup_memory_debugger() + copy_dlls_beside_windows_executable() diff --git a/tests/shaders/CMakeLists.txt b/tests/shaders/CMakeLists.txt index eefdf2aa3a..3ee9f4ae9f 100644 --- a/tests/shaders/CMakeLists.txt +++ b/tests/shaders/CMakeLists.txt @@ -18,4 +18,7 @@ include_directories("${PROJECT_BINARY_DIR}/../../libraries/entities-renderer/") include_directories("${PROJECT_BINARY_DIR}/../../libraries/model/") message(${PROJECT_BINARY_DIR}) + +setup_memory_debugger() + copy_dlls_beside_windows_executable() diff --git a/tests/shared/CMakeLists.txt b/tests/shared/CMakeLists.txt index bc6eab0212..c3d6cfd810 100644 --- a/tests/shared/CMakeLists.txt +++ b/tests/shared/CMakeLists.txt @@ -8,4 +8,6 @@ macro (setup_testcase_dependencies) copy_dlls_beside_windows_executable() endmacro () -setup_hifi_testcase() \ No newline at end of file +setup_memory_debugger() + +setup_hifi_testcase() diff --git a/tests/ui/CMakeLists.txt b/tests/ui/CMakeLists.txt index ad1009d925..c0f20a280f 100644 --- a/tests/ui/CMakeLists.txt +++ b/tests/ui/CMakeLists.txt @@ -13,4 +13,6 @@ endif() # link in the shared libraries link_hifi_libraries(ui render-utils gpu shared) -copy_dlls_beside_windows_executable() \ No newline at end of file +setup_memory_debugger() + +copy_dlls_beside_windows_executable() diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 55994f3d89..4d8618b37c 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,3 +1,5 @@ +setup_memory_debugger() + # add the tool directories add_subdirectory(mtc) set_target_properties(mtc PROPERTIES FOLDER "Tools") @@ -7,4 +9,3 @@ set_target_properties(scribe PROPERTIES FOLDER "Tools") add_subdirectory(vhacd-util) set_target_properties(vhacd-util PROPERTIES FOLDER "Tools") - diff --git a/tools/mtc/CMakeLists.txt b/tools/mtc/CMakeLists.txt index 5c598eaf0b..fe5f1920bc 100644 --- a/tools/mtc/CMakeLists.txt +++ b/tools/mtc/CMakeLists.txt @@ -1,4 +1,6 @@ set(TARGET_NAME mtc) setup_hifi_project() -copy_dlls_beside_windows_executable() \ No newline at end of file +setup_memory_debugger() + +copy_dlls_beside_windows_executable() diff --git a/tools/scribe/CMakeLists.txt b/tools/scribe/CMakeLists.txt index b71a287e46..0abf70f727 100755 --- a/tools/scribe/CMakeLists.txt +++ b/tools/scribe/CMakeLists.txt @@ -1,2 +1,5 @@ set(TARGET_NAME scribe) -setup_hifi_project() \ No newline at end of file + +setup_memory_debugger() + +setup_hifi_project() diff --git a/tools/vhacd-util/CMakeLists.txt b/tools/vhacd-util/CMakeLists.txt index c94b2ad083..b79a6a1893 100644 --- a/tools/vhacd-util/CMakeLists.txt +++ b/tools/vhacd-util/CMakeLists.txt @@ -8,6 +8,8 @@ find_package(VHACD REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${VHACD_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${VHACD_LIBRARIES}) +setup_memory_debugger() + if (UNIX AND NOT APPLE) include(FindOpenMP) if(OPENMP_FOUND) From 0ac885da80ab10197608c5c7f902d6259deffc63 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 20 Aug 2015 10:17:13 -0700 Subject: [PATCH 10/25] fix when memory debugging message is printed --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc4c938f08..b2f35b1443 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,6 +215,9 @@ if (ANDROID OR DESKTOP_GVR) endif () if (DEFINED ENV{HIFI_MEMORY_DEBUGGING}) + SET( HIFI_MEMORY_DEBUGGING true ) +endif () +if (HIFI_MEMORY_DEBUGGING) if (UNIX) MESSAGE("-- Memory debugging is enabled") endif (UNIX) From aac15e52bb223fac707f9c2f4e77ab6982ef11d1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 20 Aug 2015 10:19:34 -0700 Subject: [PATCH 11/25] add explicit cast to double for uptime --- domain-server/src/DomainServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 369cb3b761..23957380e6 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1502,7 +1502,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { nodeJson[JSON_KEY_LOCAL_SOCKET] = jsonForSocket(node->getLocalSocket()); // add the node uptime in our list - nodeJson[JSON_KEY_UPTIME] = QString::number((QDateTime::currentMSecsSinceEpoch() - node->getWakeTimestamp()) / 1000.0); + nodeJson[JSON_KEY_UPTIME] = QString::number(double(QDateTime::currentMSecsSinceEpoch() - node->getWakeTimestamp()) / 1000.0); // if the node has pool information, add it DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); From e93b360908141742750fe10a231bc41e6132effa Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 20 Aug 2015 12:12:43 -0700 Subject: [PATCH 12/25] expose options property to AI script interface --- libraries/audio/src/AudioInjector.cpp | 30 +++++++++---------- libraries/audio/src/AudioInjector.h | 9 +++--- libraries/avatars/src/Player.cpp | 2 +- .../script-engine/src/ScriptAudioInjector.h | 6 ++-- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 22d57176a5..8fd7cb9ce5 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -77,9 +77,9 @@ void AudioInjector::injectAudio() { int byteOffset = (int) floorf(AudioConstants::SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f)); byteOffset *= sizeof(int16_t); - _currentSendPosition = byteOffset; + _currentSendOffset = byteOffset; } else { - _currentSendPosition = 0; + _currentSendOffset = 0; } if (_options.localOnly) { @@ -119,7 +119,7 @@ void AudioInjector::injectLocally() { _localBuffer->setVolume(_options.volume); // give our current send position to the local buffer - _localBuffer->setCurrentOffset(_currentSendPosition); + _localBuffer->setCurrentOffset(_currentSendOffset); success = _localAudioInterface->outputLocalInjector(_options.stereo, this); @@ -144,9 +144,9 @@ void AudioInjector::injectLocally() { const uchar MAX_INJECTOR_VOLUME = 0xFF; void AudioInjector::injectToMixer() { - if (_currentSendPosition < 0 || - _currentSendPosition >= _audioData.size()) { - _currentSendPosition = 0; + if (_currentSendOffset < 0 || + _currentSendOffset >= _audioData.size()) { + _currentSendOffset = 0; } auto nodeList = DependencyManager::get(); @@ -203,15 +203,15 @@ void AudioInjector::injectToMixer() { // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks quint16 outgoingInjectedAudioSequenceNumber = 0; - while (_currentSendPosition < _audioData.size() && !_shouldStop) { + while (_currentSendOffset < _audioData.size() && !_shouldStop) { int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, - _audioData.size() - _currentSendPosition); + _audioData.size() - _currentSendOffset); // Measure the loudness of this frame _loudness = 0.0f; for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { - _loudness += abs(*reinterpret_cast(_audioData.data() + _currentSendPosition + i)) / + _loudness += abs(*reinterpret_cast(_audioData.data() + _currentSendOffset + i)) / (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); } _loudness /= (float)(bytesToCopy / sizeof(int16_t)); @@ -220,7 +220,7 @@ void AudioInjector::injectToMixer() { // pack the sequence number audioPacket->writePrimitive(outgoingInjectedAudioSequenceNumber); - + audioPacket->seek(positionOptionOffset); audioPacket->writePrimitive(_options.position); audioPacket->writePrimitive(_options.orientation); @@ -232,7 +232,7 @@ void AudioInjector::injectToMixer() { audioPacket->seek(audioDataOffset); // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet - audioPacket->write(_audioData.data() + _currentSendPosition, bytesToCopy); + audioPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy); // set the correct size used for this packet audioPacket->setPayloadSize(audioPacket->pos()); @@ -246,11 +246,11 @@ void AudioInjector::injectToMixer() { outgoingInjectedAudioSequenceNumber++; } - _currentSendPosition += bytesToCopy; + _currentSendOffset += bytesToCopy; // send two packets before the first sleep so the mixer can start playback right away - if (_currentSendPosition != bytesToCopy && _currentSendPosition < _audioData.size()) { + if (_currentSendOffset != bytesToCopy && _currentSendOffset < _audioData.size()) { // process events in case we have been told to stop and be deleted QCoreApplication::processEvents(); @@ -268,8 +268,8 @@ void AudioInjector::injectToMixer() { } } - if (shouldLoop && _currentSendPosition >= _audioData.size()) { - _currentSendPosition = 0; + if (shouldLoop && _currentSendOffset >= _audioData.size()) { + _currentSendOffset = 0; } } } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 88d3f1e151..d65925b865 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -31,7 +31,6 @@ class AbstractAudioInterface; class AudioInjector : public QObject { Q_OBJECT - Q_PROPERTY(AudioInjectorOptions options WRITE setOptions READ getOptions) public: AudioInjector(QObject* parent); AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); @@ -39,7 +38,8 @@ public: bool isFinished() const { return _isFinished; } - int getCurrentSendPosition() const { return _currentSendPosition; } + int getCurrentSendOffset() const { return _currentSendOffset; } + void setCurrentSendOffset(int currentSendOffset) { _currentSendOffset = currentSendOffset; } AudioInjectorLocalBuffer* getLocalBuffer() const { return _localBuffer; } bool isLocalOnly() const { return _options.localOnly; } @@ -58,9 +58,8 @@ public slots: void stopAndDeleteLater(); const AudioInjectorOptions& getOptions() const { return _options; } - void setOptions(const AudioInjectorOptions& options) { _options = options; } + void setOptions(const AudioInjectorOptions& options) { _options = options; } - void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } float getLoudness() const { return _loudness; } bool isPlaying() const { return _isPlaying; } void restartPortionAfterFinished(); @@ -82,7 +81,7 @@ private: bool _isStarted = false; bool _isFinished = false; bool _shouldDeleteAfterFinish = false; - int _currentSendPosition = 0; + int _currentSendOffset = 0; AbstractAudioInterface* _localAudioInterface = NULL; AudioInjectorLocalBuffer* _localBuffer = NULL; }; diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index e7d94f0735..29544924b2 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -371,7 +371,7 @@ void Player::setAudioInjectorPosition() { int MSEC_PER_SEC = 1000; int FRAME_SIZE = sizeof(AudioConstants::AudioSample) * _recording->numberAudioChannel(); int currentAudioFrame = elapsed() * FRAME_SIZE * (AudioConstants::SAMPLE_RATE / MSEC_PER_SEC); - _injector->setCurrentSendPosition(currentAudioFrame); + _injector->setCurrentSendOffset(currentAudioFrame); } void Player::setPlayFromCurrentLocation(bool playFromCurrentLocation) { diff --git a/libraries/script-engine/src/ScriptAudioInjector.h b/libraries/script-engine/src/ScriptAudioInjector.h index 92bc5d31da..0d16b26fdf 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.h +++ b/libraries/script-engine/src/ScriptAudioInjector.h @@ -21,13 +21,15 @@ class ScriptAudioInjector : public QObject { Q_PROPERTY(bool isPlaying READ isPlaying) Q_PROPERTY(float loudness READ getLoudness) + Q_PROPERTY(AudioInjectorOptions options WRITE setOptions READ getOptions) public: ScriptAudioInjector(AudioInjector* injector); ~ScriptAudioInjector(); public slots: void restart() { _injector->restart(); } void stop() { _injector->stop(); } - + + const AudioInjectorOptions& getOptions() const { return _injector->getOptions(); } void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); } float getLoudness() const { return _injector->getLoudness(); } @@ -49,4 +51,4 @@ Q_DECLARE_METATYPE(ScriptAudioInjector*) QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in); void injectorFromScriptValue(const QScriptValue& object, ScriptAudioInjector*& out); -#endif // hifi_ScriptAudioInjector_h \ No newline at end of file +#endif // hifi_ScriptAudioInjector_h From f26849c7e15a25b836308f9c3a613e43ef48eded Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 20 Aug 2015 12:34:50 -0700 Subject: [PATCH 13/25] Proper HMD scaling. --- interface/src/avatar/MyAvatar.cpp | 40 +++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 52473a6d47..cd968c3d59 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -846,7 +846,7 @@ void MyAvatar::sendKillAvatar() { void MyAvatar::updateLookAtTargetAvatar() { // // Look at the avatar whose eyes are closest to the ray in direction of my avatar's head - // + // And set the correctedLookAt for all (nearby) avatars that are looking at me. _lookAtTargetAvatar.reset(); _targetAvatarPosition = glm::vec3(0.0f); @@ -870,14 +870,39 @@ void MyAvatar::updateLookAtTargetAvatar() { smallestAngleTo = angleTo; } if (Application::getInstance()->isLookingAtMyAvatar(avatar)) { + // Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face. // Offset their gaze according to whether they're looking at one of my eyes or my mouth. - glm::vec3 gazeOffset = avatar->getHead()->getLookAtPosition() - getHead()->getEyePosition(); - const float HUMAN_EYE_SEPARATION = 0.065f; - float myEyeSeparation = glm::length(getHead()->getLeftEyePosition() - getHead()->getRightEyePosition()); - gazeOffset = gazeOffset * HUMAN_EYE_SEPARATION / myEyeSeparation; - avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition() - + gazeOffset); + + // The camera isn't at the point midway between the avatar eyes. (Even without an HMD, the head can be offset a bit.) + // First find out where (in world space) the person is looking relative to that bridge-of-the-avatar point. + // (We will be adding that offset to the camera position, after making some other adjustments.) + glm::vec3 lookAtPosition = avatar->getHead()->getLookAtPosition(); + glm::vec3 gazeOffset = lookAtPosition - getHead()->getEyePosition(); + + // Scale by proportional differences between avatar and human. + glm::mat4 leftEye = Application::getInstance()->getEyeOffset(Eye::Left); // Pose? + glm::mat4 rightEye = Application::getInstance()->getEyeOffset(Eye::Right); + glm::vec3 leftEyePosition = glm::vec3(leftEye[3]); + glm::vec3 rightEyePosition = glm::vec3(rightEye[3]); + float humanEyeSeparationInModelSpace = glm::length(leftEyePosition - rightEyePosition); + float avatarEyeSeparation = glm::length(getHead()->getLeftEyePosition() - getHead()->getRightEyePosition()); + gazeOffset = gazeOffset * humanEyeSeparationInModelSpace / avatarEyeSeparation; + + // If the camera is also not oriented with the head, adjust by getting the offset in head-space... + /* Not needed (i.e., code is a no-op), but I'm leaving the example code here in case something like this is needed someday. + glm::quat avatarHeadOrientation = getHead()->getOrientation(); + glm::vec3 gazeOffsetLocalToHead = glm::inverse(avatarHeadOrientation) * gazeOffset; + // ... and treat that as though it were in camera space, bringing it back to world space. + // But camera is fudged to make the picture feel like the avatar's orientation. + glm::quat humanOrientation = Application::getInstance()->getViewFrustum()->getOrientation(); // or just avatar getOrienation() ? + gazeOffset = humanOrientation * gazeOffsetLocalToHead; + */ + + // And now we can finally add that offset to the camera. + glm::vec3 corrected = Application::getInstance()->getViewFrustum()->getPosition() + gazeOffset; + avatar->getHead()->setCorrectedLookAtPosition(corrected); + } else { avatar->getHead()->clearCorrectedLookAtPosition(); } @@ -1114,6 +1139,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl getHead()->render(renderArgs, 1.0f, renderFrustum); } + // This is drawing the lookat vectors from our avatar to wherever we're looking. if (qApp->isHMDMode()) { glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); From e9b3d481650e7fdfa1baa6044b07677fc22aba0b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 20 Aug 2015 16:10:31 -0700 Subject: [PATCH 14/25] Add menu item that disables adjusting eyelids to follow pupil Developer > Avatar > Disable Eyelid Adjustment --- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + interface/src/avatar/Head.cpp | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f074dc5ac7..11bc38c85e 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -444,6 +444,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Connexion, 0, false, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index ca46b80f92..278da363d1 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -168,6 +168,7 @@ namespace MenuOption { const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DeleteBookmark = "Delete Bookmark..."; const QString DisableActivityLogger = "Disable Activity Logger"; + const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment"; const QString DisableLightEntities = "Disable Light Entities"; const QString DisableNackPackets = "Disable Entity NACK Packets"; const QString DiskCacheEditor = "Disk Cache Editor"; diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 9ab2c83a79..d645253eab 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -277,6 +277,10 @@ void Head::calculateMouthShapes() { void Head::applyEyelidOffset(glm::quat headOrientation) { // Adjusts the eyelid blendshape coefficients so that the eyelid follows the iris as the head pitches. + if (Menu::getInstance()->isOptionChecked(MenuOption::DisableEyelidAdjustment)) { + return; + } + glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FRONT, getLookAtPosition() - _eyePosition); eyeRotation = eyeRotation * glm::angleAxis(safeEulerAngles(headOrientation).y, IDENTITY_UP); // Rotation w.r.t. head float eyePitch = safeEulerAngles(eyeRotation).x; From 05f4145acb97974b8fb40a85ac28e2a333b647c0 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 20 Aug 2015 16:28:40 -0700 Subject: [PATCH 15/25] Checkpoint. Working? --- interface/src/avatar/MyAvatar.cpp | 54 ++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cd968c3d59..1b8a3b2f89 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -843,6 +843,7 @@ void MyAvatar::sendKillAvatar() { DependencyManager::get()->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::AvatarMixer); } +static int counter = 0; void MyAvatar::updateLookAtTargetAvatar() { // // Look at the avatar whose eyes are closest to the ray in direction of my avatar's head @@ -872,21 +873,34 @@ void MyAvatar::updateLookAtTargetAvatar() { if (Application::getInstance()->isLookingAtMyAvatar(avatar)) { // Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face. - // Offset their gaze according to whether they're looking at one of my eyes or my mouth. + glm::vec3 lookAtPosition = avatar->getHead()->getLookAtPosition(); // A position, in world space, on my avatar. // The camera isn't at the point midway between the avatar eyes. (Even without an HMD, the head can be offset a bit.) + // Let's get everything to world space: + glm::vec3 avatarLeftEye = getHead()->getLeftEyePosition(); + glm::vec3 avatarRightEye = getHead()->getRightEyePosition(); + // When not in HMD, these might both answer identity (i.e., the bridge of the nose). That's ok. + // By my inpsection of the code and live testing, getEyeOffset and getEyePose are the same. (Application hands identity as offset matrix.) + glm::mat4 leftEye = Application::getInstance()->getEyeOffset(Eye::Left); + glm::mat4 rightEye = Application::getInstance()->getEyeOffset(Eye::Right); + glm::vec3 leftEyeHeadLocal = glm::vec3(leftEye[3]); + glm::vec3 rightEyeHeadLocal = glm::vec3(rightEye[3]); + auto humanSystem = Application::getInstance()->getViewFrustum(); + glm::vec3 humanLeftEye = humanSystem->getPosition() + (humanSystem->getOrientation() * leftEyeHeadLocal); + glm::vec3 humanRightEye = humanSystem->getPosition() + (humanSystem->getOrientation() * rightEyeHeadLocal); + + // debugging or some code paths + glm::vec3 avatarAverage = avatarLeftEye + ((avatarRightEye - avatarLeftEye) * 0.5f); + glm::vec3 humanAverage = humanLeftEye + ((humanRightEye - humanLeftEye) * 0.5f); + +#if 1 // First find out where (in world space) the person is looking relative to that bridge-of-the-avatar point. // (We will be adding that offset to the camera position, after making some other adjustments.) - glm::vec3 lookAtPosition = avatar->getHead()->getLookAtPosition(); glm::vec3 gazeOffset = lookAtPosition - getHead()->getEyePosition(); // Scale by proportional differences between avatar and human. - glm::mat4 leftEye = Application::getInstance()->getEyeOffset(Eye::Left); // Pose? - glm::mat4 rightEye = Application::getInstance()->getEyeOffset(Eye::Right); - glm::vec3 leftEyePosition = glm::vec3(leftEye[3]); - glm::vec3 rightEyePosition = glm::vec3(rightEye[3]); - float humanEyeSeparationInModelSpace = glm::length(leftEyePosition - rightEyePosition); - float avatarEyeSeparation = glm::length(getHead()->getLeftEyePosition() - getHead()->getRightEyePosition()); + float humanEyeSeparationInModelSpace = glm::length(humanLeftEye - humanRightEye); + float avatarEyeSeparation = glm::length(avatarLeftEye - avatarRightEye); gazeOffset = gazeOffset * humanEyeSeparationInModelSpace / avatarEyeSeparation; // If the camera is also not oriented with the head, adjust by getting the offset in head-space... @@ -895,13 +909,30 @@ void MyAvatar::updateLookAtTargetAvatar() { glm::vec3 gazeOffsetLocalToHead = glm::inverse(avatarHeadOrientation) * gazeOffset; // ... and treat that as though it were in camera space, bringing it back to world space. // But camera is fudged to make the picture feel like the avatar's orientation. - glm::quat humanOrientation = Application::getInstance()->getViewFrustum()->getOrientation(); // or just avatar getOrienation() ? + glm::quat humanOrientation = humanSystem->getOrientation(); // or just avatar getOrienation() ? gazeOffset = humanOrientation * gazeOffsetLocalToHead; - */ + glm::vec3 corrected = humanSystem->getPosition() + gazeOffset; + */ // And now we can finally add that offset to the camera. glm::vec3 corrected = Application::getInstance()->getViewFrustum()->getPosition() + gazeOffset; +#else + //glm::vec3 gazeOffset = ((humanRightEye - avatarRightEye) + (humanLeftEye - avatarLeftEye)) * 0.5f; + glm::vec3 gazeOffset = humanAverage - avatarAverage; + glm::vec3 corrected = lookAtPosition + gazeOffset; +#endif avatar->getHead()->setCorrectedLookAtPosition(corrected); + + if (counter++ > 60) { + counter = 0; + qCDebug(interfaceapp) << Application::getInstance()->isHMDMode(); + qCDebug(interfaceapp) << "camera:" << Application::getInstance()->getViewFrustum()->getPosition() << "delta from av human:" << (humanAverage - Application::getInstance()->getViewFrustum()->getPosition()); + + qCDebug(interfaceapp) << "lt avatar:" << avatarLeftEye << " lt human:" << humanLeftEye; + qCDebug(interfaceapp) << "rt avatar:" << avatarRightEye << " rt human:" << humanRightEye; + qCDebug(interfaceapp) << "av avatar:" << avatarAverage << " av humn:" << humanAverage; + qCDebug(interfaceapp) << "offset:" << gazeOffset << " corrected:" << corrected << " from:" << lookAtPosition; + } } else { avatar->getHead()->clearCorrectedLookAtPosition(); @@ -1140,6 +1171,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl } // This is drawing the lookat vectors from our avatar to wherever we're looking. + /* if (qApp->isHMDMode()) { glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); @@ -1155,7 +1187,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl cameraPosition + getOrientation() * (rightEyePosition - headPosition)); } else { getHead()->renderLookAts(renderArgs); - } + }*/ getHand()->render(renderArgs, true); } From ea02583875178380c429046fd5e8ef3fcfb8258a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 20 Aug 2015 17:33:10 -0700 Subject: [PATCH 16/25] Cleanup. --- interface/src/avatar/MyAvatar.cpp | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1b8a3b2f89..501c7eb8d5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -881,6 +881,7 @@ void MyAvatar::updateLookAtTargetAvatar() { glm::vec3 avatarRightEye = getHead()->getRightEyePosition(); // When not in HMD, these might both answer identity (i.e., the bridge of the nose). That's ok. // By my inpsection of the code and live testing, getEyeOffset and getEyePose are the same. (Application hands identity as offset matrix.) + // This might be more work than needed for any given use, but as we explore different formulations, we go mad if we don't work in world space. glm::mat4 leftEye = Application::getInstance()->getEyeOffset(Eye::Left); glm::mat4 rightEye = Application::getInstance()->getEyeOffset(Eye::Right); glm::vec3 leftEyeHeadLocal = glm::vec3(leftEye[3]); @@ -888,12 +889,8 @@ void MyAvatar::updateLookAtTargetAvatar() { auto humanSystem = Application::getInstance()->getViewFrustum(); glm::vec3 humanLeftEye = humanSystem->getPosition() + (humanSystem->getOrientation() * leftEyeHeadLocal); glm::vec3 humanRightEye = humanSystem->getPosition() + (humanSystem->getOrientation() * rightEyeHeadLocal); - - // debugging or some code paths - glm::vec3 avatarAverage = avatarLeftEye + ((avatarRightEye - avatarLeftEye) * 0.5f); - glm::vec3 humanAverage = humanLeftEye + ((humanRightEye - humanLeftEye) * 0.5f); -#if 1 + // First find out where (in world space) the person is looking relative to that bridge-of-the-avatar point. // (We will be adding that offset to the camera position, after making some other adjustments.) glm::vec3 gazeOffset = lookAtPosition - getHead()->getEyePosition(); @@ -916,23 +913,8 @@ void MyAvatar::updateLookAtTargetAvatar() { // And now we can finally add that offset to the camera. glm::vec3 corrected = Application::getInstance()->getViewFrustum()->getPosition() + gazeOffset; -#else - //glm::vec3 gazeOffset = ((humanRightEye - avatarRightEye) + (humanLeftEye - avatarLeftEye)) * 0.5f; - glm::vec3 gazeOffset = humanAverage - avatarAverage; - glm::vec3 corrected = lookAtPosition + gazeOffset; -#endif + avatar->getHead()->setCorrectedLookAtPosition(corrected); - - if (counter++ > 60) { - counter = 0; - qCDebug(interfaceapp) << Application::getInstance()->isHMDMode(); - qCDebug(interfaceapp) << "camera:" << Application::getInstance()->getViewFrustum()->getPosition() << "delta from av human:" << (humanAverage - Application::getInstance()->getViewFrustum()->getPosition()); - - qCDebug(interfaceapp) << "lt avatar:" << avatarLeftEye << " lt human:" << humanLeftEye; - qCDebug(interfaceapp) << "rt avatar:" << avatarRightEye << " rt human:" << humanRightEye; - qCDebug(interfaceapp) << "av avatar:" << avatarAverage << " av humn:" << humanAverage; - qCDebug(interfaceapp) << "offset:" << gazeOffset << " corrected:" << corrected << " from:" << lookAtPosition; - } } else { avatar->getHead()->clearCorrectedLookAtPosition(); From 642e56903329bbbe3c70d0795f40a2fc3f7913f7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 20 Aug 2015 17:38:49 -0700 Subject: [PATCH 17/25] Uncomment code that was commented out to simplify debugging. --- interface/src/avatar/MyAvatar.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 501c7eb8d5..eada41eb29 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1153,7 +1153,6 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl } // This is drawing the lookat vectors from our avatar to wherever we're looking. - /* if (qApp->isHMDMode()) { glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); @@ -1169,7 +1168,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl cameraPosition + getOrientation() * (rightEyePosition - headPosition)); } else { getHead()->renderLookAts(renderArgs); - }*/ + } getHand()->render(renderArgs, true); } From 1b3d7fabc8a4ac6021441d671d249885ee4c741d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 7 Aug 2015 17:58:43 -0700 Subject: [PATCH 18/25] ResourceCache, NetworkGeometry and Model refactoring and optimizations. * Removed validation logic from Resource class, Qt does this internally and is more standards compliant. This should result in more accurate caching and faster resource fetching when cache is stale and validation fails. * Added loaded and failed slots to Resource class, so it does not have to be polled. * NetworkGeometry now uses multiple Resource objects to download the fst/mapping file and the fbx/obj models. * NetworkGeometry is no longer a subclass of Resource * NetworkGeometry now has signals for success and failure, you no longer have to poll it to determine when loading is complete (except for textures *sigh*) Some functionality was removed * NetworkGeometry no longer has a fallback * NetworkGeometry no longer loads LODs or has lod logic. * The number of FBXGeometry copies is greatly reduced. * Model::setURL no supports fallback URL, delayLoad or retainCurrent option. This can result in a pop when switching avatars, and there's no longer a default if avatar loading fails. --- interface/src/ModelPackager.cpp | 11 +- interface/src/ModelPackager.h | 4 +- interface/src/avatar/Avatar.cpp | 24 +- interface/src/avatar/Head.cpp | 3 - libraries/animation/src/AnimationCache.cpp | 17 +- libraries/animation/src/AnimationCache.h | 9 +- .../src/RenderableZoneEntityItem.cpp | 4 +- libraries/fbx/src/FBXReader.cpp | 22 +- libraries/fbx/src/FBXReader.h | 4 +- libraries/fbx/src/OBJReader.cpp | 9 +- libraries/fbx/src/OBJReader.h | 4 +- libraries/networking/src/ResourceCache.cpp | 115 +-- libraries/networking/src/ResourceCache.h | 17 +- libraries/render-utils/src/GeometryCache.cpp | 806 +++++++----------- libraries/render-utils/src/GeometryCache.h | 145 ++-- libraries/render-utils/src/Model.cpp | 175 +--- libraries/render-utils/src/Model.h | 21 +- tests/networking/src/ResourceTests.cpp | 95 +++ tests/networking/src/ResourceTests.h | 23 + tools/vhacd-util/src/VHACDUtil.cpp | 7 +- 20 files changed, 682 insertions(+), 833 deletions(-) create mode 100644 tests/networking/src/ResourceTests.cpp create mode 100644 tests/networking/src/ResourceTests.h diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 09d572c31d..0b564f3574 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -106,16 +106,17 @@ bool ModelPackager::loadModel() { } qCDebug(interfaceapp) << "Reading FBX file : " << _fbxInfo.filePath(); QByteArray fbxContents = fbx.readAll(); - _geometry = readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath()); - + + _geometry.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath())); + // make sure we have some basic mappings - populateBasicMapping(_mapping, _fbxInfo.filePath(), _geometry); + populateBasicMapping(_mapping, _fbxInfo.filePath(), *_geometry); return true; } bool ModelPackager::editProperties() { // open the dialog to configure the rest - ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), _geometry); + ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), *_geometry); if (properties.exec() == QDialog::Rejected) { return false; } @@ -339,7 +340,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename void ModelPackager::listTextures() { _textures.clear(); - foreach (FBXMesh mesh, _geometry.meshes) { + foreach (FBXMesh mesh, _geometry->meshes) { foreach (FBXMeshPart part, mesh.parts) { if (!part.diffuseTexture.filename.isEmpty() && part.diffuseTexture.content.isEmpty() && !_textures.contains(part.diffuseTexture.filename)) { diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index c681ae436f..10942833f9 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -39,11 +39,11 @@ private: QString _texDir; QVariantHash _mapping; - FBXGeometry _geometry; + std::unique_ptr _geometry; QStringList _textures; }; -#endif // hifi_ModelPackager_h \ No newline at end of file +#endif // hifi_ModelPackager_h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index c1a1d11268..19f84018f8 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -196,7 +196,6 @@ void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("hand"); getHand()->simulate(deltaTime, false); } - _skeletonModel.setLODDistance(getLODDistance()); if (!_shouldRenderBillboard && inViewFrustum) { { @@ -562,24 +561,22 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::fixupModelsInScene() { - if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { - return; - } // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene render::ScenePointer scene = Application::getInstance()->getMain3DScene(); render::PendingChanges pendingChanges; - if (_skeletonModel.needsFixupInScene()) { + if (_skeletonModel.isRenderable() && _skeletonModel.needsFixupInScene()) { _skeletonModel.removeFromScene(scene, pendingChanges); _skeletonModel.addToScene(scene, pendingChanges); } - if (getHead()->getFaceModel().needsFixupInScene()) { - getHead()->getFaceModel().removeFromScene(scene, pendingChanges); - getHead()->getFaceModel().addToScene(scene, pendingChanges); + Model& faceModel = getHead()->getFaceModel(); + if (faceModel.isRenderable() && faceModel.needsFixupInScene()) { + faceModel.removeFromScene(scene, pendingChanges); + faceModel.addToScene(scene, pendingChanges); } for (auto attachmentModel : _attachmentModels) { - if (attachmentModel->needsFixupInScene()) { + if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) { attachmentModel->removeFromScene(scene, pendingChanges); attachmentModel->addToScene(scene, pendingChanges); } @@ -621,11 +618,8 @@ void Avatar::simulateAttachments(float deltaTime) { int jointIndex = getJointIndex(attachment.jointName); glm::vec3 jointPosition; glm::quat jointRotation; - if (!isMyAvatar()) { - model->setLODDistance(getLODDistance()); - } if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) && - _skeletonModel.getJointCombinedRotation(jointIndex, jointRotation)) { + _skeletonModel.getJointCombinedRotation(jointIndex, jointRotation)) { model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale); model->setRotation(jointRotation * attachment.rotation); model->setScaleToFit(true, _scale * attachment.scale, true); // hack to force rescale @@ -978,12 +972,12 @@ void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const { void Avatar::setFaceModelURL(const QUrl& faceModelURL) { AvatarData::setFaceModelURL(faceModelURL); - getHead()->getFaceModel().setURL(_faceModelURL, AvatarData::defaultFullAvatarModelUrl(), true, !isMyAvatar()); + getHead()->getFaceModel().setURL(_faceModelURL); } void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { AvatarData::setSkeletonModelURL(skeletonModelURL); - _skeletonModel.setURL(_skeletonModelURL, AvatarData::defaultFullAvatarModelUrl(), true, !isMyAvatar()); + _skeletonModel.setURL(_skeletonModelURL); } void Avatar::setAttachmentData(const QVector& attachmentData) { diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index d645253eab..3806dd6edc 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -233,9 +233,6 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { _saccade = glm::vec3(); } - if (!isMine) { - _faceModel.setLODDistance(static_cast(_owningAvatar)->getLODDistance()); - } _leftEyePosition = _rightEyePosition = getPosition(); if (!billboard) { _faceModel.simulate(deltaTime); diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index 634e0589b7..7f3f393a8b 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -13,6 +13,7 @@ #include #include "AnimationCache.h" +#include "AnimationLogging.h" static int animationPointerMetaTypeId = qRegisterMetaType(); @@ -62,11 +63,15 @@ void AnimationReader::run() { QSharedPointer animation = _animation.toStrongRef(); if (!animation.isNull()) { QMetaObject::invokeMethod(animation.data(), "setGeometry", - Q_ARG(const FBXGeometry&, readFBX(_reply->readAll(), QVariantHash(), _reply->property("url").toString()))); + Q_ARG(FBXGeometry*, readFBX(_reply->readAll(), QVariantHash(), _reply->property("url").toString()))); } _reply->deleteLater(); } +bool Animation::isLoaded() const { + return _loaded && _geometry; +} + QStringList Animation::getJointNames() const { if (QThread::currentThread() != thread()) { QStringList result; @@ -75,7 +80,7 @@ QStringList Animation::getJointNames() const { return result; } QStringList names; - foreach (const FBXJoint& joint, _geometry.joints) { + foreach (const FBXJoint& joint, _geometry->joints) { names.append(joint.name); } return names; @@ -88,15 +93,15 @@ QVector Animation::getFrames() const { Q_RETURN_ARG(QVector, result)); return result; } - return _geometry.animationFrames; + return _geometry->animationFrames; } const QVector& Animation::getFramesReference() const { - return _geometry.animationFrames; + return _geometry->animationFrames; } -void Animation::setGeometry(const FBXGeometry& geometry) { - _geometry = geometry; +void Animation::setGeometry(FBXGeometry* geometry) { + _geometry.reset(geometry); finishedLoading(true); } diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index 6a0a77f659..3ff5957fa2 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -52,7 +52,10 @@ public: Animation(const QUrl& url); - const FBXGeometry& getGeometry() const { return _geometry; } + const FBXGeometry& getGeometry() const { return *_geometry; } + + virtual bool isLoaded() const override; + Q_INVOKABLE QStringList getJointNames() const; @@ -62,13 +65,13 @@ public: protected: - Q_INVOKABLE void setGeometry(const FBXGeometry& geometry); + Q_INVOKABLE void setGeometry(FBXGeometry* geometry); virtual void downloadFinished(QNetworkReply* reply); private: - FBXGeometry _geometry; + std::unique_ptr _geometry; }; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index bb0a35f7b0..930a684617 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -38,7 +38,7 @@ void RenderableZoneEntityItem::changeProperties(Lambda setNewProperties) { _model = getModel(); _needsInitialSimulation = true; - _model->setURL(getCompoundShapeURL(), QUrl(), true, true); + _model->setURL(getCompoundShapeURL()); } if (oldPosition != getPosition() || oldRotation != getRotation() || @@ -85,7 +85,7 @@ void RenderableZoneEntityItem::initialSimulation() { void RenderableZoneEntityItem::updateGeometry() { if (_model && !_model->isActive() && hasCompoundShapeURL()) { // Since we have a delayload, we need to update the geometry if it has been downloaded - _model->setURL(getCompoundShapeURL(), QUrl(), true); + _model->setURL(getCompoundShapeURL()); } if (_model && _model->isActive() && _needsInitialSimulation) { initialSimulation(); diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 2db5f5fa51..f0d13f8792 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1373,12 +1373,12 @@ FBXLight extractLight(const FBXNode& object) { #if USE_MODEL_MESH -void buildModelMesh(ExtractedMesh& extracted) { +void buildModelMesh(ExtractedMesh& extracted, const QString& url) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("buildModelMesh failed -- .*"); if (extracted.mesh.vertices.size() == 0) { extracted.mesh._mesh = model::Mesh(); - qCDebug(modelformat) << "buildModelMesh failed -- no vertices"; + qCDebug(modelformat) << "buildModelMesh failed -- no vertices, url = " << url; return; } FBXMesh& fbxMesh = extracted.mesh; @@ -1465,7 +1465,7 @@ void buildModelMesh(ExtractedMesh& extracted) { if (! totalIndices) { extracted.mesh._mesh = model::Mesh(); - qCDebug(modelformat) << "buildModelMesh failed -- no indices"; + qCDebug(modelformat) << "buildModelMesh failed -- no indices, url = " << url; return; } @@ -1505,7 +1505,7 @@ void buildModelMesh(ExtractedMesh& extracted) { mesh.setPartBuffer(pbv); } else { extracted.mesh._mesh = model::Mesh(); - qCDebug(modelformat) << "buildModelMesh failed -- no parts"; + qCDebug(modelformat) << "buildModelMesh failed -- no parts, url = " << url; return; } @@ -1530,7 +1530,7 @@ QByteArray fileOnUrl(const QByteArray& filenameString, const QString& url) { return filename; } -FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { QHash meshes; QHash modelIDsToNames; QHash meshIDsToMeshIndices; @@ -1615,7 +1615,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, #if defined(DEBUG_FBXREADER) int unknown = 0; #endif - FBXGeometry geometry; + FBXGeometry* geometryPtr = new FBXGeometry; + FBXGeometry& geometry = *geometryPtr; + float unitScaleFactor = 1.0f; glm::vec3 ambientColor; QString hifiGlobalNodeID; @@ -2680,7 +2682,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex); # if USE_MODEL_MESH - buildModelMesh(extracted); + buildModelMesh(extracted, url); # endif if (extracted.mesh.isEye) { @@ -2761,15 +2763,15 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, } } - return geometry; + return geometryPtr; } -FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); return readFBX(&buffer, mapping, url, loadLightmaps, lightmapLevel); } -FBXGeometry readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { return extractFBXGeometry(parseFBX(device), mapping, url, loadLightmaps, lightmapLevel); } diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index b8a22b0b80..471a9c1777 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -272,10 +272,10 @@ Q_DECLARE_METATYPE(FBXGeometry) /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); #endif // hifi_FBXReader_h diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index b7ae948490..841fdcfad9 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -399,15 +399,16 @@ done: } -FBXGeometry OBJReader::readOBJ(const QByteArray& model, const QVariantHash& mapping) { +FBXGeometry* OBJReader::readOBJ(const QByteArray& model, const QVariantHash& mapping) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); return readOBJ(&buffer, mapping, nullptr); } -FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url) { - FBXGeometry geometry; +FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url) { + FBXGeometry* geometryPtr = new FBXGeometry(); + FBXGeometry& geometry = *geometryPtr; OBJTokenizer tokenizer(device); float scaleGuess = 1.0f; @@ -545,7 +546,7 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, Q qCDebug(modelformat) << "OBJ reader fail: " << e.what(); } - return geometry; + return geometryPtr; } diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 2e7b050b0a..df4c88553e 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -71,8 +71,8 @@ public: QHash materials; QNetworkReply* request(QUrl& url, bool isTest); - FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping); - FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url); + FBXGeometry* readOBJ(const QByteArray& model, const QVariantHash& mapping); + FBXGeometry* readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url); private: QUrl* _url = nullptr; diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index e127380630..75028abe93 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -320,7 +320,6 @@ void Resource::attemptRequest() { void Resource::finishedLoading(bool success) { if (success) { _loaded = true; - emit loaded(); } else { _failedToLoad = true; } @@ -333,91 +332,26 @@ void Resource::reinsert() { static const int REPLY_TIMEOUT_MS = 5000; void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - if (!_reply->isFinished()) { - _bytesReceived = bytesReceived; - _bytesTotal = bytesTotal; - _replyTimer->start(REPLY_TIMEOUT_MS); - return; - } - _reply->disconnect(this); - _replyTimer->disconnect(this); - QNetworkReply* reply = _reply; - _reply = nullptr; - _replyTimer->deleteLater(); - _replyTimer = nullptr; - ResourceCache::requestCompleted(this); - - downloadFinished(reply); + _bytesReceived = bytesReceived; + _bytesTotal = bytesTotal; + _replyTimer->start(REPLY_TIMEOUT_MS); } void Resource::handleReplyError() { - handleReplyError(_reply->error(), qDebug() << _reply->errorString()); + handleReplyErrorInternal(_reply->error()); } void Resource::handleReplyTimeout() { - handleReplyError(QNetworkReply::TimeoutError, qDebug() << "Timed out loading" << _reply->url() << - "received" << _bytesReceived << "total" << _bytesTotal); -} - -void Resource::maybeRefresh() { - if (Q_LIKELY(NetworkAccessManager::getInstance().cache())) { - QNetworkReply* reply = qobject_cast(sender()); - QVariant variant = reply->header(QNetworkRequest::LastModifiedHeader); - QNetworkCacheMetaData metaData = NetworkAccessManager::getInstance().cache()->metaData(_url); - if (variant.isValid() && variant.canConvert() && metaData.isValid()) { - QDateTime lastModified = variant.value(); - QDateTime lastModifiedOld = metaData.lastModified(); - if (lastModified.isValid() && lastModifiedOld.isValid() && - lastModifiedOld >= lastModified) { // With >=, cache won't thrash in eventually-consistent cdn. - qCDebug(networking) << "Using cached version of" << _url.fileName(); - // We don't need to update, return - return; - } - } else if (!variant.isValid() || !variant.canConvert() || - !variant.value().isValid() || variant.value().isNull()) { - qCDebug(networking) << "Cannot determine when" << _url.fileName() << "was modified last, cached version might be outdated"; - return; - } - qCDebug(networking) << "Loaded" << _url.fileName() << "from the disk cache but the network version is newer, refreshing."; - refresh(); - } + handleReplyErrorInternal(QNetworkReply::TimeoutError); } void Resource::makeRequest() { _reply = NetworkAccessManager::getInstance().get(_request); - + connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64))); connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError())); connect(_reply, SIGNAL(finished()), SLOT(handleReplyFinished())); - - if (_reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool()) { - // If the file as been updated since it was cached, refresh it - QNetworkRequest request(_request); - request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false); - QNetworkReply* reply = NetworkAccessManager::getInstance().head(request); - connect(reply, &QNetworkReply::finished, this, &Resource::maybeRefresh); - } else { - if (Q_LIKELY(NetworkAccessManager::getInstance().cache())) { - QNetworkCacheMetaData metaData = NetworkAccessManager::getInstance().cache()->metaData(_url); - bool needUpdate = false; - if (metaData.expirationDate().isNull() || metaData.expirationDate() <= QDateTime::currentDateTime()) { - // If the expiration date is NULL or in the past, - // put one far enough away that it won't be an issue. - metaData.setExpirationDate(QDateTime::currentDateTime().addYears(100)); - needUpdate = true; - } - if (metaData.lastModified().isNull()) { - // If the lastModified date is NULL, set it to now. - metaData.setLastModified(QDateTime::currentDateTime()); - needUpdate = true; - } - if (needUpdate) { - NetworkAccessManager::getInstance().cache()->updateMetaData(metaData); - } - } - } - + _replyTimer = new QTimer(this); connect(_replyTimer, SIGNAL(timeout()), SLOT(handleReplyTimeout())); _replyTimer->setSingleShot(true); @@ -425,7 +359,8 @@ void Resource::makeRequest() { _bytesReceived = _bytesTotal = 0; } -void Resource::handleReplyError(QNetworkReply::NetworkError error, QDebug debug) { +void Resource::handleReplyErrorInternal(QNetworkReply::NetworkError error) { + _reply->disconnect(this); _replyTimer->disconnect(this); _reply->deleteLater(); @@ -433,7 +368,7 @@ void Resource::handleReplyError(QNetworkReply::NetworkError error, QDebug debug) _replyTimer->deleteLater(); _replyTimer = nullptr; ResourceCache::requestCompleted(this); - + // retry for certain types of failures switch (error) { case QNetworkReply::RemoteHostClosedError: @@ -444,26 +379,46 @@ void Resource::handleReplyError(QNetworkReply::NetworkError error, QDebug debug) case QNetworkReply::UnknownNetworkError: case QNetworkReply::UnknownProxyError: case QNetworkReply::UnknownContentError: - case QNetworkReply::ProtocolFailure: { + case QNetworkReply::ProtocolFailure: { // retry with increasing delays const int MAX_ATTEMPTS = 8; const int BASE_DELAY_MS = 1000; if (++_attempts < MAX_ATTEMPTS) { QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(attemptRequest())); - debug << "-- retrying..."; + qCWarning(networking) << "error downloading url =" << _url.toDisplayString() << ", error =" << error << ", retrying (" << _attempts << "/" << MAX_ATTEMPTS << ")"; return; } // fall through to final failure - } + } default: + qCCritical(networking) << "error downloading, url =" << _url.toDisplayString() << ", error =" << error; + emit failed(error); finishedLoading(false); break; } } void Resource::handleReplyFinished() { - qCDebug(networking) << "Got finished without download progress/error?" << _url; - handleDownloadProgress(0, 0); + + bool fromCache = _reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(); + qCDebug(networking) << "success downloading url =" << _url.toDisplayString() << (fromCache ? "from cache" : ""); + + _reply->disconnect(this); + _replyTimer->disconnect(this); + QNetworkReply* reply = _reply; + _reply = nullptr; + _replyTimer->deleteLater(); + _replyTimer = nullptr; + ResourceCache::requestCompleted(this); + + finishedLoading(true); + emit loaded(*reply); + downloadFinished(reply); +} + + +void Resource::downloadFinished(QNetworkReply* reply) { + ; } uint qHash(const QPointer& value, uint seed) { diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 93ddfe77be..9a88c434e1 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -150,7 +150,7 @@ public: float getLoadPriority(); /// Checks whether the resource has loaded. - bool isLoaded() const { return _loaded; } + virtual bool isLoaded() const { return _loaded; } /// For loading resources, returns the number of bytes received. qint64 getBytesReceived() const { return _bytesReceived; } @@ -174,21 +174,22 @@ public: signals: /// Fired when the resource has been loaded. - void loaded(); + void loaded(QNetworkReply& request); + + /// Fired when resource failed to load. + void failed(QNetworkReply::NetworkError error); + + /// Fired when resource is refreshed. void onRefresh(); protected slots: void attemptRequest(); - - /// Refreshes the resource if the last modified date on the network - /// is greater than the last modified date in the cache. - void maybeRefresh(); protected: virtual void init(); /// Called when the download has finished. The recipient should delete the reply when done with it. - virtual void downloadFinished(QNetworkReply* reply) = 0; + virtual void downloadFinished(QNetworkReply* reply); /// Should be called by subclasses when all the loading that will be done has been done. Q_INVOKABLE void finishedLoading(bool success); @@ -216,7 +217,7 @@ private: void makeRequest(); - void handleReplyError(QNetworkReply::NetworkError error, QDebug debug); + void handleReplyErrorInternal(QNetworkReply::NetworkError error); friend class ResourceCache; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 31b030f75a..f48ceb9b62 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include @@ -50,6 +49,13 @@ GeometryCache::~GeometryCache() { #endif //def WANT_DEBUG } +QSharedPointer GeometryCache::createResource(const QUrl& url, const QSharedPointer& fallback, + bool delayLoad, const void* extra) { + // NetworkGeometry is no longer a subclass of Resource, but requires this method because, it is pure virtual. + assert(false); + return QSharedPointer(); +} + const int NUM_VERTICES_PER_TRIANGLE = 3; const int NUM_TRIANGLES_PER_QUAD = 2; const int NUM_VERTICES_PER_TRIANGULATED_QUAD = NUM_VERTICES_PER_TRIANGLE * NUM_TRIANGLES_PER_QUAD; @@ -1643,19 +1649,6 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm batch.draw(gpu::LINES, 2, 0); } - -QSharedPointer GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) { - return getResource(url, fallback, delayLoad, NULL).staticCast(); -} - -QSharedPointer GeometryCache::createResource(const QUrl& url, const QSharedPointer& fallback, - bool delayLoad, const void* extra) { - QSharedPointer geometry(new NetworkGeometry(url, fallback.staticCast(), delayLoad), - &Resource::allReferencesCleared); - geometry->setLODParent(geometry); - return geometry.staticCast(); -} - void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { if (!_standardDrawPipeline) { auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); @@ -1685,33 +1678,82 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { } } -const float NetworkGeometry::NO_HYSTERESIS = -1.0f; +GeometryReader::GeometryReader(const QUrl& url, QNetworkReply* reply, const QVariantHash& mapping) : + _url(url), + _reply(reply), + _mapping(mapping) { +} -NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, - const QVariantHash& mapping, const QUrl& textureBase) : - Resource(url, delayLoad), - _mapping(mapping), - _textureBase(textureBase.isValid() ? textureBase : url), - _fallback(fallback) -{ - - if (url.isEmpty()) { - // make the minimal amount of dummy geometry to satisfy Model - FBXJoint joint = { false, QVector(), -1, 0.0f, 0.0f, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), - glm::quat(), glm::mat4(), glm::mat4(), glm::vec3(), glm::vec3(), glm::quat(), glm::quat(), - glm::mat4(), QString(""), false}; - _geometry.joints.append(joint); - _geometry.leftEyeJointIndex = -1; - _geometry.rightEyeJointIndex = -1; - _geometry.neckJointIndex = -1; - _geometry.rootJointIndex = -1; - _geometry.leanJointIndex = -1; - _geometry.headJointIndex = -1; - _geometry.leftHandJointIndex = -1; - _geometry.rightHandJointIndex = -1; +void GeometryReader::run() { + try { + if (!_reply) { + throw QString("Reply is NULL ?!"); + } + QString urlname = _url.path().toLower(); + bool urlValid = true; + urlValid &= !urlname.isEmpty(); + urlValid &= !_url.path().isEmpty(); + urlValid &= _url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"); + + if (urlValid) { + // Let's read the binaries from the network + FBXGeometry* fbxgeo = nullptr; + if (_url.path().toLower().endsWith(".fbx")) { + const bool grabLightmaps = true; + const float lightmapLevel = 1.0f; + fbxgeo = readFBX(_reply, _mapping, _url.path(), grabLightmaps, lightmapLevel); + } else if (_url.path().toLower().endsWith(".obj")) { + fbxgeo = OBJReader().readOBJ(_reply, _mapping, &_url); + } else { + QString errorStr("usupported format"); + emit onError(NetworkGeometry::ModelParseError, errorStr); + } + emit onSuccess(fbxgeo); + } else { + throw QString("url is invalid"); + } + + } catch (const QString& error) { + qCDebug(renderutils) << "Error reading " << _url << ": " << error; + emit onError(NetworkGeometry::ModelParseError, error); } - - connect(this, &Resource::loaded, this, &NetworkGeometry::replaceTexturesWithPendingChanges); + _reply->deleteLater(); +} + +NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) : + _url(url), + _mapping(mapping), + _textureBaseUrl(textureBaseUrl) { + + if (delayLoad) { + _state = DelayState; + } else { + attemptRequestInternal(); + } +} + +NetworkGeometry::~NetworkGeometry() { + if (_resource) { + _resource->deleteLater(); + } +} + +void NetworkGeometry::attemptRequest() { + if (_state == DelayState) { + attemptRequestInternal(); + } +} + +void NetworkGeometry::attemptRequestInternal() { + if (_url.path().toLower().endsWith(".fst")) { + requestMapping(_url); + } else { + requestModel(_url); + } +} + +bool NetworkGeometry::isLoaded() const { + return _state == SuccessState; } bool NetworkGeometry::isLoadedWithTextures() const { @@ -1719,12 +1761,12 @@ bool NetworkGeometry::isLoadedWithTextures() const { return false; } if (!_isLoadedWithTextures) { - foreach (const NetworkMesh& mesh, _meshes) { - foreach (const NetworkMeshPart& part, mesh.parts) { - if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) || - (part.normalTexture && !part.normalTexture->isLoaded()) || - (part.specularTexture && !part.specularTexture->isLoaded()) || - (part.emissiveTexture && !part.emissiveTexture->isLoaded())) { + for (auto&& mesh : _meshes) { + for (auto && part : mesh->_parts) { + if ((part->diffuseTexture && !part->diffuseTexture->isLoaded()) || + (part->normalTexture && !part->normalTexture->isLoaded()) || + (part->specularTexture && !part->specularTexture->isLoaded()) || + (part->emissiveTexture && !part->emissiveTexture->isLoaded())) { return false; } } @@ -1734,183 +1776,38 @@ bool NetworkGeometry::isLoadedWithTextures() const { return true; } -QSharedPointer NetworkGeometry::getLODOrFallback(float distance, float& hysteresis, bool delayLoad) const { - if (_lodParent.data() != this) { - return _lodParent.data()->getLODOrFallback(distance, hysteresis, delayLoad); - } - if (_failedToLoad && _fallback) { - return _fallback; - } - QSharedPointer lod = _lodParent; - float lodDistance = 0.0f; - QMap >::const_iterator it = _lods.upperBound(distance); - if (it != _lods.constBegin()) { - it = it - 1; - lod = it.value(); - lodDistance = it.key(); - } - if (hysteresis != NO_HYSTERESIS && hysteresis != lodDistance) { - // if we previously selected a different distance, make sure we've moved far enough to justify switching - const float HYSTERESIS_PROPORTION = 0.1f; - if (glm::abs(distance - qMax(hysteresis, lodDistance)) / fabsf(hysteresis - lodDistance) < HYSTERESIS_PROPORTION) { - lod = _lodParent; - lodDistance = 0.0f; - it = _lods.upperBound(hysteresis); - if (it != _lods.constBegin()) { - it = it - 1; - lod = it.value(); - lodDistance = it.key(); - } - } - } - if (lod && lod->isLoaded()) { - hysteresis = lodDistance; - return lod; - } - // if the ideal LOD isn't loaded, we need to make sure it's started to load, and possibly return the closest loaded one - if (!delayLoad) { - lod->ensureLoading(); - } - float closestDistance = FLT_MAX; - if (isLoaded()) { - lod = _lodParent; - closestDistance = distance; - } - for (it = _lods.constBegin(); it != _lods.constEnd(); it++) { - float distanceToLOD = glm::abs(distance - it.key()); - if (it.value()->isLoaded() && distanceToLOD < closestDistance) { - lod = it.value(); - closestDistance = distanceToLOD; - } - } - hysteresis = NO_HYSTERESIS; - return lod; -} - -uint qHash(const QWeakPointer& animation, uint seed = 0) { - return qHash(animation.data(), seed); -} - -QVector NetworkGeometry::getJointMappings(const AnimationPointer& animation) { - QVector mappings = _jointMappings.value(animation); - if (mappings.isEmpty() && isLoaded() && animation && animation->isLoaded()) { - const FBXGeometry& animationGeometry = animation->getGeometry(); - for (int i = 0; i < animationGeometry.joints.size(); i++) { - mappings.append(_geometry.jointIndices.value(animationGeometry.joints.at(i).name) - 1); - } - _jointMappings.insert(animation, mappings); - } - return mappings; -} - -void NetworkGeometry::setLoadPriority(const QPointer& owner, float priority) { - Resource::setLoadPriority(owner, priority); - - for (int i = 0; i < _meshes.size(); i++) { - NetworkMesh& mesh = _meshes[i]; - for (int j = 0; j < mesh.parts.size(); j++) { - NetworkMeshPart& part = mesh.parts[j]; - if (part.diffuseTexture) { - part.diffuseTexture->setLoadPriority(owner, priority); - } - if (part.normalTexture) { - part.normalTexture->setLoadPriority(owner, priority); - } - if (part.specularTexture) { - part.specularTexture->setLoadPriority(owner, priority); - } - if (part.emissiveTexture) { - part.emissiveTexture->setLoadPriority(owner, priority); - } - } - } -} - -void NetworkGeometry::setLoadPriorities(const QHash, float>& priorities) { - Resource::setLoadPriorities(priorities); - - for (int i = 0; i < _meshes.size(); i++) { - NetworkMesh& mesh = _meshes[i]; - for (int j = 0; j < mesh.parts.size(); j++) { - NetworkMeshPart& part = mesh.parts[j]; - if (part.diffuseTexture) { - part.diffuseTexture->setLoadPriorities(priorities); - } - if (part.normalTexture) { - part.normalTexture->setLoadPriorities(priorities); - } - if (part.specularTexture) { - part.specularTexture->setLoadPriorities(priorities); - } - if (part.emissiveTexture) { - part.emissiveTexture->setLoadPriorities(priorities); - } - } - } -} - -void NetworkGeometry::clearLoadPriority(const QPointer& owner) { - Resource::clearLoadPriority(owner); - - for (int i = 0; i < _meshes.size(); i++) { - NetworkMesh& mesh = _meshes[i]; - for (int j = 0; j < mesh.parts.size(); j++) { - NetworkMeshPart& part = mesh.parts[j]; - if (part.diffuseTexture) { - part.diffuseTexture->clearLoadPriority(owner); - } - if (part.normalTexture) { - part.normalTexture->clearLoadPriority(owner); - } - if (part.specularTexture) { - part.specularTexture->clearLoadPriority(owner); - } - if (part.emissiveTexture) { - part.emissiveTexture->clearLoadPriority(owner); - } - } - } -} - void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& url) { if (_meshes.size() > 0) { auto textureCache = DependencyManager::get(); - for (int i = 0; i < _meshes.size(); i++) { - NetworkMesh& mesh = _meshes[i]; - for (int j = 0; j < mesh.parts.size(); j++) { - NetworkMeshPart& part = mesh.parts[j]; - + for (size_t i = 0; i < _meshes.size(); i++) { + NetworkMesh& mesh = *(_meshes[i].get()); + for (size_t j = 0; j < mesh._parts.size(); j++) { + NetworkMeshPart& part = *(mesh._parts[j].get()); QSharedPointer matchingTexture = QSharedPointer(); if (part.diffuseTextureName == name) { - part.diffuseTexture = textureCache->getTexture(url, DEFAULT_TEXTURE, _geometry.meshes[i].isEye); - part.diffuseTexture->setLoadPriorities(_loadPriorities); + part.diffuseTexture = textureCache->getTexture(url, DEFAULT_TEXTURE, _geometry->meshes[i].isEye); } else if (part.normalTextureName == name) { part.normalTexture = textureCache->getTexture(url); - part.normalTexture->setLoadPriorities(_loadPriorities); } else if (part.specularTextureName == name) { part.specularTexture = textureCache->getTexture(url); - part.specularTexture->setLoadPriorities(_loadPriorities); } else if (part.emissiveTextureName == name) { part.emissiveTexture = textureCache->getTexture(url); - part.emissiveTexture->setLoadPriorities(_loadPriorities); } } } } else { - qCDebug(renderutils) << "Adding a name url pair to pending" << name << url; - // we don't have meshes downloaded yet, so hold this texture as pending - _pendingTextureChanges.insert(name, url); + qCWarning(renderutils) << "Ignoring setTextureWirthNameToURL() geometry not ready." << name << url; } _isLoadedWithTextures = false; } QStringList NetworkGeometry::getTextureNames() const { QStringList result; - for (int i = 0; i < _meshes.size(); i++) { - const NetworkMesh& mesh = _meshes[i]; - for (int j = 0; j < mesh.parts.size(); j++) { - const NetworkMeshPart& part = mesh.parts[j]; - + for (size_t i = 0; i < _meshes.size(); i++) { + const NetworkMesh& mesh = *(_meshes[i].get()); + for (size_t j = 0; j < mesh._parts.size(); j++) { + const NetworkMeshPart& part = *(mesh._parts[j].get()); + if (!part.diffuseTextureName.isEmpty() && part.diffuseTexture) { QString textureURL = part.diffuseTexture->getURL().toString(); result << part.diffuseTextureName + ":" + textureURL; @@ -1935,320 +1832,259 @@ QStringList NetworkGeometry::getTextureNames() const { return result; } -void NetworkGeometry::replaceTexturesWithPendingChanges() { - QHash::Iterator it = _pendingTextureChanges.begin(); - - while (it != _pendingTextureChanges.end()) { - setTextureWithNameToURL(it.key(), it.value()); - it = _pendingTextureChanges.erase(it); +void NetworkGeometry::requestMapping(const QUrl& url) { + _state = RequestMappingState; + if (_resource) { + _resource->deleteLater(); } + _resource = new Resource(url, false); + connect(_resource, SIGNAL(loaded(QNetworkReply&)), SLOT(mappingRequestDone(QNetworkReply&))); + connect(_resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(mappingRequestError(QNetworkReply::NetworkError))); } -/// Reads geometry in a worker thread. -class GeometryReader : public QRunnable { -public: - - GeometryReader(const QWeakPointer& geometry, const QUrl& url, - QNetworkReply* reply, const QVariantHash& mapping); - - virtual void run(); - -private: - - QWeakPointer _geometry; - QUrl _url; - QNetworkReply* _reply; - QVariantHash _mapping; -}; - -GeometryReader::GeometryReader(const QWeakPointer& geometry, const QUrl& url, - QNetworkReply* reply, const QVariantHash& mapping) : - _geometry(geometry), - _url(url), - _reply(reply), - _mapping(mapping) { +void NetworkGeometry::requestModel(const QUrl& url) { + _state = RequestModelState; + if (_resource) { + _resource->deleteLater(); + } + _resource = new Resource(url, false); + connect(_resource, SIGNAL(loaded(QNetworkReply&)), SLOT(modelRequestDone(QNetworkReply&))); + connect(_resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(modelRequestError(QNetworkReply::NetworkError))); } -void GeometryReader::run() { - QSharedPointer geometry = _geometry.toStrongRef(); - if (geometry.isNull()) { - _reply->deleteLater(); - return; - } - try { - if (!_reply) { - throw QString("Reply is NULL ?!"); - } - QString urlname = _url.path().toLower(); - bool urlValid = true; - urlValid &= !urlname.isEmpty(); - urlValid &= !_url.path().isEmpty(); - urlValid &= _url.path().toLower().endsWith(".fbx") - || _url.path().toLower().endsWith(".obj") - || _url.path().toLower().endsWith(".svo"); +void NetworkGeometry::mappingRequestDone(QNetworkReply& reply) { + assert(_state == RequestMappingState); - if (urlValid) { - // Let's read the binaries from the network - FBXGeometry fbxgeo; - if (_url.path().toLower().endsWith(".fbx")) { - bool grabLightmaps = true; - float lightmapLevel = 1.0f; - // HACK: For monday 12/01/2014 we need to kill lighmaps loading in starchamber... - if (_url.path().toLower().endsWith("loungev4_11-18.fbx")) { - grabLightmaps = false; - } else if (_url.path().toLower().endsWith("apt8_reboot.fbx")) { - lightmapLevel = 4.0f; - } else if (_url.path().toLower().endsWith("palaceoforinthilian4.fbx")) { - lightmapLevel = 3.5f; - } - fbxgeo = readFBX(_reply, _mapping, _url.path(), grabLightmaps, lightmapLevel); - } else if (_url.path().toLower().endsWith(".obj")) { - fbxgeo = OBJReader().readOBJ(_reply, _mapping, &_url); + // parse the mapping file + _mapping = FSTReader::readMapping(reply.readAll()); + + QUrl replyUrl = reply.url(); + QString modelUrlStr = _mapping.value("filename").toString(); + if (modelUrlStr.isNull()) { + qCDebug(renderutils) << "Mapping file " << _url << "has no \"filename\" entry"; + emit onFailure(*this, MissingFilenameInMapping); + } else { + // read _textureBase from mapping file, if present + QString texdir = _mapping.value("texdir").toString(); + if (!texdir.isNull()) { + if (!texdir.endsWith('/')) { + texdir += '/'; } - QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo)); - } else { - throw QString("url is invalid"); + _textureBaseUrl = replyUrl.resolved(texdir); } - } catch (const QString& error) { - qCDebug(renderutils) << "Error reading " << _url << ": " << error; - QMetaObject::invokeMethod(geometry.data(), "finishedLoading", Q_ARG(bool, false)); - } - _reply->deleteLater(); -} - -void NetworkGeometry::init() { - _mapping = QVariantHash(); - _geometry = FBXGeometry(); - _meshes.clear(); - _lods.clear(); - _pendingTextureChanges.clear(); - _request.setUrl(_url); - Resource::init(); -} - -void NetworkGeometry::downloadFinished(QNetworkReply* reply) { - QUrl url = reply->url(); - if (url.path().toLower().endsWith(".fst")) { - // it's a mapping file; parse it and get the mesh filename - _mapping = FSTReader::readMapping(reply->readAll()); - reply->deleteLater(); - QString filename = _mapping.value("filename").toString(); - if (filename.isNull()) { - qCDebug(renderutils) << "Mapping file " << url << " has no filename."; - finishedLoading(false); - - } else { - QString texdir = _mapping.value("texdir").toString(); - if (!texdir.isNull()) { - if (!texdir.endsWith('/')) { - texdir += '/'; - } - _textureBase = url.resolved(texdir); - } - QVariantHash lods = _mapping.value("lod").toHash(); - for (QVariantHash::const_iterator it = lods.begin(); it != lods.end(); it++) { - auto geometry = QSharedPointer::create(url.resolved(it.key()), - QSharedPointer(), true, _mapping, _textureBase); - geometry->setSelf(geometry.staticCast()); - geometry->setLODParent(_lodParent); - _lods.insert(it.value().toFloat(), geometry); - } - _request.setUrl(url.resolved(filename)); - - // make the request immediately only if we have no LODs to switch between - _startedLoading = false; - if (_lods.isEmpty()) { - attemptRequest(); - } - } - return; - } - - // send the reader off to the thread pool - QThreadPool::globalInstance()->start(new GeometryReader(_self, url, reply, _mapping)); -} - -void NetworkGeometry::reinsert() { - Resource::reinsert(); - - _lodParent = qWeakPointerCast(_self); - foreach (const QSharedPointer& lod, _lods) { - lod->setLODParent(_lodParent); + QUrl modelUrl = replyUrl.resolved(modelUrlStr); + requestModel(modelUrl); } } -void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { - _geometry = geometry; +void NetworkGeometry::mappingRequestError(QNetworkReply::NetworkError error) { + assert(_state == RequestMappingState); + _state = ErrorState; + emit onFailure(*this, MappingRequestError); +} +void NetworkGeometry::modelRequestDone(QNetworkReply& reply) { + assert(_state == RequestModelState); + + _state = ParsingModelState; + + // asynchronously parse the model file. + GeometryReader* geometryReader = new GeometryReader(reply.url(), &reply, _mapping); + connect(geometryReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(modelParseSuccess(FBXGeometry*))); + connect(geometryReader, SIGNAL(onError(int, QString)), SLOT(modelParseError(int, QString))); + + QThreadPool::globalInstance()->start(geometryReader); +} + +void NetworkGeometry::modelRequestError(QNetworkReply::NetworkError error) { + assert(_state == RequestModelState); + _state = ErrorState; + emit onFailure(*this, ModelRequestError); +} + +static NetworkMesh* buildNetworkMesh(const FBXMesh& mesh, const QUrl& textureBaseUrl) { auto textureCache = DependencyManager::get(); - - foreach (const FBXMesh& mesh, _geometry.meshes) { - NetworkMesh networkMesh; - - int totalIndices = 0; - bool checkForTexcoordLightmap = false; - foreach (const FBXMeshPart& part, mesh.parts) { - NetworkMeshPart networkPart; - if (!part.diffuseTexture.filename.isEmpty()) { - networkPart.diffuseTexture = textureCache->getTexture( - _textureBase.resolved(QUrl(part.diffuseTexture.filename)), DEFAULT_TEXTURE, - mesh.isEye, part.diffuseTexture.content); - networkPart.diffuseTextureName = part.diffuseTexture.name; - networkPart.diffuseTexture->setLoadPriorities(_loadPriorities); - } - if (!part.normalTexture.filename.isEmpty()) { - networkPart.normalTexture = textureCache->getTexture( - _textureBase.resolved(QUrl(part.normalTexture.filename)), NORMAL_TEXTURE, - false, part.normalTexture.content); - networkPart.normalTextureName = part.normalTexture.name; - networkPart.normalTexture->setLoadPriorities(_loadPriorities); - } - if (!part.specularTexture.filename.isEmpty()) { - networkPart.specularTexture = textureCache->getTexture( - _textureBase.resolved(QUrl(part.specularTexture.filename)), SPECULAR_TEXTURE, - false, part.specularTexture.content); - networkPart.specularTextureName = part.specularTexture.name; - networkPart.specularTexture->setLoadPriorities(_loadPriorities); - } - if (!part.emissiveTexture.filename.isEmpty()) { - networkPart.emissiveTexture = textureCache->getTexture( - _textureBase.resolved(QUrl(part.emissiveTexture.filename)), EMISSIVE_TEXTURE, - false, part.emissiveTexture.content); - networkPart.emissiveTextureName = part.emissiveTexture.name; - networkPart.emissiveTexture->setLoadPriorities(_loadPriorities); - checkForTexcoordLightmap = true; - } - networkMesh.parts.append(networkPart); - - totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); + NetworkMesh* networkMesh = new NetworkMesh(); + + int totalIndices = 0; + bool checkForTexcoordLightmap = false; + + // process network parts + foreach (const FBXMeshPart& part, mesh.parts) { + NetworkMeshPart* networkPart = new NetworkMeshPart(); + + if (!part.diffuseTexture.filename.isEmpty()) { + networkPart->diffuseTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.diffuseTexture.filename)), DEFAULT_TEXTURE, + mesh.isEye, part.diffuseTexture.content); + networkPart->diffuseTextureName = part.diffuseTexture.name; } - - { - networkMesh._indexBuffer = std::make_shared(); - networkMesh._indexBuffer->resize(totalIndices * sizeof(int)); - int offset = 0; - foreach(const FBXMeshPart& part, mesh.parts) { - networkMesh._indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int), - (gpu::Byte*) part.quadIndices.constData()); - offset += part.quadIndices.size() * sizeof(int); - networkMesh._indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int), - (gpu::Byte*) part.triangleIndices.constData()); - offset += part.triangleIndices.size() * sizeof(int); - } + if (!part.normalTexture.filename.isEmpty()) { + networkPart->normalTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.normalTexture.filename)), NORMAL_TEXTURE, + false, part.normalTexture.content); + networkPart->normalTextureName = part.normalTexture.name; } - - { - networkMesh._vertexBuffer = std::make_shared(); - // if we don't need to do any blending, the positions/normals can be static - if (mesh.blendshapes.isEmpty()) { - int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); - int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); - int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - - networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); - - networkMesh._vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); - networkMesh._vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); - networkMesh._vertexBuffer->setSubData(tangentsOffset, - mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); - networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); - networkMesh._vertexBuffer->setSubData(texCoordsOffset, - mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); - networkMesh._vertexBuffer->setSubData(texCoords1Offset, - mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords1.constData()); - networkMesh._vertexBuffer->setSubData(clusterIndicesOffset, - mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); - networkMesh._vertexBuffer->setSubData(clusterWeightsOffset, - mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); - - // otherwise, at least the cluster indices/weights can be static - networkMesh._vertexStream = std::make_shared(); - networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, 0, sizeof(glm::vec3)); - if (mesh.normals.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, normalsOffset, sizeof(glm::vec3)); - if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, tangentsOffset, sizeof(glm::vec3)); - if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3)); - if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); - if (mesh.texCoords1.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoords1Offset, sizeof(glm::vec2)); - if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); - if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); - - int channelNum = 0; - networkMesh._vertexFormat = std::make_shared(); - networkMesh._vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - if (mesh.normals.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); - if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - if (mesh.texCoords1.size()) { - networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - } else if (checkForTexcoordLightmap && mesh.texCoords.size()) { - // need lightmap texcoord UV but doesn't have uv#1 so just reuse the same channel - networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum - 1, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - } - if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - } - else { - int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - - networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); - networkMesh._vertexBuffer->setSubData(0, mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); - networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); - networkMesh._vertexBuffer->setSubData(texCoordsOffset, - mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); - networkMesh._vertexBuffer->setSubData(clusterIndicesOffset, - mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); - networkMesh._vertexBuffer->setSubData(clusterWeightsOffset, - mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); - - networkMesh._vertexStream = std::make_shared(); - if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, 0, sizeof(glm::vec3)); - if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3)); - if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); - if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); - if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); - - int channelNum = 0; - networkMesh._vertexFormat = std::make_shared(); - networkMesh._vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.normals.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); - if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); - - } + if (!part.specularTexture.filename.isEmpty()) { + networkPart->specularTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.specularTexture.filename)), SPECULAR_TEXTURE, + false, part.specularTexture.content); + networkPart->specularTextureName = part.specularTexture.name; } - - _meshes.append(networkMesh); + if (!part.emissiveTexture.filename.isEmpty()) { + networkPart->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(part.emissiveTexture.filename)), EMISSIVE_TEXTURE, + false, part.emissiveTexture.content); + networkPart->emissiveTextureName = part.emissiveTexture.name; + checkForTexcoordLightmap = true; + } + networkMesh->_parts.emplace_back(networkPart); + totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); } - - finishedLoading(true); + + // initialize index buffer + { + networkMesh->_indexBuffer = std::make_shared(); + networkMesh->_indexBuffer->resize(totalIndices * sizeof(int)); + int offset = 0; + foreach(const FBXMeshPart& part, mesh.parts) { + networkMesh->_indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int), + (gpu::Byte*) part.quadIndices.constData()); + offset += part.quadIndices.size() * sizeof(int); + networkMesh->_indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int), + (gpu::Byte*) part.triangleIndices.constData()); + offset += part.triangleIndices.size() * sizeof(int); + } + } + + // initialize vertex buffer + { + networkMesh->_vertexBuffer = std::make_shared(); + // if we don't need to do any blending, the positions/normals can be static + if (mesh.blendshapes.isEmpty()) { + int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); + int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); + int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + + networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); + + networkMesh->_vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); + networkMesh->_vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); + networkMesh->_vertexBuffer->setSubData(tangentsOffset, + mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); + networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); + networkMesh->_vertexBuffer->setSubData(texCoordsOffset, + mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); + networkMesh->_vertexBuffer->setSubData(texCoords1Offset, + mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords1.constData()); + networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, + mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); + networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, + mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); + + // otherwise, at least the cluster indices/weights can be static + networkMesh->_vertexStream = std::make_shared(); + networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); + if (mesh.normals.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, normalsOffset, sizeof(glm::vec3)); + if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, tangentsOffset, sizeof(glm::vec3)); + if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); + if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); + if (mesh.texCoords1.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoords1Offset, sizeof(glm::vec2)); + if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); + if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); + + int channelNum = 0; + networkMesh->_vertexFormat = std::make_shared(); + networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); + if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + if (mesh.texCoords1.size()) { + networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + } else if (checkForTexcoordLightmap && mesh.texCoords.size()) { + // need lightmap texcoord UV but doesn't have uv#1 so just reuse the same channel + networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum - 1, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + } + if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + } + else { + int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + + networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); + networkMesh->_vertexBuffer->setSubData(0, mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); + networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); + networkMesh->_vertexBuffer->setSubData(texCoordsOffset, + mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); + networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, + mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); + networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, + mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); + + networkMesh->_vertexStream = std::make_shared(); + if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); + if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); + if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); + if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); + if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); + + int channelNum = 0; + networkMesh->_vertexFormat = std::make_shared(); + networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); + if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + } + } + + return networkMesh; +} + +void NetworkGeometry::modelParseSuccess(FBXGeometry* geometry) { + // assume owner ship of geometry pointer + _geometry.reset(geometry); + + foreach(const FBXMesh& mesh, _geometry->meshes) { + _meshes.emplace_back(buildNetworkMesh(mesh, _textureBaseUrl)); + } + + _state = SuccessState; + emit onSuccess(*this, *_geometry.get()); + + delete _resource; + _resource = nullptr; +} + +void NetworkGeometry::modelParseError(int error, QString str) { + _state = ErrorState; + emit onFailure(*this, (NetworkGeometry::Error)error); + + delete _resource; + _resource = nullptr; } bool NetworkMeshPart::isTranslucent() const { return diffuseTexture && diffuseTexture->isTranslucent(); } - bool NetworkMesh::isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const { assert(partIndex >= 0); - assert(partIndex < parts.size()); - return (parts.at(partIndex).isTranslucent() || fbxMesh.parts.at(partIndex).opacity != 1.0f); + assert((size_t)partIndex < _parts.size()); + return (_parts.at(partIndex)->isTranslucent() || fbxMesh.parts.at(partIndex).opacity != 1.0f); } int NetworkMesh::getTranslucentPartCount(const FBXMesh& fbxMesh) const { int count = 0; - for (int i = 0; i < parts.size(); i++) { + + for (size_t i = 0; i < _parts.size(); i++) { if (isPartTranslucent(fbxMesh, i)) { count++; } diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index f70eae6380..774df1561c 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -13,6 +13,7 @@ #define hifi_GeometryCache_h #include +#include #include #include @@ -129,6 +130,9 @@ public: int allocateID() { return _nextID++; } static const int UNKNOWN_ID; + virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, + bool delayLoad, const void* extra); + void renderSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec3& color, bool solid = true, int id = UNKNOWN_ID) { renderSphere(batch, radius, slices, stacks, glm::vec4(color, 1.0f), solid, id); } @@ -208,11 +212,6 @@ public: /// Set a batch to the simple pipeline, returning the previous pipeline void useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend = false); -protected: - - virtual QSharedPointer createResource(const QUrl& url, - const QSharedPointer& fallback, bool delayLoad, const void* extra); - private: GeometryCache(); virtual ~GeometryCache(); @@ -305,70 +304,104 @@ private: QHash > _networkGeometry; }; -/// Geometry loaded from the network. -class NetworkGeometry : public Resource { +class NetworkGeometry : public QObject { Q_OBJECT public: - - /// A hysteresis value indicating that we have no state memory. - static const float NO_HYSTERESIS; - - NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, - const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl()); + // mapping is only used if url is a .fbx or .obj file, it is essentially the content of an fst file. + // if delayLoad is true, the url will not be immediately downloaded. + // use the attemptRequest method to initiate the download. + NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl = QUrl()); + ~NetworkGeometry(); - /// Checks whether the geometry and its textures are loaded. + const QUrl& getURL() const { return _url; } + + void attemptRequest(); + + // true when the geometry is loaded (but maybe not it's associated textures) + bool isLoaded() const; + + // true when the requested geometry and its textures are loaded. bool isLoadedWithTextures() const; - /// Returns a pointer to the geometry appropriate for the specified distance. - /// \param hysteresis a hysteresis parameter that prevents rapid model switching - QSharedPointer getLODOrFallback(float distance, float& hysteresis, bool delayLoad = false) const; + // WARNING: only valid when isLoaded returns true. + const FBXGeometry& getFBXGeometry() const { return *_geometry; } + const std::vector>& getMeshes() const { return _meshes; } - const FBXGeometry& getFBXGeometry() const { return _geometry; } - const QVector& getMeshes() const { return _meshes; } - - QVector getJointMappings(const AnimationPointer& animation); - - virtual void setLoadPriority(const QPointer& owner, float priority); - virtual void setLoadPriorities(const QHash, float>& priorities); - virtual void clearLoadPriority(const QPointer& owner); - void setTextureWithNameToURL(const QString& name, const QUrl& url); QStringList getTextureNames() const; - + + enum Error { + MissingFilenameInMapping = 0, + MappingRequestError, + ModelRequestError, + ModelParseError + }; + +signals: + // Fired when everything has downloaded and parsed successfully. + void onSuccess(NetworkGeometry& networkGeometry, FBXGeometry& fbxGeometry); + + // Fired when something went wrong. + void onFailure(NetworkGeometry& networkGeometry, Error error); + +protected slots: + void mappingRequestDone(QNetworkReply& reply); + void mappingRequestError(QNetworkReply::NetworkError error); + + void modelRequestDone(QNetworkReply& reply); + void modelRequestError(QNetworkReply::NetworkError error); + + void modelParseSuccess(FBXGeometry* geometry); + void modelParseError(int error, QString str); + protected: + void attemptRequestInternal(); + void requestMapping(const QUrl& url); + void requestModel(const QUrl& url); - virtual void init(); - virtual void downloadFinished(QNetworkReply* reply); - virtual void reinsert(); - - Q_INVOKABLE void setGeometry(const FBXGeometry& geometry); - -private slots: - void replaceTexturesWithPendingChanges(); -private: - - friend class GeometryCache; - - void setLODParent(const QWeakPointer& lodParent) { _lodParent = lodParent; } - + enum State { DelayState, + RequestMappingState, + RequestModelState, + ParsingModelState, + SuccessState, + ErrorState }; + State _state; + + QUrl _url; QVariantHash _mapping; - QUrl _textureBase; - QSharedPointer _fallback; - - QMap > _lods; - FBXGeometry _geometry; - QVector _meshes; - - QWeakPointer _lodParent; - - QHash, QVector > _jointMappings; - - QHash _pendingTextureChanges; + QUrl _textureBaseUrl; + Resource* _resource = nullptr; + std::unique_ptr _geometry; + std::vector> _meshes; + + // cache for isLoadedWithTextures() mutable bool _isLoadedWithTextures = false; }; +/// Reads geometry in a worker thread. +class GeometryReader : public QObject, public QRunnable { + Q_OBJECT + +public: + + GeometryReader(const QUrl& url, QNetworkReply* reply, const QVariantHash& mapping); + + virtual void run(); + +signals: + void onSuccess(FBXGeometry* geometry); + void onError(int error, QString str); + +private: + + QWeakPointer _geometry; + QUrl _url; + QNetworkReply* _reply; + QVariantHash _mapping; +}; + /// The state associated with a single mesh part. class NetworkMeshPart { public: @@ -394,9 +427,9 @@ public: gpu::BufferStreamPointer _vertexStream; gpu::Stream::FormatPointer _vertexFormat; - - QVector parts; - + + std::vector> _parts; + int getTranslucentPartCount(const FBXMesh& fbxMesh) const; bool isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const; }; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7452c32ed2..c2d723a323 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -48,6 +48,8 @@ #include "model_lightmap_specular_map_frag.h" #include "model_translucent_frag.h" +#include "RenderUtilsLogging.h" + using namespace std; static int modelPointerTypeId = qRegisterMetaType >(); @@ -66,7 +68,6 @@ Model::Model(RigPointer rig, QObject* parent) : _snappedToRegistrationPoint(false), _showTrueJointTransforms(true), _cauterizeBones(false), - _lodDistance(0.0f), _pupilDilation(0.0f), _url(HTTP_INVALID_COM), _urlAsString(HTTP_INVALID_COM), @@ -234,7 +235,7 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { }; void Model::initJointTransforms() { - if (!_geometry) { + if (!_geometry || !_geometry->isLoaded()) { return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -368,8 +369,10 @@ void Model::init() { } void Model::reset() { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - _rig->reset(geometry.joints); + if (_geometry && _geometry->isLoaded()) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + _rig->reset(geometry.joints); + } _meshGroupsKnown = false; _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid @@ -378,68 +381,23 @@ void Model::reset() { bool Model::updateGeometry() { PROFILE_RANGE(__FUNCTION__); bool needFullUpdate = false; - bool needToRebuild = false; - if (_nextGeometry) { - _nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _nextLODHysteresis); - _nextGeometry->setLoadPriority(this, -_lodDistance); - _nextGeometry->ensureLoading(); - if (_nextGeometry->isLoaded()) { - applyNextGeometry(); - needToRebuild = true; - } - } - if (!_geometry) { + + if (!_geometry || !_geometry->isLoaded()) { // geometry is not ready return false; } - QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis); - if (_geometry != geometry) { + _needsReload = false; - // NOTE: it is theoretically impossible to reach here after passing through the applyNextGeometry() call above. - // Which means we don't need to worry about calling deleteGeometry() below immediately after creating new geometry. - - const FBXGeometry& newGeometry = geometry->getFBXGeometry(); - QVector newJointStates = createJointStates(newGeometry); - - if (! _rig->jointStatesEmpty()) { - // copy the existing joint states - const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); - for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); - it != oldGeometry.jointIndices.constEnd(); it++) { - int oldIndex = it.value() - 1; - int newIndex = newGeometry.getJointIndex(it.key()); - if (newIndex != -1) { - newJointStates[newIndex].copyState(_rig->getJointState(oldIndex)); - } - } - } - - deleteGeometry(); - _dilatedTextures.clear(); - if (!geometry) { - std::cout << "WARNING: no geometry in Model::updateGeometry\n"; - } - setGeometry(geometry); - - _meshGroupsKnown = false; - _readyWhenAdded = false; // in case any of our users are using scenes - invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid - initJointStates(newJointStates); - needToRebuild = true; - } else if (_rig->jointStatesEmpty()) { + QSharedPointer geometry = _geometry; + if (_rig->jointStatesEmpty()) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); if (fbxGeometry.joints.size() > 0) { initJointStates(createJointStates(fbxGeometry)); needToRebuild = true; } - } else if (!geometry->isLoaded()) { - deleteGeometry(); - _dilatedTextures.clear(); } - _geometry->setLoadPriority(this, -_lodDistance); - _geometry->ensureLoading(); if (needToRebuild) { const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); @@ -454,7 +412,7 @@ bool Model::updateGeometry() { buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), - mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); + mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); } _blendedVertexBuffers.push_back(buffer); } @@ -1069,53 +1027,36 @@ int Model::getLastFreeJointIndex(int jointIndex) const { return (isActive() && jointIndex != -1) ? _geometry->getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1; } -void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bool delayLoad) { +void Model::setURL(const QUrl& url) { + // don't recreate the geometry if it's the same URL if (_url == url && _geometry && _geometry->getURL() == url) { return; } - _readyWhenAdded = false; // reset out render items. - _needsReload = true; - invalidCalculatedMeshBoxes(); - _url = url; _urlAsString = _url.toString(); + { + render::PendingChanges pendingChanges; + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + removeFromScene(scene, pendingChanges); + scene->enqueuePendingChanges(pendingChanges); + } + + _needsReload = true; + _meshGroupsKnown = false; + invalidCalculatedMeshBoxes(); + deleteGeometry(); + + _geometry.reset(new NetworkGeometry(url, false, QVariantHash())); onInvalidate(); - - // if so instructed, keep the current geometry until the new one is loaded - _nextGeometry = DependencyManager::get()->getGeometry(url, fallback, delayLoad); - _nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS; - if (!retainCurrent || !isActive() || (_nextGeometry && _nextGeometry->isLoaded())) { - applyNextGeometry(); - } } -void Model::geometryRefreshed() { - QObject* sender = QObject::sender(); - - if (sender == _geometry) { - _readyWhenAdded = false; // reset out render items. - _needsReload = true; - invalidCalculatedMeshBoxes(); - - onInvalidate(); - - // if so instructed, keep the current geometry until the new one is loaded - _nextGeometry = DependencyManager::get()->getGeometry(_url); - _nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS; - applyNextGeometry(); - } else { - sender->disconnect(this, SLOT(geometryRefreshed())); - } -} - - const QSharedPointer Model::getCollisionGeometry(bool delayLoad) { if (_collisionGeometry.isNull() && !_collisionUrl.isEmpty()) { - _collisionGeometry = DependencyManager::get()->getGeometry(_collisionUrl, QUrl(), delayLoad); + _collisionGeometry.reset(new NetworkGeometry(_collisionUrl, delayLoad, QVariantHash())); } if (_collisionGeometry && _collisionGeometry->isLoaded()) { @@ -1130,7 +1071,7 @@ void Model::setCollisionModelURL(const QUrl& url) { return; } _collisionUrl = url; - _collisionGeometry = DependencyManager::get()->getGeometry(url, QUrl(), true); + _collisionGeometry.reset(new NetworkGeometry(url, false, QVariantHash())); } bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { @@ -1461,46 +1402,23 @@ void Model::setGeometry(const QSharedPointer& newGeometry) { if (_geometry == newGeometry) { return; } - - if (_geometry) { - _geometry->disconnect(_geometry.data(), &Resource::onRefresh, this, &Model::geometryRefreshed); - } _geometry = newGeometry; - QObject::connect(_geometry.data(), &Resource::onRefresh, this, &Model::geometryRefreshed); -} - -void Model::applyNextGeometry() { - // delete our local geometry and custom textures - deleteGeometry(); - _dilatedTextures.clear(); - _lodHysteresis = _nextLODHysteresis; - - // we retain a reference to the base geometry so that its reference count doesn't fall to zero - setGeometry(_nextGeometry); - - _meshGroupsKnown = false; - _readyWhenAdded = false; // in case any of our users are using scenes - _needsReload = false; // we are loaded now! - invalidCalculatedMeshBoxes(); - _nextGeometry.reset(); } void Model::deleteGeometry() { _blendedVertexBuffers.clear(); _rig->clearJointStates(); _meshStates.clear(); - _rig->deleteAnimations(); - - if (_geometry) { - _geometry->clearLoadPriority(this); - } - _blendedBlendshapeCoefficients.clear(); } AABox Model::getPartBounds(int meshIndex, int partIndex) { + if (!_geometry || !_geometry->isLoaded()) { + return AABox(); + } + if (meshIndex < _meshStates.size()) { const MeshState& state = _meshStates.at(meshIndex); bool isSkinned = state.clusterMatrices.size() > 1; @@ -1531,7 +1449,7 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { } void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) { - // PROFILE_RANGE(__FUNCTION__); +// PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("Model::renderPart"); if (!_readyWhenAdded) { return; // bail asap @@ -1557,14 +1475,14 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const QVector& networkMeshes = _geometry->getMeshes(); + const std::vector>& networkMeshes = _geometry->getMeshes(); // guard against partially loaded meshes - if (meshIndex >= networkMeshes.size() || meshIndex >= geometry.meshes.size() || meshIndex >= _meshStates.size() ) { + if (meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)geometry.meshes.size() || meshIndex >= (int)_meshStates.size() ) { return; } - const NetworkMesh& networkMesh = networkMeshes.at(meshIndex); + const NetworkMesh& networkMesh = *(networkMeshes.at(meshIndex).get()); const FBXMesh& mesh = geometry.meshes.at(meshIndex); const MeshState& state = _meshStates.at(meshIndex); @@ -1614,8 +1532,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // to false to rebuild out mesh groups. - - if (meshIndex < 0 || meshIndex >= networkMeshes.size() || meshIndex > geometry.meshes.size()) { + if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex > geometry.meshes.size()) { _meshGroupsKnown = false; // regenerate these lists next time around. _readyWhenAdded = false; // in case any of our users are using scenes invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid @@ -1669,11 +1586,11 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } // guard against partially loaded meshes - if (partIndex >= networkMesh.parts.size() || partIndex >= mesh.parts.size()) { + if (partIndex >= (int)networkMesh._parts.size() || partIndex >= mesh.parts.size()) { return; } - const NetworkMeshPart& networkPart = networkMesh.parts.at(partIndex); + const NetworkMeshPart& networkPart = *(networkMesh._parts.at(partIndex).get()); const FBXMeshPart& part = mesh.parts.at(partIndex); model::MaterialPointer material = part._material; @@ -1790,10 +1707,10 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran void Model::segregateMeshGroups() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const QVector& networkMeshes = _geometry->getMeshes(); + const std::vector>& networkMeshes = _geometry->getMeshes(); // all of our mesh vectors must match in size - if (networkMeshes.size() != geometry.meshes.size() || + if ((int)networkMeshes.size() != geometry.meshes.size() || geometry.meshes.size() != _meshStates.size()) { qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; @@ -1803,12 +1720,12 @@ void Model::segregateMeshGroups() { _opaqueRenderItems.clear(); // Run through all of the meshes, and place them into their segregated, but unsorted buckets - for (int i = 0; i < networkMeshes.size(); i++) { - const NetworkMesh& networkMesh = networkMeshes.at(i); + for (int i = 0; i < (int)networkMeshes.size(); i++) { + const NetworkMesh& networkMesh = *(networkMeshes.at(i).get()); const FBXMesh& mesh = geometry.meshes.at(i); const MeshState& state = _meshStates.at(i); - bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size(); + bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == (int)networkMesh._parts.size(); bool hasTangents = !mesh.tangents.isEmpty(); bool hasSpecular = mesh.hasSpecularTexture(); bool hasLightmap = mesh.hasEmissiveTexture(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index c9b63b598e..e55bff6aca 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -68,11 +68,7 @@ public: /// Sets the URL of the model to render. - /// \param fallback the URL of a fallback model to render if the requested model fails to load - /// \param retainCurrent if true, keep rendering the current model until the new one is loaded - /// \param delayLoad if true, don't load the model immediately; wait until actually requested - Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(), - bool retainCurrent = false, bool delayLoad = false); + Q_INVOKABLE void setURL(const QUrl& url); const QUrl& getURL() const { return _url; } const QString& getURLAsString() const { return _urlAsString; } @@ -89,7 +85,7 @@ public: render::Item::Status::Getters& statusGetters); void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); void renderSetup(RenderArgs* args); - bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); } + bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().empty()); } bool isVisible() const { return _isVisible; } @@ -141,14 +137,11 @@ public: const QUrl& getCollisionURL() const { return _collisionUrl; } /// Returns a reference to the shared collision geometry. - const QSharedPointer getCollisionGeometry(bool delayLoad = true); + const QSharedPointer getCollisionGeometry(bool delayLoad = false); void setOffset(const glm::vec3& offset); const glm::vec3& getOffset() const { return _offset; } - /// Sets the distance parameter used for LOD computations. - void setLODDistance(float distance) { _lodDistance = distance; } - void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false); bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled @@ -309,20 +302,12 @@ protected: // hook for derived classes to be notified when setUrl invalidates the current model. virtual void onInvalidate() {}; - void geometryRefreshed(); - private: - void applyNextGeometry(); void deleteGeometry(); QVector createJointStates(const FBXGeometry& geometry); void initJointTransforms(); - QSharedPointer _nextGeometry; - float _lodDistance; - float _lodHysteresis; - float _nextLODHysteresis; - QSharedPointer _collisionGeometry; float _pupilDilation; diff --git a/tests/networking/src/ResourceTests.cpp b/tests/networking/src/ResourceTests.cpp new file mode 100644 index 0000000000..f18f676398 --- /dev/null +++ b/tests/networking/src/ResourceTests.cpp @@ -0,0 +1,95 @@ +// +// ResoruceTests.cpp +// +// Copyright 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 + +#include "ResourceCache.h" +#include "NetworkAccessManager.h" +#include "DependencyManager.h" + +#include "ResourceTests.h" + +QTEST_MAIN(ResourceTests) + +void ResourceTests::initTestCase() { + + auto resourceCacheSharedItems = DependencyManager::set(); + + const qint64 MAXIMUM_CACHE_SIZE = 1024 * 1024 * 1024; // 1GB + + // set up the file cache + //QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + QString cachePath = "./resourceTestCache"; + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkDiskCache* cache = new QNetworkDiskCache(); + cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); + cache->setCacheDirectory(cachePath); + cache->clear(); // clear the cache + networkAccessManager.setCache(cache); +} + +static Resource* resource = nullptr; + + +static bool waitForSignal(QObject *sender, const char *signal, int timeout = 1000) { + QEventLoop loop; + QTimer timer; + timer.setInterval(timeout); + timer.setSingleShot(true); + + loop.connect(sender, signal, SLOT(quit())); + loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + timer.start(); + loop.exec(); + + return timer.isActive(); +} + +void ResourceTests::downloadFirst() { + + // download the Mery fst file + QUrl meryUrl = QUrl("http://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst"); + resource = new Resource(meryUrl, false); + + const int timeout = 1000; + QEventLoop loop; + QTimer timer; + timer.setInterval(timeout); + timer.setSingleShot(true); + + loop.connect(resource, SIGNAL(loaded(QNetworkReply&)), SLOT(quit())); + loop.connect(resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(quit())); + loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + timer.start(); + loop.exec(); + + QVERIFY(resource->isLoaded()); +} + +void ResourceTests::downloadAgain() { + + // download the Mery fst file + QUrl meryUrl = QUrl("http://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst"); + resource = new Resource(meryUrl, false); + + const int timeout = 1000; + QEventLoop loop; + QTimer timer; + timer.setInterval(timeout); + timer.setSingleShot(true); + + loop.connect(resource, SIGNAL(loaded(QNetworkReply&)), SLOT(quit())); + loop.connect(resource, SIGNAL(failed(QNetworkReply::NetworkError)), SLOT(quit())); + loop.connect(&timer, SIGNAL(timeout()), SLOT(quit())); + timer.start(); + loop.exec(); + + QVERIFY(resource->isLoaded()); + +} diff --git a/tests/networking/src/ResourceTests.h b/tests/networking/src/ResourceTests.h new file mode 100644 index 0000000000..32fc151982 --- /dev/null +++ b/tests/networking/src/ResourceTests.h @@ -0,0 +1,23 @@ +// +// ResourceTests.h +// +// Copyright 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_ResourceTests_h +#define hifi_ResourceTests_h + +#include + +class ResourceTests : public QObject { + Q_OBJECT +private slots: + void initTestCase(); + void downloadFirst(); + void downloadAgain(); +}; + +#endif // hifi_ResourceTests_h diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 3c02c956e4..f39bea9cf9 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -36,15 +36,16 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { std::cout << "Reading FBX.....\n"; QByteArray fbxContents = fbx.readAll(); - + FBXGeometry* geom; if (filename.toLower().endsWith(".obj")) { - result = OBJReader().readOBJ(fbxContents, QVariantHash()); + geom = OBJReader().readOBJ(fbxContents, QVariantHash()); } else if (filename.toLower().endsWith(".fbx")) { - result = readFBX(fbxContents, QVariantHash(), filename); + geom = readFBX(fbxContents, QVariantHash(), filename); } else { qDebug() << "unknown file extension"; return false; } + result = *geom; reSortFBXGeometryMeshes(result); From 9f501d4d72ccb0274798e0786905bd237ccb5ec0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Aug 2015 16:55:19 -0700 Subject: [PATCH 19/25] first cut at attempting to hide menu in full screen --- interface/src/Application.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a28435e258..0bc4f9b943 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4985,6 +4985,12 @@ void Application::setFullscreen(const QScreen* target) { #endif _window->windowHandle()->setScreen((QScreen*)target); _window->showFullScreen(); + + // also hide the QMainWindow's menuBar + QMenuBar* menuBar = _window->menuBar(); + if (menuBar) { + menuBar->setVisible(false); + } } void Application::unsetFullscreen(const QScreen* avoid) { @@ -5015,6 +5021,12 @@ void Application::unsetFullscreen(const QScreen* avoid) { #else _window->setGeometry(targetGeometry); #endif + + // also show the QMainWindow's menuBar + QMenuBar* menuBar = _window->menuBar(); + if (menuBar) { + menuBar->setVisible(true); + } } From 06b2a88fb6f730c7fd157f9543671c2c39efc373 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Aug 2015 19:11:15 -0700 Subject: [PATCH 20/25] toggle menu near top of window --- interface/src/Application.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0bc4f9b943..a088c42f87 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1744,6 +1744,27 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { return; } +#if 1 //ndef Q_OS_MAC + // If in full screen, and our main windows menu bar is hidden, and we're close to the top of the QMainWindow + // then show the menubar. + if (_window->isFullScreen()) { + QMenuBar* menuBar = _window->menuBar(); + if (menuBar) { + static const int MENU_TOGGLE_AREA = 10; + if (!menuBar->isVisible()) { + if (event->pos().y() <= MENU_TOGGLE_AREA) { + menuBar->setVisible(true); + } + } else { + if (event->pos().y() > MENU_TOGGLE_AREA) { + menuBar->setVisible(false); + } + } + } + } +#endif + + _entities.mouseMoveEvent(event, deviceID); _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts @@ -4986,11 +5007,13 @@ void Application::setFullscreen(const QScreen* target) { _window->windowHandle()->setScreen((QScreen*)target); _window->showFullScreen(); +#ifndef Q_OS_MAC // also hide the QMainWindow's menuBar QMenuBar* menuBar = _window->menuBar(); if (menuBar) { menuBar->setVisible(false); } +#endif } void Application::unsetFullscreen(const QScreen* avoid) { @@ -5022,11 +5045,13 @@ void Application::unsetFullscreen(const QScreen* avoid) { _window->setGeometry(targetGeometry); #endif +#ifndef Q_OS_MAC // also show the QMainWindow's menuBar QMenuBar* menuBar = _window->menuBar(); if (menuBar) { menuBar->setVisible(true); } +#endif } From bd29fb247497856f688f699780904eef1958c41f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Aug 2015 19:31:51 -0700 Subject: [PATCH 21/25] fix ifdef --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a088c42f87..b9bafbab1a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1744,7 +1744,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { return; } -#if 1 //ndef Q_OS_MAC +#ifndef Q_OS_MAC // If in full screen, and our main windows menu bar is hidden, and we're close to the top of the QMainWindow // then show the menubar. if (_window->isFullScreen()) { From ef4ad3107e1c95f58208e470948070050412b35d Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 20 Aug 2015 22:11:11 -0700 Subject: [PATCH 22/25] First pass, grenade toy. Needs art. --- examples/toys/grenade.js | 197 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 examples/toys/grenade.js diff --git a/examples/toys/grenade.js b/examples/toys/grenade.js new file mode 100644 index 0000000000..545bfbb982 --- /dev/null +++ b/examples/toys/grenade.js @@ -0,0 +1,197 @@ +// +// Grenade.js +// examples +// +// Created by Philip Rosedale on August 20, 2015 +// Copyright 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 +// + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +var grenadeURL = HIFI_PUBLIC_BUCKET + "models/props/grenade/grenade.fbx"; +var fuseSoundURL = HIFI_PUBLIC_BUCKET + "sounds/burningFuse.wav"; +var boomSoundURL = HIFI_PUBLIC_BUCKET + "sounds/explosion.wav"; + +var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0); +var audioOptions = { + volume: 0.5, + loop: true +} + +var injector = null; + +var fuseSound = SoundCache.getSound(fuseSoundURL, audioOptions.isStereo); +var boomSound = SoundCache.getSound(boomSoundURL, audioOptions.isStereo); + +var grenade = null; +var particles = null; +var properties = null; +var originalPosition = null; +var isGrenade = false; +var isBurning = false; + +var animationSettings = JSON.stringify({ + running: true, + loop: true + }); +var explodeAnimationSettings = JSON.stringify({ + running: true, + loop: false + }); + +var GRAVITY = -9.8; +var TIME_TO_EXPLODE = 2500; +var DISTANCE_IN_FRONT_OF_ME = 1.0; + +function makeGrenade() { + var position = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, + { x: 0, y: 0.0, z: -DISTANCE_IN_FRONT_OF_ME })); + var rotation = Quat.multiply(MyAvatar.orientation, + Quat.fromPitchYawRollDegrees(0, -90, 0)); + grenade = Entities.addEntity({ + type: "Model", + position: position, + rotation: rotation, + dimensions: { x: 0.09, + y: 0.20, + z: 0.09 }, + collisionsWillMove: true, + modelURL: grenadeURL, + shapeType: "box" + }); + + properties = Entities.getEntityProperties(grenade); + audioOptions.position = position; + audioOptions.orientation = rotation; + originalPosition = position; +} + +function update() { + if (!grenade) { + makeGrenade(); + } else { + var newProperties = Entities.getEntityProperties(grenade); + if (!isBurning) { + // If moved, start fuse + var FUSE_START_MOVE = 0.01; + if (Vec3.length(Vec3.subtract(newProperties.position, originalPosition)) > FUSE_START_MOVE) { + isBurning = true; + // Create fuse particles + particles = Entities.addEntity({ + type: "ParticleEffect", + animationSettings: animationSettings, + position: newProperties.position, + textures: 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png', + emitRate: 100, + emitStrength: 2.0, + emitDirection: { x: 0.0, y: 1.0, z: 0.0 }, + color: { red: 200, green: 0, blue: 0 }, + lifespan: 10.0, + visible: true, + locked: false + + }); + // Start fuse sound + injector = Audio.playSound(fuseSound, audioOptions); + // Start explosion timer + Script.setTimeout(boom, TIME_TO_EXPLODE); + originalPosition = newProperties.position; + // Add gravity + Entities.editEntity(grenade, { gravity: {x: 0, y: GRAVITY, z: 0 }}); + } + } + + if (newProperties.type === "Model") { + if (newProperties.position != properties.position) { + audioOptions.position = newProperties.position; + } + if (newProperties.orientation != properties.orientation) { + audioOptions.orientation = newProperties.orientation; + } + + properties = newProperties; + // Update sound location if playing + if (injector) { + injector.options = audioOptions; + } + if (particles) { + Entities.editEntity(particles, { position: newProperties.position }); + } + } else { + grenade = null; + Script.update.disconnect(update); + Script.scriptEnding.connect(scriptEnding); + scriptEnding(); + Script.stop(); + } + } +} +function boom() { + injector.stop(); + isBurning = false; + var audioOptions = { + position: properties.position, + volume: 0.75, + loop: false + } + Audio.playSound(boomSound, audioOptions); + Entities.addEntity({ + type: "ParticleEffect", + animationSettings: explodeAnimationSettings, + position: properties.position, + textures: 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png', + emitRate: 200, + emitStrength: 3.0, + emitDirection: { x: 0.0, y: 1.0, z: 0.0 }, + color: { red: 255, green: 255, blue: 0 }, + lifespan: 2.0, + visible: true, + lifetime: 2, + locked: false + + }); + var BLAST_RADIUS = 20.0; + var LIFT_DEPTH = 2.0; + var epicenter = properties.position; + epicenter.y -= LIFT_DEPTH; + blowShitUp(epicenter, BLAST_RADIUS); + deleteStuff(); +} + +function blowShitUp(position, radius) { + var stuff = Entities.findEntities(position, radius); + var numMoveable = 0; + var STRENGTH = 3.5; + for (var i = 0; i < stuff.length; i++) { + var properties = Entities.getEntityProperties(stuff[i]); + if (properties.collisionsWillMove) { + var diff = Vec3.subtract(properties.position, position); + var distance = Vec3.length(diff); + var velocity = Vec3.sum(properties.velocity, Vec3.multiply(STRENGTH * 1.0 / distance, Vec3.normalize(diff))); + Entities.editEntity(stuff[i], { velocity: velocity }); + } + } +} + +function scriptEnding() { + deleteStuff(); +} + +function deleteStuff() { + if (grenade != null) { + Entities.deleteEntity(grenade); + grenade = null; + } + if (particles != null) { + Entities.deleteEntity(particles); + particles = null; + } +} + +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); + From 7284e3aa3c42e4b188672531411e5f7d3d512f46 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 20 Aug 2015 22:20:51 -0700 Subject: [PATCH 23/25] added a little bit of spin, too. --- examples/toys/grenade.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/toys/grenade.js b/examples/toys/grenade.js index 545bfbb982..b2dfebb888 100644 --- a/examples/toys/grenade.js +++ b/examples/toys/grenade.js @@ -166,13 +166,17 @@ function blowShitUp(position, radius) { var stuff = Entities.findEntities(position, radius); var numMoveable = 0; var STRENGTH = 3.5; + var SPIN_RATE = 20.0; for (var i = 0; i < stuff.length; i++) { var properties = Entities.getEntityProperties(stuff[i]); if (properties.collisionsWillMove) { var diff = Vec3.subtract(properties.position, position); var distance = Vec3.length(diff); var velocity = Vec3.sum(properties.velocity, Vec3.multiply(STRENGTH * 1.0 / distance, Vec3.normalize(diff))); - Entities.editEntity(stuff[i], { velocity: velocity }); + var angularVelocity = { x: Math.random() * SPIN_RATE, y: Math.random() * SPIN_RATE, z: Math.random() * SPIN_RATE }; + angularVelocity = Vec3.multiply( 1.0 / distance, angularVelocity); + Entities.editEntity(stuff[i], { velocity: velocity, + angularVelocity: angularVelocity }); } } } From cee570d2b26fb787e0f04dc26ed5ec692e949144 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 20 Aug 2015 23:06:05 -0700 Subject: [PATCH 24/25] First pass at basketball, just appears in front of you with some reasonable values --- examples/toys/basketball.js | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 examples/toys/basketball.js diff --git a/examples/toys/basketball.js b/examples/toys/basketball.js new file mode 100644 index 0000000000..d30dce6e72 --- /dev/null +++ b/examples/toys/basketball.js @@ -0,0 +1,82 @@ +// +// basketball.js +// examples +// +// Created by Philip Rosedale on August 20, 2015 +// Copyright 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 +// + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; +var collisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; + + +var basketball = null; +var originalPosition = null; +var hasMoved = false; + +var GRAVITY = -9.8; +var DISTANCE_IN_FRONT_OF_ME = 1.0; +var START_MOVE = 0.01; +var DIAMETER = 0.30; + +function makeBasketball() { + var position = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, + { x: 0, y: 0.0, z: -DISTANCE_IN_FRONT_OF_ME })); + var rotation = Quat.multiply(MyAvatar.orientation, + Quat.fromPitchYawRollDegrees(0, -90, 0)); + basketball = Entities.addEntity({ + type: "Model", + position: position, + rotation: rotation, + dimensions: { x: DIAMETER, + y: DIAMETER, + z: DIAMETER }, + collisionsWillMove: true, + collisionSoundURL: collisionSoundURL, + modelURL: basketballURL, + restitution: 1.0, + linearDamping: 0.00001, + shapeType: "sphere" + }); + originalPosition = position; +} + +function update() { + if (!basketball) { + makeBasketball(); + } else { + var newProperties = Entities.getEntityProperties(basketball); + var moved = Vec3.length(Vec3.subtract(originalPosition, newProperties.position)); + if (!hasMoved && (moved > START_MOVE)) { + hasMoved = true; + Entities.editEntity(basketball, { gravity: {x: 0, y: GRAVITY, z: 0 }}); + } + var MAX_DISTANCE = 10.0; + var distance = Vec3.length(Vec3.subtract(MyAvatar.position, newProperties.position)); + if (distance > MAX_DISTANCE) { + deleteStuff(); + } + } +} + +function scriptEnding() { + deleteStuff(); +} + +function deleteStuff() { + if (basketball != null) { + Entities.deleteEntity(basketball); + basketball = null; + hasMoved = false; + } +} + +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); + From a0552050b08681e939afb36e316499f2a5833d9c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Aug 2015 08:23:18 -0700 Subject: [PATCH 25/25] set vsync on startup --- interface/src/Application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b9bafbab1a..aa18e602bf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -737,6 +737,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : }); connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); + + setVSyncEnabled(); // make sure VSync is set properly at startup } void Application::aboutToQuit() {