From d88a13fc80c94188ffe66caff1cb68f2c4e076f8 Mon Sep 17 00:00:00 2001
From: danteruiz <danteruiz102@gmail.com>
Date: Tue, 2 Apr 2019 13:28:41 -0700
Subject: [PATCH 01/23] fix keybaord scaling

---
 interface/src/Application.cpp | 4 +++-
 interface/src/ui/Keyboard.h   | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 8225d27033..f1bbb0ab9e 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -5847,7 +5847,9 @@ void Application::reloadResourceCaches() {
     DependencyManager::get<ScriptEngines>()->reloadAllScripts();
     getOffscreenUI()->clearCache();
 
-    DependencyManager::get<Keyboard>()->createKeyboard();
+    auto keyboard = DependencyManager::get<Keyboard>();
+    keyboard->createKeyboard();
+    keyboard->scaleKeyboard(getMyAvatar()->getSensorToWorldScale());
 
     getMyAvatar()->resetFullAvatarURL();
 }
diff --git a/interface/src/ui/Keyboard.h b/interface/src/ui/Keyboard.h
index 51e5e0571f..2b6829bf2b 100644
--- a/interface/src/ui/Keyboard.h
+++ b/interface/src/ui/Keyboard.h
@@ -98,6 +98,7 @@ public:
     bool isPassword() const;
     void setPassword(bool password);
     void enableRightMallet();
+    void scaleKeyboard(float sensorToWorldScale);
     void enableLeftMallet();
     void disableRightMallet();
     void disableLeftMallet();
@@ -122,7 +123,6 @@ public slots:
     void handleTriggerContinue(const QUuid& id, const PointerEvent& event);
     void handleHoverBegin(const QUuid& id, const PointerEvent& event);
     void handleHoverEnd(const QUuid& id, const PointerEvent& event);
-    void scaleKeyboard(float sensorToWorldScale);
 
 private:
     struct Anchor {

From 5f5e7d5f4e9b4f7fb716dedb0ba80eac04f66d43 Mon Sep 17 00:00:00 2001
From: danteruiz <danteruiz102@gmail.com>
Date: Wed, 3 Apr 2019 10:45:40 -0700
Subject: [PATCH 02/23] a better fix

---
 interface/src/Application.cpp | 4 +---
 interface/src/ui/Keyboard.cpp | 3 +++
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index f1bbb0ab9e..8225d27033 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -5847,9 +5847,7 @@ void Application::reloadResourceCaches() {
     DependencyManager::get<ScriptEngines>()->reloadAllScripts();
     getOffscreenUI()->clearCache();
 
-    auto keyboard = DependencyManager::get<Keyboard>();
-    keyboard->createKeyboard();
-    keyboard->scaleKeyboard(getMyAvatar()->getSensorToWorldScale());
+    DependencyManager::get<Keyboard>()->createKeyboard();
 
     getMyAvatar()->resetFullAvatarURL();
 }
diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp
index 1cbe31f1eb..6262210620 100644
--- a/interface/src/ui/Keyboard.cpp
+++ b/interface/src/ui/Keyboard.cpp
@@ -910,6 +910,9 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) {
         });
         _layerIndex = 0;
         addIncludeItemsToMallets();
+
+        auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
+        scaleKeyboard(myAvatar->getSensorToWorldScale());
     });
 
     request->send();

From d1d999dd7228d186ceea2165c73f68fc2b068edd Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Fri, 5 Apr 2019 19:58:26 +0100
Subject: [PATCH 03/23] init node order fix

---
 libraries/fbx/src/GLTFSerializer.cpp | 37 ++++++++++++++++++++--------
 libraries/fbx/src/GLTFSerializer.h   |  2 +-
 2 files changed, 28 insertions(+), 11 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 1699722215..ec58f15a95 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -751,7 +751,7 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>&
     }
 }
 
-void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, std::vector<int>& result) {
+void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool order, std::vector<int>& result) {
     int startingIndex = 0;
     int finalIndex = (int)children.size();
     if (stride == -1) {
@@ -763,10 +763,12 @@ void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children
         result.push_back(c);
         std::vector<int> nested = _file.nodes[c].children.toStdVector();
         if (nested.size() != 0) {
-            std::sort(nested.begin(), nested.end());
+            if (order) {
+                std::sort(nested.begin(), nested.end());
+            }
             for (int r : nested) {
                 if (result.end() == std::find(result.begin(), result.end(), r)) {
-                    getNodeQueueByDepthFirstChildren(nested, stride, result);
+                    getNodeQueueByDepthFirstChildren(nested, stride, order, result);
                 }
             }
         } 
@@ -776,13 +778,18 @@ void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children
 
 bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     int numNodes = _file.nodes.size();
+    bool skinnedModel = !_file.skins.isEmpty();
 
     //Build dependencies
     QVector<QVector<int>> nodeDependencies(numNodes);
     int nodecount = 0;
+    bool parentOutOfOrder = false;
     foreach(auto &node, _file.nodes) {
         //nodes_transforms.push_back(getModelTransform(node));
-        foreach(int child, node.children) nodeDependencies[child].push_back(nodecount);
+        foreach(int child, node.children) {
+            nodeDependencies[child].push_back(nodecount);
+            parentOutOfOrder |= nodecount < child;
+        }
         nodecount++;
     }
     
@@ -806,20 +813,27 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
 
 
     // initialize order in which nodes will be parsed
+    bool sortNodes = (!parentOutOfOrder && skinnedModel) || !skinnedModel;
     std::vector<int> nodeQueue;
     nodeQueue.reserve(numNodes);
     int rootNode = 0;
     int finalNode = numNodes;
-    if (!_file.scenes[_file.scene].nodes.contains(0)) {
-        rootNode = numNodes - 1;
-        finalNode = -1;
+    for (int sceneNode : _file.scenes[_file.scene].nodes) {
+        if (!_file.nodes[sceneNode].defined["camera"] && sceneNode != 0) {
+            rootNode = numNodes - 1;
+            finalNode = -1;
+            break;
+        }
     }
     bool rootAtStartOfList = rootNode < finalNode;
     int nodeListStride = 1;
     if (!rootAtStartOfList) { nodeListStride = -1; }
 
     QVector<int> initialSceneNodes = _file.scenes[_file.scene].nodes; 
-    std::sort(initialSceneNodes.begin(), initialSceneNodes.end());
+    if (sortNodes) {
+        std::sort(initialSceneNodes.begin(), initialSceneNodes.end());
+    }
+
     int sceneRootNode = 0; 
     int sceneFinalNode = initialSceneNodes.size();
     if (!rootAtStartOfList) { 
@@ -830,10 +844,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
         int i = initialSceneNodes[index];
         nodeQueue.push_back(i);
         std::vector<int> children = _file.nodes[i].children.toStdVector(); 
-        std::sort(children.begin(), children.end());
-        getNodeQueueByDepthFirstChildren(children, nodeListStride, nodeQueue);
+        if (sortNodes) {
+            std::sort(children.begin(), children.end());
+        }
+        getNodeQueueByDepthFirstChildren(children, nodeListStride, sortNodes, nodeQueue);
     }
 
+
     // Build joints
     HFMJoint joint;
     joint.distanceToParent = 0;
diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h
index d9c477bd99..dd3649478e 100755
--- a/libraries/fbx/src/GLTFSerializer.h
+++ b/libraries/fbx/src/GLTFSerializer.h
@@ -713,7 +713,7 @@ private:
 
     glm::mat4 getModelTransform(const GLTFNode& node);
     void getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues);
-    void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, std::vector<int>& result);
+    void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool order, std::vector<int>& result);
 
     bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url);
     bool parseGLTF(const hifi::ByteArray& data);

From 6f5786e01717f9e862627d66540788d93f17d0b5 Mon Sep 17 00:00:00 2001
From: NissimHadar <nissim.hadar@gmail.com>
Date: Fri, 5 Apr 2019 12:25:25 -0700
Subject: [PATCH 04/23] Added 'metaverse' to dev-build URL.

---
 tools/nitpick/src/TestRunner.h | 2 +-
 tools/nitpick/src/common.h     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/nitpick/src/TestRunner.h b/tools/nitpick/src/TestRunner.h
index 6d36f246f7..13549e6eec 100644
--- a/tools/nitpick/src/TestRunner.h
+++ b/tools/nitpick/src/TestRunner.h
@@ -61,7 +61,7 @@ protected:
 
     QString _workingFolder;
 
-    const QString DEV_BUILD_XML_URL{ "https://highfidelity.com/dev-builds.xml" };
+    const QString DEV_BUILD_XML_URL{ "https://metaverse.highfidelity.com/dev-builds.xml" };
     const QString DEV_BUILD_XML_FILENAME{ "dev-builds.xml" };
 
     bool buildXMLDownloaded;
diff --git a/tools/nitpick/src/common.h b/tools/nitpick/src/common.h
index dc1ca17d08..b375f1ee2b 100644
--- a/tools/nitpick/src/common.h
+++ b/tools/nitpick/src/common.h
@@ -60,5 +60,5 @@ const double R_Y = 0.212655f;
 const double G_Y = 0.715158f;
 const double B_Y = 0.072187f;
 
-const QString nitpickVersion{ "v3.2.0" }; 
+const QString nitpickVersion{ "v3.2.1" }; 
 #endif // hifi_common_h
\ No newline at end of file

From 75e1437274fdc557008ee1fe19abbcb3a636a3e9 Mon Sep 17 00:00:00 2001
From: NissimHadar <nissim.hadar@gmail.com>
Date: Fri, 5 Apr 2019 14:55:23 -0700
Subject: [PATCH 05/23] Use optional environment variable for timestamp server
 URL

---
 CMakeLists.txt                                  | 5 +++++
 cmake/macros/OptionalWinExecutableSigning.cmake | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1ba5e1264f..0fad5c10f1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -52,6 +52,11 @@ else()
   set(MOBILE 0)
 endif()
 
+# Use default time server if none defined in environment
+if (NOT DEFINED TIMESERVER_URL)
+  set(TIMESERVER_URL "http://sha256timestamp.ws.symantec.com/sha256/timestamp")
+endif()
+
 set(HIFI_USE_OPTIMIZED_IK OFF)
 set(BUILD_CLIENT_OPTION ON)
 set(BUILD_SERVER_OPTION ON)
diff --git a/cmake/macros/OptionalWinExecutableSigning.cmake b/cmake/macros/OptionalWinExecutableSigning.cmake
index 069fc12fc5..cbefdaea8f 100644
--- a/cmake/macros/OptionalWinExecutableSigning.cmake
+++ b/cmake/macros/OptionalWinExecutableSigning.cmake
@@ -22,7 +22,7 @@ macro(optional_win_executable_signing)
         # setup a post build command to sign the executable
         add_custom_command(
           TARGET ${TARGET_NAME} POST_BUILD
-          COMMAND ${SIGNTOOL_EXECUTABLE} sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 ${EXECUTABLE_PATH}
+          COMMAND ${SIGNTOOL_EXECUTABLE} sign /fd sha256 /f %HF_PFX_FILE% /p %HF_PFX_PASSPHRASE% /tr ${TIMESERVER_URL} /td SHA256 ${EXECUTABLE_PATH}
         )
       else ()
         message(FATAL_ERROR "HF_PFX_PASSPHRASE must be set for executables to be signed.")

From 53ea50d1f345f68d860106dd19b12347e19fb20b Mon Sep 17 00:00:00 2001
From: NissimHadar <nissim.hadar@gmail.com>
Date: Fri, 5 Apr 2019 15:45:11 -0700
Subject: [PATCH 06/23] Use set_from_env macro.

---
 CMakeLists.txt | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0fad5c10f1..551cfd2636 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -53,9 +53,7 @@ else()
 endif()
 
 # Use default time server if none defined in environment
-if (NOT DEFINED TIMESERVER_URL)
-  set(TIMESERVER_URL "http://sha256timestamp.ws.symantec.com/sha256/timestamp")
-endif()
+set_from_env(TIMESERVER_URL TIMESERVER_URL "http://sha256timestamp.ws.symantec.com/sha256/timestamp")
 
 set(HIFI_USE_OPTIMIZED_IK OFF)
 set(BUILD_CLIENT_OPTION ON)

From 40fe156c8b25ec2a3124d6c5f9d3fd02fe46fc0e Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Mon, 8 Apr 2019 19:54:31 +0100
Subject: [PATCH 07/23] naming, condition

---
 libraries/fbx/src/GLTFSerializer.cpp | 14 +++++++-------
 libraries/fbx/src/GLTFSerializer.h   |  2 +-
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index ec58f15a95..21df5bc2dc 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -751,7 +751,7 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>&
     }
 }
 
-void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool order, std::vector<int>& result) {
+void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool needToSort, std::vector<int>& result) {
     int startingIndex = 0;
     int finalIndex = (int)children.size();
     if (stride == -1) {
@@ -763,12 +763,12 @@ void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children
         result.push_back(c);
         std::vector<int> nested = _file.nodes[c].children.toStdVector();
         if (nested.size() != 0) {
-            if (order) {
+            if (needToSort) {
                 std::sort(nested.begin(), nested.end());
             }
             for (int r : nested) {
                 if (result.end() == std::find(result.begin(), result.end(), r)) {
-                    getNodeQueueByDepthFirstChildren(nested, stride, order, result);
+                    getNodeQueueByDepthFirstChildren(nested, stride, needToSort, result);
                 }
             }
         } 
@@ -813,7 +813,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
 
 
     // initialize order in which nodes will be parsed
-    bool sortNodes = (!parentOutOfOrder && skinnedModel) || !skinnedModel;
+    bool needToSort = !parentOutOfOrder || !skinnedModel;
     std::vector<int> nodeQueue;
     nodeQueue.reserve(numNodes);
     int rootNode = 0;
@@ -830,7 +830,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     if (!rootAtStartOfList) { nodeListStride = -1; }
 
     QVector<int> initialSceneNodes = _file.scenes[_file.scene].nodes; 
-    if (sortNodes) {
+    if (needToSort) {
         std::sort(initialSceneNodes.begin(), initialSceneNodes.end());
     }
 
@@ -844,10 +844,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
         int i = initialSceneNodes[index];
         nodeQueue.push_back(i);
         std::vector<int> children = _file.nodes[i].children.toStdVector(); 
-        if (sortNodes) {
+        if (needToSort) {
             std::sort(children.begin(), children.end());
         }
-        getNodeQueueByDepthFirstChildren(children, nodeListStride, sortNodes, nodeQueue);
+        getNodeQueueByDepthFirstChildren(children, nodeListStride, needToSort, nodeQueue);
     }
 
 
diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h
index dd3649478e..3eee24284e 100755
--- a/libraries/fbx/src/GLTFSerializer.h
+++ b/libraries/fbx/src/GLTFSerializer.h
@@ -713,7 +713,7 @@ private:
 
     glm::mat4 getModelTransform(const GLTFNode& node);
     void getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues);
-    void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool order, std::vector<int>& result);
+    void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool needToSort, std::vector<int>& result);
 
     bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url);
     bool parseGLTF(const hifi::ByteArray& data);

From 523253aa13b9b8d188dfa5c2dd49dd17d99faf1b Mon Sep 17 00:00:00 2001
From: NissimHadar <nissim.hadar@gmail.com>
Date: Mon, 8 Apr 2019 18:21:04 -0700
Subject: [PATCH 08/23] Added vhacd-util to all installers.

---
 tools/CMakeLists.txt | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index b9ae635a4f..a323dc46af 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -32,6 +32,7 @@ if (BUILD_TOOLS)
             skeleton-dump
             atp-client
             oven
+            vhacd-util
         )
     else()
         set(ALL_TOOLS 
@@ -45,6 +46,7 @@ if (BUILD_TOOLS)
             skeleton-dump
             atp-client
             oven
+            vhacd-util
             nitpick
         )
     endif()

From 781949da4c1ed3ec1af9764d021c0edc8d617372 Mon Sep 17 00:00:00 2001
From: NissimHadar <nissim.hadar@gmail.com>
Date: Mon, 8 Apr 2019 18:33:33 -0700
Subject: [PATCH 09/23] Added package_libraries_for_deployment.

---
 tools/CMakeLists.txt            | 12 +++++-------
 tools/vhacd-util/CMakeLists.txt |  4 +++-
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index a323dc46af..fd74786a5e 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -21,8 +21,8 @@ endfunction()
 if (BUILD_TOOLS)
     # Allow different tools for stable builds
     if (STABLE_BUILD)
-        set(ALL_TOOLS 
-            udt-test 
+        set(ALL_TOOLS
+            udt-test
             vhacd-util
             frame-optimizer
             gpu-frame-player
@@ -32,11 +32,10 @@ if (BUILD_TOOLS)
             skeleton-dump
             atp-client
             oven
-            vhacd-util
         )
     else()
-        set(ALL_TOOLS 
-            udt-test 
+        set(ALL_TOOLS
+            udt-test
             vhacd-util
             frame-optimizer
             gpu-frame-player
@@ -46,11 +45,10 @@ if (BUILD_TOOLS)
             skeleton-dump
             atp-client
             oven
-            vhacd-util
             nitpick
         )
     endif()
-    
+
     foreach(TOOL ${ALL_TOOLS})
         check_test(${TOOL})
         if (${BUILD_TOOL_RESULT})
diff --git a/tools/vhacd-util/CMakeLists.txt b/tools/vhacd-util/CMakeLists.txt
index 90cfdf878a..eccc496170 100644
--- a/tools/vhacd-util/CMakeLists.txt
+++ b/tools/vhacd-util/CMakeLists.txt
@@ -12,7 +12,9 @@ target_link_libraries(${TARGET_NAME} ${VHACD_LIBRARIES})
 
 setup_memory_debugger()
 
-if (UNIX AND NOT APPLE)
+if (WIN32)
+  package_libraries_for_deployment()
+elseif (UNIX AND NOT APPLE)
   include(FindOpenMP)
   if(OPENMP_FOUND)
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")

From c970a58715fcc4e568b2dd99acf21939ede919ce Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Tue, 9 Apr 2019 17:29:07 +0100
Subject: [PATCH 10/23] comments, edge case for skinned

---
 libraries/fbx/src/GLTFSerializer.cpp | 31 ++++++++++++++--------------
 libraries/fbx/src/GLTFSerializer.h   |  2 +-
 2 files changed, 16 insertions(+), 17 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 21df5bc2dc..e4a759fa9b 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -751,24 +751,23 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>&
     }
 }
 
-void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool needToSort, std::vector<int>& result) {
+void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool addChildrenInReverseOrder, std::vector<int>& result) {
     int startingIndex = 0;
     int finalIndex = (int)children.size();
-    if (stride == -1) {
+    if (stride == -1 || addChildrenInReverseOrder) {
         startingIndex = (int)children.size() - 1;
         finalIndex = -1;
+        stride = -1; 
     }
     for (int index = startingIndex; index != finalIndex; index += stride) {
         int c = children[index];
         result.push_back(c);
         std::vector<int> nested = _file.nodes[c].children.toStdVector();
         if (nested.size() != 0) {
-            if (needToSort) {
-                std::sort(nested.begin(), nested.end());
-            }
+            std::sort(nested.begin(), nested.end());
             for (int r : nested) {
                 if (result.end() == std::find(result.begin(), result.end(), r)) {
-                    getNodeQueueByDepthFirstChildren(nested, stride, needToSort, result);
+                    getNodeQueueByDepthFirstChildren(nested, stride, addChildrenInReverseOrder, result);
                 }
             }
         } 
@@ -783,12 +782,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     //Build dependencies
     QVector<QVector<int>> nodeDependencies(numNodes);
     int nodecount = 0;
-    bool parentOutOfOrder = false;
+    bool parentGreaterThanChild = false;
     foreach(auto &node, _file.nodes) {
         //nodes_transforms.push_back(getModelTransform(node));
         foreach(int child, node.children) {
             nodeDependencies[child].push_back(nodecount);
-            parentOutOfOrder |= nodecount < child;
+            parentGreaterThanChild |= nodecount > child;
         }
         nodecount++;
     }
@@ -813,12 +812,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
 
 
     // initialize order in which nodes will be parsed
-    bool needToSort = !parentOutOfOrder || !skinnedModel;
     std::vector<int> nodeQueue;
     nodeQueue.reserve(numNodes);
     int rootNode = 0;
     int finalNode = numNodes;
     for (int sceneNode : _file.scenes[_file.scene].nodes) {
+        // reverse the order in which the nodes are initialized 
         if (!_file.nodes[sceneNode].defined["camera"] && sceneNode != 0) {
             rootNode = numNodes - 1;
             finalNode = -1;
@@ -830,9 +829,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     if (!rootAtStartOfList) { nodeListStride = -1; }
 
     QVector<int> initialSceneNodes = _file.scenes[_file.scene].nodes; 
-    if (needToSort) {
-        std::sort(initialSceneNodes.begin(), initialSceneNodes.end());
-    }
+    std::sort(initialSceneNodes.begin(), initialSceneNodes.end());
 
     int sceneRootNode = 0; 
     int sceneFinalNode = initialSceneNodes.size();
@@ -840,14 +837,16 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
         sceneRootNode = initialSceneNodes.size() - 1;
         sceneFinalNode = -1;
     }
+    // this is an edge case where, for a skinned model, there is a parent who's index is greater than it's child's index
+    // when the opposite is expected
+    // in this case, we want the order of the children to be reversed, even if the root node is 0
+    bool addChildrenInReverseOrder = rootAtStartOfList && parentGreaterThanChild && skinnedModel;
     for (int index = sceneRootNode; index != sceneFinalNode; index += nodeListStride) {
         int i = initialSceneNodes[index];
         nodeQueue.push_back(i);
         std::vector<int> children = _file.nodes[i].children.toStdVector(); 
-        if (needToSort) {
-            std::sort(children.begin(), children.end());
-        }
-        getNodeQueueByDepthFirstChildren(children, nodeListStride, needToSort, nodeQueue);
+        std::sort(children.begin(), children.end());
+        getNodeQueueByDepthFirstChildren(children, nodeListStride, addChildrenInReverseOrder, nodeQueue);
     }
 
 
diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h
index 3eee24284e..ca4e79fcb6 100755
--- a/libraries/fbx/src/GLTFSerializer.h
+++ b/libraries/fbx/src/GLTFSerializer.h
@@ -713,7 +713,7 @@ private:
 
     glm::mat4 getModelTransform(const GLTFNode& node);
     void getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues);
-    void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool needToSort, std::vector<int>& result);
+    void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool addChildrenInReverseOrder, std::vector<int>& result);
 
     bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url);
     bool parseGLTF(const hifi::ByteArray& data);

From 13f0b97d07915b97cddbcc2a208a6167f5f07486 Mon Sep 17 00:00:00 2001
From: NissimHadar <nissim.hadar@gmail.com>
Date: Tue, 9 Apr 2019 10:40:04 -0700
Subject: [PATCH 11/23] Added install_beside_console

---
 tools/vhacd-util/CMakeLists.txt | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tools/vhacd-util/CMakeLists.txt b/tools/vhacd-util/CMakeLists.txt
index eccc496170..744e711694 100644
--- a/tools/vhacd-util/CMakeLists.txt
+++ b/tools/vhacd-util/CMakeLists.txt
@@ -22,3 +22,7 @@ elseif (UNIX AND NOT APPLE)
     set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
   endif()
 endif ()
+
+if (BUILD_SERVER)
+  install_beside_console()
+endif ()

From 3e292c4b03f90940ad4b80d76fa098810ba8f2cc Mon Sep 17 00:00:00 2001
From: NissimHadar <nissim.hadar@gmail.com>
Date: Tue, 9 Apr 2019 12:30:34 -0700
Subject: [PATCH 12/23] Install both oven and vhacd in both client and
 client+server builds.

---
 tools/oven/CMakeLists.txt       | 4 +---
 tools/vhacd-util/CMakeLists.txt | 4 +---
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt
index c9b1aca1d4..86fee8a067 100644
--- a/tools/oven/CMakeLists.txt
+++ b/tools/oven/CMakeLists.txt
@@ -18,6 +18,4 @@ elseif (APPLE)
   set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks")
 endif()
 
-if (BUILD_SERVER)
-  install_beside_console()
-endif ()
+install_beside_console()
diff --git a/tools/vhacd-util/CMakeLists.txt b/tools/vhacd-util/CMakeLists.txt
index 744e711694..79e1fcb774 100644
--- a/tools/vhacd-util/CMakeLists.txt
+++ b/tools/vhacd-util/CMakeLists.txt
@@ -23,6 +23,4 @@ elseif (UNIX AND NOT APPLE)
   endif()
 endif ()
 
-if (BUILD_SERVER)
-  install_beside_console()
-endif ()
+install_beside_console()

From 3c84a46cba8590980e5ea3479465705b72de5327 Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Mon, 15 Apr 2019 20:27:54 +0100
Subject: [PATCH 13/23] update how nodes are initially parsed

---
 libraries/fbx/src/GLTFSerializer.cpp | 143 +++++++++++++--------------
 libraries/fbx/src/GLTFSerializer.h   |   1 -
 2 files changed, 69 insertions(+), 75 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index e4a759fa9b..95d055481a 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -751,43 +751,25 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>&
     }
 }
 
-void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool addChildrenInReverseOrder, std::vector<int>& result) {
-    int startingIndex = 0;
-    int finalIndex = (int)children.size();
-    if (stride == -1 || addChildrenInReverseOrder) {
-        startingIndex = (int)children.size() - 1;
-        finalIndex = -1;
-        stride = -1; 
-    }
-    for (int index = startingIndex; index != finalIndex; index += stride) {
-        int c = children[index];
-        result.push_back(c);
-        std::vector<int> nested = _file.nodes[c].children.toStdVector();
-        if (nested.size() != 0) {
-            std::sort(nested.begin(), nested.end());
-            for (int r : nested) {
-                if (result.end() == std::find(result.begin(), result.end(), r)) {
-                    getNodeQueueByDepthFirstChildren(nested, stride, addChildrenInReverseOrder, result);
-                }
-            }
-        } 
-    }
-}
-
-
 bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     int numNodes = _file.nodes.size();
     bool skinnedModel = !_file.skins.isEmpty();
 
+
     //Build dependencies
     QVector<QVector<int>> nodeDependencies(numNodes);
+    QVector<int> parents;
+    QVector<int> nodesToSort = _file.scenes[_file.scene].nodes;
+    parents.fill(-1, numNodes);
+    nodesToSort.reserve(numNodes);
     int nodecount = 0;
-    bool parentGreaterThanChild = false;
     foreach(auto &node, _file.nodes) {
-        //nodes_transforms.push_back(getModelTransform(node));
         foreach(int child, node.children) {
             nodeDependencies[child].push_back(nodecount);
-            parentGreaterThanChild |= nodecount > child;
+            parents[child] = nodecount;
+        }
+        if (!nodesToSort.contains(nodecount)) {
+            nodesToSort.push_back(nodecount);
         }
         nodecount++;
     }
@@ -806,47 +788,62 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                 } else break;
             }
         }
-        
         nodecount++;
     }
 
-
+ 
     // initialize order in which nodes will be parsed
     std::vector<int> nodeQueue;
-    nodeQueue.reserve(numNodes);
-    int rootNode = 0;
-    int finalNode = numNodes;
-    for (int sceneNode : _file.scenes[_file.scene].nodes) {
-        // reverse the order in which the nodes are initialized 
-        if (!_file.nodes[sceneNode].defined["camera"] && sceneNode != 0) {
-            rootNode = numNodes - 1;
-            finalNode = -1;
-            break;
+    QVector<int> originalToNewNodeIndexMap;
+    QVector<bool> hasBeenSorted;
+    originalToNewNodeIndexMap.fill(-1, numNodes);
+    hasBeenSorted.fill(false, numNodes);
+    nodeQueue = _file.scenes[_file.scene].nodes.toStdVector();
+
+    for (int sceneNodeCount = 0; sceneNodeCount < _file.scenes[_file.scene].nodes.size(); sceneNodeCount++) {
+        int sceneNode = nodeQueue[sceneNodeCount];
+        originalToNewNodeIndexMap[sceneNode] = sceneNodeCount;
+        nodesToSort[nodesToSort.indexOf(sceneNode)] = nodesToSort.back();
+        nodesToSort.pop_back();
+        hasBeenSorted[sceneNode] = true;
+        for (int child : _file.nodes[sceneNode].children.toStdVector()) {
+            nodesToSort[nodesToSort.indexOf(child)] = nodesToSort.back();
+            nodesToSort.pop_back();
         }
-    }
-    bool rootAtStartOfList = rootNode < finalNode;
-    int nodeListStride = 1;
-    if (!rootAtStartOfList) { nodeListStride = -1; }
 
-    QVector<int> initialSceneNodes = _file.scenes[_file.scene].nodes; 
-    std::sort(initialSceneNodes.begin(), initialSceneNodes.end());
+        for (int child : _file.nodes[sceneNode].children) {
+            originalToNewNodeIndexMap[child] = nodeQueue.size();
+            nodeQueue.push_back(child);
+            hasBeenSorted[child] = true;
 
-    int sceneRootNode = 0; 
-    int sceneFinalNode = initialSceneNodes.size();
-    if (!rootAtStartOfList) { 
-        sceneRootNode = initialSceneNodes.size() - 1;
-        sceneFinalNode = -1;
-    }
-    // this is an edge case where, for a skinned model, there is a parent who's index is greater than it's child's index
-    // when the opposite is expected
-    // in this case, we want the order of the children to be reversed, even if the root node is 0
-    bool addChildrenInReverseOrder = rootAtStartOfList && parentGreaterThanChild && skinnedModel;
-    for (int index = sceneRootNode; index != sceneFinalNode; index += nodeListStride) {
-        int i = initialSceneNodes[index];
-        nodeQueue.push_back(i);
-        std::vector<int> children = _file.nodes[i].children.toStdVector(); 
-        std::sort(children.begin(), children.end());
-        getNodeQueueByDepthFirstChildren(children, nodeListStride, addChildrenInReverseOrder, nodeQueue);
+            if (!_file.nodes[child].children.isEmpty() && nodeQueue.size() < numNodes) {
+                int newSize = nodesToSort.size();
+                while (!nodesToSort.isEmpty()) {
+                    int i = 0;
+
+                    while (i < nodesToSort.size()) {
+                        int nodeIndex = nodesToSort[i];
+                        int parentIndex = parents[nodeIndex];
+                        newSize = nodesToSort.size();
+
+                        if ((parentIndex == -1 || hasBeenSorted[parentIndex])) {
+                            originalToNewNodeIndexMap[nodeIndex] = nodeQueue.size();
+                            nodeQueue.push_back(nodeIndex);
+                            hasBeenSorted[nodeIndex] = true;
+                            // copy back and pop
+                            nodesToSort[i] = nodesToSort.back();
+                            nodesToSort.pop_back();
+                        } else {  // skip
+                            i++;
+                        }
+                    }
+                    // if the end of nodesToSort is reached without removing any nodes, break to move onto the next child
+                    if (newSize == nodesToSort.size() && i == nodesToSort.size()) {
+                        break;
+                    }
+                }
+            }
+        }
     }
 
 
@@ -861,13 +858,13 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
 
         joint.parentIndex = -1;
         if (!_file.scenes[_file.scene].nodes.contains(nodeIndex)) {
-            joint.parentIndex = std::distance(nodeQueue.begin(), std::find(nodeQueue.begin(), nodeQueue.end(), nodeDependencies[nodeIndex][0]));
+            joint.parentIndex = originalToNewNodeIndexMap[parents[nodeIndex]];
         }
         joint.transform = node.transforms.first();
         joint.translation = extractTranslation(joint.transform);
         joint.rotation = glmExtractRotation(joint.transform);
         glm::vec3 scale = extractScale(joint.transform);
-        joint.postTransform = glm::scale(glm::mat4(), scale);        
+        joint.postTransform = glm::scale(glm::mat4(), scale);  
 
         joint.name = node.name;
         joint.isSkeletonJoint = false;
@@ -882,20 +879,19 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
         std::vector<std::vector<float>> inverseBindValues;
         getSkinInverseBindMatrices(inverseBindValues);
 
-        int jointIndex = finalNode;
-        while (jointIndex != rootNode) {
-            rootAtStartOfList ? jointIndex-- : jointIndex++;
-            int jOffset = nodeQueue[jointIndex];
+        for (int jointIndex = 0; jointIndex < numNodes; jointIndex++) {
+            int nodeIndex = nodeQueue[jointIndex];
             auto joint = hfmModel.joints[jointIndex];
 
             hfmModel.hasSkeletonJoints = true;
             for (int s = 0; s < _file.skins.size(); s++) {
-                auto skin = _file.skins[s];
-                joint.isSkeletonJoint = skin.joints.contains(jOffset);
+                const auto& skin = _file.skins[s];
+                int matrixIndex = skin.joints.indexOf(nodeIndex);
+                joint.isSkeletonJoint = skin.joints.contains(nodeIndex);
 
                 if (joint.isSkeletonJoint) {
-                    std::vector<float> value = inverseBindValues[s];
-                    int matrixCount = 16 * skin.joints.indexOf(jOffset);
+                    std::vector<float>& value = inverseBindValues[s];
+                    int matrixCount = 16 * matrixIndex;
                     jointInverseBindTransforms[jointIndex] =
                         glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3], 
                             value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7], 
@@ -932,7 +928,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     
     // Build meshes
     nodecount = 0;
-    for (int nodeIndex = rootNode; nodeIndex != finalNode; nodeIndex += nodeListStride) {
+    for (int nodeIndex : nodeQueue) {
         auto& node = _file.nodes[nodeIndex];
 
         if (node.defined["mesh"]) {
@@ -947,7 +943,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                     cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
                     mesh.clusters.append(cluster);
                 } else {
-                    for (int j = rootNode; j != finalNode; j += nodeListStride) {
+                    for (int j : nodeQueue) {
                         HFMCluster cluster;
                         cluster.jointIndex = j;
                         cluster.inverseBindMatrix = jointInverseBindTransforms[j];
@@ -956,7 +952,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                     }
                 }
                 HFMCluster root; 
-                root.jointIndex = rootNode;
+                root.jointIndex = nodeQueue[0];
                 if (root.jointIndex == -1) { 
                     root.jointIndex = 0; 
                 }
@@ -1181,7 +1177,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
         nodecount++;
     }
 
-    
     return true;
 }
 
diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h
index ca4e79fcb6..a9e7152e5b 100755
--- a/libraries/fbx/src/GLTFSerializer.h
+++ b/libraries/fbx/src/GLTFSerializer.h
@@ -713,7 +713,6 @@ private:
 
     glm::mat4 getModelTransform(const GLTFNode& node);
     void getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues);
-    void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, bool addChildrenInReverseOrder, std::vector<int>& result);
 
     bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url);
     bool parseGLTF(const hifi::ByteArray& data);

From 23918258d90c00a742e658d76b8c500d3ba0ea14 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@alumni.brown.edu>
Date: Tue, 16 Apr 2019 10:30:09 -0700
Subject: [PATCH 14/23] fix materialJSON paths in domain baker

---
 libraries/baking/src/MaterialBaker.cpp |  7 ++++++-
 libraries/baking/src/MaterialBaker.h   |  3 ++-
 tools/oven/src/DomainBaker.cpp         | 10 +++-------
 tools/oven/src/DomainBaker.h           |  2 +-
 4 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/libraries/baking/src/MaterialBaker.cpp b/libraries/baking/src/MaterialBaker.cpp
index 7fc2573d7b..79e31014fd 100644
--- a/libraries/baking/src/MaterialBaker.cpp
+++ b/libraries/baking/src/MaterialBaker.cpp
@@ -27,9 +27,10 @@ std::function<QThread*()> MaterialBaker::_getNextOvenWorkerThreadOperator;
 
 static int materialNum = 0;
 
-MaterialBaker::MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir) :
+MaterialBaker::MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir, QUrl destinationPath) :
     _materialData(materialData),
     _isURL(isURL),
+    _destinationPath(destinationPath),
     _bakedOutputDir(bakedOutputDir),
     _textureOutputDir(bakedOutputDir + "/materialTextures/" + QString::number(materialNum++))
 {
@@ -177,6 +178,10 @@ void MaterialBaker::handleFinishedTextureBaker() {
             auto newURL = QUrl(_textureOutputDir).resolved(baker->getMetaTextureFileName());
             auto relativeURL = QDir(_bakedOutputDir).relativeFilePath(newURL.toString());
 
+            if (!_destinationPath.isEmpty()) {
+                relativeURL = _destinationPath.resolved(relativeURL).toDisplayString();
+            }
+
             // Replace the old texture URLs
             for (auto networkMaterial : _materialsNeedingRewrite.values(textureKey)) {
                 networkMaterial->getTextureMap(baker->getMapChannel())->getTextureSource()->setUrl(relativeURL);
diff --git a/libraries/baking/src/MaterialBaker.h b/libraries/baking/src/MaterialBaker.h
index ab2a0a5901..7a7411142e 100644
--- a/libraries/baking/src/MaterialBaker.h
+++ b/libraries/baking/src/MaterialBaker.h
@@ -24,7 +24,7 @@ static const QString BAKED_MATERIAL_EXTENSION = ".baked.json";
 class MaterialBaker : public Baker {
     Q_OBJECT
 public:
-    MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir);
+    MaterialBaker(const QString& materialData, bool isURL, const QString& bakedOutputDir, QUrl destinationPath = QUrl());
 
     QString getMaterialData() const { return _materialData; }
     bool isURL() const { return _isURL; }
@@ -51,6 +51,7 @@ private:
 
     QString _materialData;
     bool _isURL;
+    QUrl _destinationPath;
 
     NetworkMaterialResourcePointer _materialResource;
 
diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp
index 50a3d212c0..7c312aff98 100644
--- a/tools/oven/src/DomainBaker.cpp
+++ b/tools/oven/src/DomainBaker.cpp
@@ -258,7 +258,7 @@ void DomainBaker::addScriptBaker(const QString& property, const QString& url, co
     _entitiesNeedingRewrite.insert(scriptURL, { property, jsonRef });
 }
 
-void DomainBaker::addMaterialBaker(const QString& property, const QString& data, bool isURL, const QJsonValueRef& jsonRef) {
+void DomainBaker::addMaterialBaker(const QString& property, const QString& data, bool isURL, const QJsonValueRef& jsonRef, QUrl destinationPath) {
     // grab a clean version of the URL without a query or fragment
     QString materialData;
     if (isURL) {
@@ -272,7 +272,7 @@ void DomainBaker::addMaterialBaker(const QString& property, const QString& data,
 
         // setup a baker for this material
         QSharedPointer<MaterialBaker> materialBaker {
-            new MaterialBaker(data, isURL, _contentOutputPath),
+            new MaterialBaker(data, isURL, _contentOutputPath, destinationPath),
             &MaterialBaker::deleteLater
         };
 
@@ -412,13 +412,9 @@ void DomainBaker::enumerateEntities() {
             if (entity.contains(MATERIAL_URL_KEY)) {
                 addMaterialBaker(MATERIAL_URL_KEY, entity[MATERIAL_URL_KEY].toString(), true, *it);
             }
-            // FIXME: Disabled for now because relative texture URLs are not supported for embedded materials in material entities
-            //        We need to make texture URLs absolute in this particular case only, keeping in mind that FSTBaker also uses embedded materials
-            /*
             if (entity.contains(MATERIAL_DATA_KEY)) {
-                addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_DATA_KEY].toString(), false, *it);
+                addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_DATA_KEY].toString(), false, *it, _destinationPath);
             }
-            */
         }
     }
 
diff --git a/tools/oven/src/DomainBaker.h b/tools/oven/src/DomainBaker.h
index 81f5c345cd..e8102ec7e8 100644
--- a/tools/oven/src/DomainBaker.h
+++ b/tools/oven/src/DomainBaker.h
@@ -76,7 +76,7 @@ private:
     void addModelBaker(const QString& property, const QString& url, const QJsonValueRef& jsonRef);
     void addTextureBaker(const QString& property, const QString& url, image::TextureUsage::Type type, const QJsonValueRef& jsonRef);
     void addScriptBaker(const QString& property, const QString& url, const QJsonValueRef& jsonRef);
-    void addMaterialBaker(const QString& property, const QString& data, bool isURL, const QJsonValueRef& jsonRef);
+    void addMaterialBaker(const QString& property, const QString& data, bool isURL, const QJsonValueRef& jsonRef, QUrl destinationPath = QUrl());
 };
 
 #endif // hifi_DomainBaker_h

From 2d5cfbf4f50b8a65713f23498124ee952cf61923 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@alumni.brown.edu>
Date: Tue, 16 Apr 2019 15:16:26 -0700
Subject: [PATCH 15/23] fix oven crash

---
 libraries/baking/src/FBXBaker.cpp    | 7 ++++++-
 libraries/render-utils/src/Model.cpp | 8 ++++++--
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp
index b7eb56c921..01897ee5e9 100644
--- a/libraries/baking/src/FBXBaker.cpp
+++ b/libraries/baking/src/FBXBaker.cpp
@@ -104,13 +104,15 @@ void FBXBaker::rewriteAndBakeSceneModels(const QVector<hfm::Mesh>& meshes, const
     int meshIndex = 0;
     for (FBXNode& rootChild : _rootNode.children) {
         if (rootChild.name == "Objects") {
-            for (auto object = rootChild.children.begin(); object != rootChild.children.end(); object++) {
+            auto object = rootChild.children.begin();
+            while (object != rootChild.children.end()) {
                 if (object->name == "Geometry") {
                     if (object->properties.at(2) == "Mesh") {
                         int meshNum = meshIndexToRuntimeOrder[meshIndex];
                         replaceMeshNodeWithDraco(*object, dracoMeshes[meshNum], dracoMaterialLists[meshNum]);
                         meshIndex++;
                     }
+                    object++;
                 } else if (object->name == "Model") {
                     for (FBXNode& modelChild : object->children) {
                         if (modelChild.name == "Properties60" || modelChild.name == "Properties70") {
@@ -136,9 +138,12 @@ void FBXBaker::rewriteAndBakeSceneModels(const QVector<hfm::Mesh>& meshes, const
                             meshIndex++;
                         }
                     }
+                    object++;
                 } else if (object->name == "Texture" || object->name == "Video") {
                     // this is an embedded texture, we need to remove it from the FBX
                     object = rootChild.children.erase(object);
+                } else {
+                    object++;
                 }
 
                 if (hasErrors()) {
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 2a35b0d161..e2d78a8d94 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -1572,9 +1572,13 @@ void Model::applyMaterialMapping() {
 
     auto& materialMapping = getMaterialMapping();
     for (auto& mapping : materialMapping) {
-        std::set<unsigned int> shapeIDs = getMeshIDsFromMaterialID(QString(mapping.first.c_str()));
         auto networkMaterialResource = mapping.second;
-        if (!networkMaterialResource || shapeIDs.size() == 0) {
+        if (!networkMaterialResource) {
+            continue;
+        }
+
+        std::set<unsigned int> shapeIDs = getMeshIDsFromMaterialID(QString(mapping.first.c_str()));
+        if (shapeIDs.size() == 0) {
             continue;
         }
 

From 104ed758b9f4fa32ca232e1247284af474ed8eb6 Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Wed, 17 Apr 2019 17:52:48 +0100
Subject: [PATCH 16/23] re-order nodes, cluster update

---
 libraries/fbx/src/GLTFSerializer.cpp | 140 +++++++++++----------------
 1 file changed, 57 insertions(+), 83 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 95d055481a..497d1f6ef0 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -753,107 +753,82 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>&
 
 bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     int numNodes = _file.nodes.size();
-    bool skinnedModel = !_file.skins.isEmpty();
 
-
-    //Build dependencies
-    QVector<QVector<int>> nodeDependencies(numNodes);
+    // Build dependencies
     QVector<int> parents;
-    QVector<int> nodesToSort = _file.scenes[_file.scene].nodes;
+    QVector<int> sortedNodes;
     parents.fill(-1, numNodes);
-    nodesToSort.reserve(numNodes);
+    sortedNodes.reserve(numNodes);
     int nodecount = 0;
     foreach(auto &node, _file.nodes) {
         foreach(int child, node.children) {
-            nodeDependencies[child].push_back(nodecount);
             parents[child] = nodecount;
         }
-        if (!nodesToSort.contains(nodecount)) {
-            nodesToSort.push_back(nodecount);
-        }
+        sortedNodes.push_back(nodecount);
         nodecount++;
     }
     
+
+    // Build transforms
     nodecount = 0;
     foreach(auto &node, _file.nodes) {
         // collect node transform
-        _file.nodes[nodecount].transforms.push_back(getModelTransform(node)); 
-        if (nodeDependencies[nodecount].size() == 1) {
-            int parentidx = nodeDependencies[nodecount][0];
-            while (true) { // iterate parents
-                // collect parents transforms
-                _file.nodes[nodecount].transforms.push_back(getModelTransform(_file.nodes[parentidx])); 
-                if (nodeDependencies[parentidx].size() == 1) {
-                    parentidx = nodeDependencies[parentidx][0];
-                } else break;
-            }
+        _file.nodes[nodecount].transforms.push_back(getModelTransform(node));
+        int parentIndex = parents[nodecount];
+        while (parentIndex != -1) {
+            const auto& parentNode = _file.nodes[parentIndex];
+            // collect transforms for a node's parents, grandparents, etc.
+            _file.nodes[nodecount].transforms.push_back(getModelTransform(parentNode));
+            parentIndex = parents[parentIndex];
         }
         nodecount++;
     }
 
  
-    // initialize order in which nodes will be parsed
-    std::vector<int> nodeQueue;
-    QVector<int> originalToNewNodeIndexMap;
+    // since parent indices must exist in the sorted list before any of their children, sortedNodes might not be initialized in the correct order
+    // therefore we need to re-initialize the order in which nodes will be parsed
     QVector<bool> hasBeenSorted;
-    originalToNewNodeIndexMap.fill(-1, numNodes);
     hasBeenSorted.fill(false, numNodes);
-    nodeQueue = _file.scenes[_file.scene].nodes.toStdVector();
-
-    for (int sceneNodeCount = 0; sceneNodeCount < _file.scenes[_file.scene].nodes.size(); sceneNodeCount++) {
-        int sceneNode = nodeQueue[sceneNodeCount];
-        originalToNewNodeIndexMap[sceneNode] = sceneNodeCount;
-        nodesToSort[nodesToSort.indexOf(sceneNode)] = nodesToSort.back();
-        nodesToSort.pop_back();
-        hasBeenSorted[sceneNode] = true;
-        for (int child : _file.nodes[sceneNode].children.toStdVector()) {
-            nodesToSort[nodesToSort.indexOf(child)] = nodesToSort.back();
-            nodesToSort.pop_back();
-        }
-
-        for (int child : _file.nodes[sceneNode].children) {
-            originalToNewNodeIndexMap[child] = nodeQueue.size();
-            nodeQueue.push_back(child);
-            hasBeenSorted[child] = true;
-
-            if (!_file.nodes[child].children.isEmpty() && nodeQueue.size() < numNodes) {
-                int newSize = nodesToSort.size();
-                while (!nodesToSort.isEmpty()) {
-                    int i = 0;
-
-                    while (i < nodesToSort.size()) {
-                        int nodeIndex = nodesToSort[i];
-                        int parentIndex = parents[nodeIndex];
-                        newSize = nodesToSort.size();
-
-                        if ((parentIndex == -1 || hasBeenSorted[parentIndex])) {
-                            originalToNewNodeIndexMap[nodeIndex] = nodeQueue.size();
-                            nodeQueue.push_back(nodeIndex);
-                            hasBeenSorted[nodeIndex] = true;
-                            // copy back and pop
-                            nodesToSort[i] = nodesToSort.back();
-                            nodesToSort.pop_back();
-                        } else {  // skip
-                            i++;
-                        }
-                    }
-                    // if the end of nodesToSort is reached without removing any nodes, break to move onto the next child
-                    if (newSize == nodesToSort.size() && i == nodesToSort.size()) {
-                        break;
-                    }
+    int i = 0;  // initial index
+    while (i < numNodes) {
+        int currentNode = sortedNodes[i];
+        int parentIndex = parents[currentNode];
+        if (parentIndex == -1 || hasBeenSorted[parentIndex]) {
+            hasBeenSorted[currentNode] = true;
+            i++;
+        } else {
+            int j = i + 1; // index of node to be sorted
+            while (j < numNodes) {
+                int nextNode = sortedNodes[j];
+                parentIndex = parents[nextNode];
+                if (parentIndex == -1 || hasBeenSorted[parentIndex]) {
+                    // swap with currentNode
+                    hasBeenSorted[nextNode] = true;
+                    sortedNodes[i] = nextNode;
+                    sortedNodes[j] = currentNode;
+                    i++;
+                    break;
                 }
+                j++;
             }
         }
     }
 
 
+     // Build map from original to new indices
+    QVector<int> originalToNewNodeIndexMap;
+    originalToNewNodeIndexMap.fill(-1, numNodes);
+    for (int i = 0; i < numNodes; i++) {
+        originalToNewNodeIndexMap[sortedNodes[i]] = i;
+    }
+
+
     // Build joints
     HFMJoint joint;
     joint.distanceToParent = 0;
     hfmModel.jointIndices["x"] = numNodes;
-    hfmModel.hasSkeletonJoints = false;
 
-    for (int nodeIndex : nodeQueue) {
+    for (int nodeIndex : sortedNodes) {
         auto& node = _file.nodes[nodeIndex];
 
         joint.parentIndex = -1;
@@ -873,22 +848,24 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
 
 
     // Build skeleton
+    hfmModel.hasSkeletonJoints = false;
     std::vector<glm::mat4> jointInverseBindTransforms;
     jointInverseBindTransforms.resize(numNodes);
     if (!_file.skins.isEmpty()) {
+        hfmModel.hasSkeletonJoints = true;
         std::vector<std::vector<float>> inverseBindValues;
         getSkinInverseBindMatrices(inverseBindValues);
 
         for (int jointIndex = 0; jointIndex < numNodes; jointIndex++) {
-            int nodeIndex = nodeQueue[jointIndex];
+            int nodeIndex = sortedNodes[jointIndex];
             auto joint = hfmModel.joints[jointIndex];
 
-            hfmModel.hasSkeletonJoints = true;
             for (int s = 0; s < _file.skins.size(); s++) {
                 const auto& skin = _file.skins[s];
                 int matrixIndex = skin.joints.indexOf(nodeIndex);
                 joint.isSkeletonJoint = skin.joints.contains(nodeIndex);
 
+                // build inverse bind matrices
                 if (joint.isSkeletonJoint) {
                     std::vector<float>& value = inverseBindValues[s];
                     int matrixCount = 16 * matrixIndex;
@@ -908,7 +885,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     }
 
 
-    //Build materials
+    // Build materials
     QVector<QString> materialIDs;
     QString unknown = "Default";
     int ukcount = 0;
@@ -928,7 +905,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     
     // Build meshes
     nodecount = 0;
-    for (int nodeIndex : nodeQueue) {
+    for (int nodeIndex : sortedNodes) {
         auto& node = _file.nodes[nodeIndex];
 
         if (node.defined["mesh"]) {
@@ -936,14 +913,14 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
             foreach(auto &primitive, _file.meshes[node.mesh].primitives) {
                 hfmModel.meshes.append(HFMMesh());
                 HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1];
-                if (!hfmModel.hasSkeletonJoints) {
+                if (!hfmModel.hasSkeletonJoints) { 
                     HFMCluster cluster;
                     cluster.jointIndex = nodecount;
                     cluster.inverseBindMatrix = glm::mat4();
                     cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
                     mesh.clusters.append(cluster);
-                } else {
-                    for (int j : nodeQueue) {
+                } else { // skinned model
+                    for (int j = 0; j < numNodes; j++) {
                         HFMCluster cluster;
                         cluster.jointIndex = j;
                         cluster.inverseBindMatrix = jointInverseBindTransforms[j];
@@ -952,10 +929,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                     }
                 }
                 HFMCluster root; 
-                root.jointIndex = nodeQueue[0];
-                if (root.jointIndex == -1) { 
-                    root.jointIndex = 0; 
-                }
+                root.jointIndex = sortedNodes[0];
                 root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex];
                 root.inverseBindTransform = Transform(root.inverseBindMatrix);
                 mesh.clusters.append(root);
@@ -1055,6 +1029,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                             qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url;
                             continue;
                         }
+                        // tangents can be a vec3 or a vec4 which includes a w component (of -1 or 1)
                         int stride = (accessor.type == GLTFAccessorType::VEC4) ? 4 : 3;
                         for (int n = 0; n < tangents.size() - 3; n += stride) {
                             float tanW = stride == 4 ? tangents[n + 3] : 1; 
@@ -1123,7 +1098,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                     }
                 }
             
-                // adapted from FBXSerializer.cpp
+                // Build weights (adapted from FBXSerializer.cpp)
                 if (hfmModel.hasSkeletonJoints) {
                     int numClusterIndices = clusterJoints.size();
                     const int WEIGHTS_PER_VERTEX = 4;
@@ -1133,7 +1108,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                     mesh.clusterWeights.fill(0, numClusterIndices);
 
                     for (int c = 0; c < clusterJoints.size(); c++) {
-                        mesh.clusterIndices[c] = _file.skins[node.skin].joints[clusterJoints[c]];
+                        mesh.clusterIndices[c] = originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[c]]];
                     }
 
                     // normalize and compress to 16-bits
@@ -1172,7 +1147,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                
                 mesh.meshIndex = hfmModel.meshes.size();
             }
-            
         }
         nodecount++;
     }

From 2fab5a0945cdce019171495200b580751fa55d8a Mon Sep 17 00:00:00 2001
From: raveenajain <jain.rav@husky.neu.edu>
Date: Wed, 17 Apr 2019 21:14:44 +0100
Subject: [PATCH 17/23] parents, skeleton, break fix

---
 libraries/fbx/src/GLTFSerializer.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp
index 497d1f6ef0..a4d18fb111 100755
--- a/libraries/fbx/src/GLTFSerializer.cpp
+++ b/libraries/fbx/src/GLTFSerializer.cpp
@@ -807,7 +807,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                     sortedNodes[i] = nextNode;
                     sortedNodes[j] = currentNode;
                     i++;
-                    break;
+                    currentNode = sortedNodes[i];
                 }
                 j++;
             }
@@ -831,9 +831,9 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
     for (int nodeIndex : sortedNodes) {
         auto& node = _file.nodes[nodeIndex];
 
-        joint.parentIndex = -1;
-        if (!_file.scenes[_file.scene].nodes.contains(nodeIndex)) {
-            joint.parentIndex = originalToNewNodeIndexMap[parents[nodeIndex]];
+        joint.parentIndex = parents[nodeIndex];
+        if (joint.parentIndex != -1) {
+            joint.parentIndex = originalToNewNodeIndexMap[joint.parentIndex];
         }
         joint.transform = node.transforms.first();
         joint.translation = extractTranslation(joint.transform);
@@ -848,10 +848,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
 
 
     // Build skeleton
-    hfmModel.hasSkeletonJoints = false;
     std::vector<glm::mat4> jointInverseBindTransforms;
     jointInverseBindTransforms.resize(numNodes);
-    if (!_file.skins.isEmpty()) {
+    hfmModel.hasSkeletonJoints = !_file.skins.isEmpty();
+    if (hfmModel.hasSkeletonJoints) {
         hfmModel.hasSkeletonJoints = true;
         std::vector<std::vector<float>> inverseBindValues;
         getSkinInverseBindMatrices(inverseBindValues);
@@ -929,7 +929,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
                     }
                 }
                 HFMCluster root; 
-                root.jointIndex = sortedNodes[0];
+                root.jointIndex = 0;
                 root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex];
                 root.inverseBindTransform = Transform(root.inverseBindMatrix);
                 mesh.clusters.append(root);

From b711ee8b8a760818d72c034191193091e2538839 Mon Sep 17 00:00:00 2001
From: sabrina-shanman <sabrina@highfidelity.io>
Date: Wed, 27 Mar 2019 15:31:22 -0700
Subject: [PATCH 18/23] Fix MaterialBaker not including names in baked
 multi-materials

---
 libraries/graphics/src/graphics/Material.h                       | 1 +
 .../src/material-networking/MaterialCache.cpp                    | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h
index d24e906f98..80b247bed0 100755
--- a/libraries/graphics/src/graphics/Material.h
+++ b/libraries/graphics/src/graphics/Material.h
@@ -318,6 +318,7 @@ public:
     void setTextureTransforms(const Transform& transform, MaterialMappingMode mode, bool repeat);
 
     const std::string& getName() const { return _name; }
+    void setName(const std::string& name) { _name = name; }
 
     const std::string& getModel() const { return _model; }
     void setModel(const std::string& model) { _model = model; }
diff --git a/libraries/material-networking/src/material-networking/MaterialCache.cpp b/libraries/material-networking/src/material-networking/MaterialCache.cpp
index 9eef89d5c9..5a5f4ab54b 100644
--- a/libraries/material-networking/src/material-networking/MaterialCache.cpp
+++ b/libraries/material-networking/src/material-networking/MaterialCache.cpp
@@ -184,6 +184,7 @@ std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource
                 auto nameJSON = materialJSON.value(key);
                 if (nameJSON.isString()) {
                     name = nameJSON.toString().toStdString();
+                    material->setName(name);
                 }
             } else if (key == "model") {
                 auto modelJSON = materialJSON.value(key);

From 343b853b598c4d11b4ab6dbffc68b392867f42d5 Mon Sep 17 00:00:00 2001
From: sabrina-shanman <sabrina@highfidelity.io>
Date: Tue, 16 Apr 2019 15:48:42 -0700
Subject: [PATCH 19/23] Fix Interface not loading some baked models correctly

---
 libraries/fbx/src/FBXSerializer_Mesh.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp
index c34b4678c7..3da08ade7c 100644
--- a/libraries/fbx/src/FBXSerializer_Mesh.cpp
+++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp
@@ -358,7 +358,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me
             std::vector<QString> dracoMaterialList;
             for (const auto& dracoChild : child.children) {
                 if (dracoChild.name == "FBXDracoMeshVersion") {
-                    if (!dracoChild.children.isEmpty()) {
+                    if (!dracoChild.properties.isEmpty()) {
                         dracoMeshNodeVersion = dracoChild.properties[0].toUInt();
                     }
                 } else if (dracoChild.name == "MaterialList") {

From 53f74abd4928d77f1828c7d0b2d2677aaaac67da Mon Sep 17 00:00:00 2001
From: sabrina-shanman <sabrina@highfidelity.io>
Date: Wed, 17 Apr 2019 11:33:53 -0700
Subject: [PATCH 20/23] Fix another Interface issue loading baked models

---
 libraries/fbx/src/FBXSerializer_Mesh.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp
index 3da08ade7c..22e6a0ddb2 100644
--- a/libraries/fbx/src/FBXSerializer_Mesh.cpp
+++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp
@@ -492,7 +492,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me
                     // Figure out what material this part is
                     if (dracoMeshNodeVersion >= 2) {
                         // Define the materialID now
-                        if (dracoMaterialList.size() - 1 <= materialID) {
+                        if (materialID <= dracoMaterialList.size() - 1) {
                             part.materialID = dracoMaterialList[materialID];
                         }
                     } else {

From 693cdabc2cf290478fd823913d5c283d1538d1ad Mon Sep 17 00:00:00 2001
From: sabrina-shanman <sabrina@highfidelity.io>
Date: Wed, 17 Apr 2019 13:26:19 -0700
Subject: [PATCH 21/23] Small style fix for materialID bounds check in
 FBXSerializer_Mesh.cpp

---
 libraries/fbx/src/FBXSerializer_Mesh.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/fbx/src/FBXSerializer_Mesh.cpp b/libraries/fbx/src/FBXSerializer_Mesh.cpp
index 22e6a0ddb2..802db4b428 100644
--- a/libraries/fbx/src/FBXSerializer_Mesh.cpp
+++ b/libraries/fbx/src/FBXSerializer_Mesh.cpp
@@ -492,7 +492,7 @@ ExtractedMesh FBXSerializer::extractMesh(const FBXNode& object, unsigned int& me
                     // Figure out what material this part is
                     if (dracoMeshNodeVersion >= 2) {
                         // Define the materialID now
-                        if (materialID <= dracoMaterialList.size() - 1) {
+                        if (materialID < dracoMaterialList.size()) {
                             part.materialID = dracoMaterialList[materialID];
                         }
                     } else {

From d80b40e5a57f0b281216723bb4dfd39f16723c40 Mon Sep 17 00:00:00 2001
From: sabrina-shanman <sabrina@highfidelity.io>
Date: Wed, 17 Apr 2019 15:11:52 -0700
Subject: [PATCH 22/23] Fix baked FSTs unable to be rebaked by creating an FST
 in the original output folder

---
 libraries/baking/src/ModelBaker.cpp | 36 +++++++++++++++++++++++++++++
 libraries/baking/src/ModelBaker.h   |  1 +
 libraries/fbx/src/FSTReader.h       |  1 +
 3 files changed, 38 insertions(+)

diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp
index e58ec00afa..00f7a38627 100644
--- a/libraries/baking/src/ModelBaker.cpp
+++ b/libraries/baking/src/ModelBaker.cpp
@@ -167,6 +167,10 @@ void ModelBaker::saveSourceModel() {
 
         connect(networkReply, &QNetworkReply::finished, this, &ModelBaker::handleModelNetworkReply);
     }
+
+    if (_mappingURL.isEmpty()) {
+        outputUnbakedFST();
+    }
 }
 
 void ModelBaker::handleModelNetworkReply() {
@@ -313,6 +317,37 @@ void ModelBaker::handleFinishedMaterialBaker() {
     outputBakedFST();
 }
 
+void ModelBaker::outputUnbakedFST() {
+    // Output an unbaked FST file in the original output folder to make it easier for FSTBaker to rebake this model
+    // TODO: Consider a more robust method that does not depend on FSTBaker navigating to a hardcoded relative path
+    QString outputFSTFilename = _modelURL.fileName();
+    auto extensionStart = outputFSTFilename.indexOf(".");
+    if (extensionStart != -1) {
+        outputFSTFilename.resize(extensionStart);
+    }
+    outputFSTFilename += FST_EXTENSION;
+    QString outputFSTURL = _originalOutputDir + "/" + outputFSTFilename;
+
+    hifi::VariantHash outputMapping;
+    outputMapping[FST_VERSION_FIELD] = FST_VERSION;
+    outputMapping[FILENAME_FIELD] = _modelURL.fileName();
+    outputMapping[COMMENT_FIELD] = "This FST file was generated by Oven for use during rebaking. It is not part of the original model. This file's existence is subject to change.";
+    hifi::ByteArray fstOut = FSTReader::writeMapping(outputMapping);
+
+    QFile fstOutputFile { outputFSTURL };
+    if (fstOutputFile.exists()) {
+        handleWarning("The file '" + outputFSTURL + "' already exists. Should that be baked instead of '" + _modelURL.toString() + "'?");
+        return;
+    }
+    if (!fstOutputFile.open(QIODevice::WriteOnly)) {
+        handleWarning("Failed to open file '" + outputFSTURL + "' for writing. Rebaking may fail on the associated model.");
+        return;
+    }
+    if (fstOutputFile.write(fstOut) == -1) {
+        handleWarning("Failed to write to file '" + outputFSTURL + "'. Rebaking may fail on the associated model.");
+    }
+}
+
 void ModelBaker::outputBakedFST() {
     // Output FST file, copying over input mappings if available
     QString outputFSTFilename = !_mappingURL.isEmpty() ? _mappingURL.fileName() : _modelURL.fileName();
@@ -327,6 +362,7 @@ void ModelBaker::outputBakedFST() {
     outputMapping[FST_VERSION_FIELD] = FST_VERSION;
     outputMapping[FILENAME_FIELD] = _bakedModelURL.fileName();
     outputMapping.remove(TEXDIR_FIELD);
+    outputMapping.remove(COMMENT_FIELD);
     hifi::ByteArray fstOut = FSTReader::writeMapping(outputMapping);
 
     QFile fstOutputFile { outputFSTURL };
diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h
index d612a0a43a..f280481803 100644
--- a/libraries/baking/src/ModelBaker.h
+++ b/libraries/baking/src/ModelBaker.h
@@ -82,6 +82,7 @@ protected slots:
     void handleFinishedMaterialBaker();
 
 private:
+    void outputUnbakedFST();
     void outputBakedFST();
 
     bool _hasBeenBaked { false };
diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h
index 2b13bf3078..3945fe1d8b 100644
--- a/libraries/fbx/src/FSTReader.h
+++ b/libraries/fbx/src/FSTReader.h
@@ -33,6 +33,7 @@ static const QString BLENDSHAPE_FIELD = "bs";
 static const QString SCRIPT_FIELD = "script";
 static const QString JOINT_NAME_MAPPING_FIELD = "jointMap";
 static const QString MATERIAL_MAPPING_FIELD = "materialMap";
+static const QString COMMENT_FIELD = "comment";
 
 class FSTReader {
 public:

From 7fa81ffef32c160ab7385e5cc574869e02baa8ec Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@alumni.brown.edu>
Date: Thu, 18 Apr 2019 12:48:46 -0700
Subject: [PATCH 23/23] fix warnings

---
 interface/src/Application.cpp | 3 ---
 interface/src/Menu.cpp        | 3 +--
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index d1add60647..7ba45da1fd 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -9320,6 +9320,3 @@ void Application::toggleAwayMode(){
 
 
 #endif
-
-
-#include "Application.moc"
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index e9aadea2b6..2c34ecd780 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -707,8 +707,7 @@ Menu::Menu() {
     // Developer > Timing >>>
     MenuWrapper* timingMenu = developerMenu->addMenu("Timing");
     MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
-    addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false,
-            qApp, SLOT(enablePerfStats(bool)));
+    addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails);
     addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
     addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
     addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandSimulationTiming, 0, false);