diff --git a/CMakeLists.txt b/CMakeLists.txt
index 38bcb42e26..b2f35b1443 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -213,3 +213,12 @@ endif ()
if (ANDROID OR DESKTOP_GVR)
add_subdirectory(gvr-interface)
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)
+endif ()
diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt
index edd68e12bf..315eeb6b83 100644
--- a/assignment-client/CMakeLists.txt
+++ b/assignment-client/CMakeLists.txt
@@ -18,4 +18,7 @@ if (UNIX)
endif (UNIX)
include_application_version()
-copy_dlls_beside_windows_executable()
\ No newline at end of file
+
+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 e4fa1d874d..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 ()
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..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.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..23957380e6 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(double(QDateTime::currentMSecsSinceEpoch() - node->getWakeTimestamp()) / 1000.0);
// if the node has pool information, add it
DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData());
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);
+
diff --git a/examples/toys/grenade.js b/examples/toys/grenade.js
new file mode 100644
index 0000000000..b2dfebb888
--- /dev/null
+++ b/examples/toys/grenade.js
@@ -0,0 +1,201 @@
+//
+// 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;
+ 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)));
+ 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 });
+ }
+ }
+}
+
+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);
+
diff --git a/examples/utilities/tools/cookies.js b/examples/utilities/tools/cookies.js
index 1a8695188b..d9fa999a13 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);
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 d7ee9228f4..0c531dfacb 100644
--- a/interface/CMakeLists.txt
+++ b/interface/CMakeLists.txt
@@ -205,4 +205,6 @@ else (APPLE)
endif()
endif (APPLE)
+setup_memory_debugger()
+
copy_dlls_beside_windows_executable()
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index a28435e258..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() {
@@ -1744,6 +1746,27 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
return;
}
+#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()) {
+ 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
@@ -4985,6 +5008,14 @@ void Application::setFullscreen(const QScreen* target) {
#endif
_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) {
@@ -5015,6 +5046,14 @@ void Application::unsetFullscreen(const QScreen* avoid) {
#else
_window->setGeometry(targetGeometry);
#endif
+
+#ifndef Q_OS_MAC
+ // also show the QMainWindow's menuBar
+ QMenuBar* menuBar = _window->menuBar();
+ if (menuBar) {
+ menuBar->setVisible(true);
+ }
+#endif
}
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/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 9ab2c83a79..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);
@@ -277,6 +274,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;
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 52473a6d47..eada41eb29 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -843,10 +843,11 @@ 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
- //
+ // And set the correctedLookAt for all (nearby) avatars that are looking at me.
_lookAtTargetAvatar.reset();
_targetAvatarPosition = glm::vec3(0.0f);
@@ -870,14 +871,51 @@ 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);
+ 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.)
+ // 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]);
+ 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);
+
+
+ // 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();
+
+ // Scale by proportional differences between avatar and human.
+ 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...
+ /* 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 = 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;
+
+ avatar->getHead()->setCorrectedLookAtPosition(corrected);
+
} else {
avatar->getHead()->clearCorrectedLookAtPosition();
}
@@ -1114,6 +1152,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();
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/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/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/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/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/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/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/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp
index 19443e01da..72436fc55e 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,19 @@
#include "EmbeddedWebserverLogging.h"
#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),
_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(SOCKET_CHECK_INTERVAL_IN_MS);
}
void HTTPManager::incomingConnection(qintptr socketDescriptor) {
@@ -157,3 +161,19 @@ 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)) {
+ qCritical() << "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
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-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/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/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/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/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/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-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/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 139b99e426..670a13f081 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 WebSockets Widgets)
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
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/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/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)
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);