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);