From 5eff12af34f8a047afa58dfd088faa171fc4c837 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 6 Jan 2017 18:09:23 -0700 Subject: [PATCH 01/50] add hover effect to PAL --- scripts/system/pal.js | 55 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index ef16889035..6428749bee 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -19,9 +19,13 @@ const UNSELECTED_TEXTURES = {"idle-D": Script.resolvePath("./assets/models/Avata const SELECTED_TEXTURES = { "idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png"), "idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png") }; +const HOVER_TEXTURES = { "idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-hover.png"), + "idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-hover.png") +}; const UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6}; -const SELECTED_COLOR = {red: 0xf3, green: 0x91, blue: 0x29}; +const SELECTED_COLOR = {red: 0xF3, green: 0x91, blue: 0x29}; +const HOVER_COLOR = {red: 0xFF, green: 0xFF, blue: 0xFF}; // white for now (function() { // BEGIN LOCAL_SCOPE @@ -46,6 +50,7 @@ function ExtendedOverlay(key, type, properties, selected, hasModel) { // A wrapp } this.key = key; this.selected = selected || false; // not undefined + this.hovering = false; this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected... } // Instance methods: @@ -58,20 +63,38 @@ ExtendedOverlay.prototype.editOverlay = function (properties) { // change displa Overlays.editOverlay(this.activeOverlay, properties); }; -function color(selected) { - return selected ? SELECTED_COLOR : UNSELECTED_COLOR; +function color(selected, hovering) { + + return hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; } -function textures(selected) { - return selected ? SELECTED_TEXTURES : UNSELECTED_TEXTURES; +function textures(selected, hovering) { + return hovering ? HOVER_TEXTURES : selected ? SELECTED_TEXTURES : UNSELECTED_TEXTURES; +} +// so we don't have to traverse the overlays to get the last one +var lastHoveringId = 0; +ExtendedOverlay.prototype.hover = function (hovering) { + if (this.key === lastHoveringId) { + if (hovering) { + return; + } else { + lastHoveringId = 0; + } + } + this.editOverlay({color: color(this.selected, hovering)}); + if (this.model) { + this.model.editOverlay({textures: textures(this.selected, hovering)}); + } + if (hovering) { + lastHoveringId = this.key; + } } - ExtendedOverlay.prototype.select = function (selected) { if (this.selected === selected) { return; } - this.editOverlay({color: color(selected)}); + this.editOverlay({color: color(selected, this.hovering)}); if (this.model) { this.model.editOverlay({textures: textures(selected)}); } @@ -93,9 +116,12 @@ ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator } } }; -ExtendedOverlay.applyPickRay = function (pickRay, cb) { // cb(overlay) on the one overlay intersected by pickRay, if any. +ExtendedOverlay.applyPickRay = function (pickRay, cb, noHit) { // cb(overlay) on the one overlay intersected by pickRay, if any. var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. if (!pickedOverlay.intersects) { + if (noHit) { + return noHit(); + } return; } ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours. @@ -311,6 +337,7 @@ function updateOverlays() { } function removeOverlays() { selectedIds = []; + lastHoveringId = 0; HighlightedEntity.clearOverlays(); ExtendedOverlay.some(function (overlay) { overlay.deleteOverlay(); }); } @@ -332,6 +359,16 @@ function handleMouseEvent(mousePressEvent) { // handleClick if we get one. } handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y)); } +function handleMouseMoveEvent(event) { + // find out which overlay (if any) is over the mouse position + // var overlayAtPoint = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + var pickRay = Camera.computePickRay(event.x, event.y); + var hit = ExtendedOverlay.applyPickRay(pickRay, function (overlay) { + overlay.hover(true); + }, function () { + ExtendedOverlay.some(function (overlay) { overlay.hover(false); }); + }); +} // We get mouseMoveEvents from the handControllers, via handControllerPointer. // But we don't get mousePressEvents. var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); @@ -371,6 +408,7 @@ function off() { if (isWired) { // It is not ok to disconnect these twice, hence guard. Script.update.disconnect(updateOverlays); Controller.mousePressEvent.disconnect(handleMouseEvent); + Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); isWired = false; } triggerMapping.disable(); // It's ok if we disable twice. @@ -385,6 +423,7 @@ function onClicked() { isWired = true; Script.update.connect(updateOverlays); Controller.mousePressEvent.connect(handleMouseEvent); + Controller.mouseMoveEvent.connect(handleMouseMoveEvent); triggerMapping.enable(); } else { off(); From 32a0225f48088177aa58f6a5acc391ead827ee26 Mon Sep 17 00:00:00 2001 From: jmferrer Date: Sun, 8 Jan 2017 08:51:56 +0100 Subject: [PATCH 02/50] Fix: $ cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/Qt5.6.1/5.6/gcc_64/lib/cmake -- Performing Test COMPILER_SUPPORTS_CXX11 -- Performing Test COMPILER_SUPPORTS_CXX11 - Success -- Performing Test COMPILER_SUPPORTS_CXX0X -- Performing Test COMPILER_SUPPORTS_CXX0X - Success -- The BUILD_BRANCH variable is: -- The BRANCH environment variable is: -- The RELEASE_TYPE variable is: -- The BUILD_GLOBAL_SERVICES variable is: DEVELOPMENT -- The USE_STABLE_GLOBAL_SERVICES variable is: 0 -- Found GLM: /home/jmferrerm/trabajo/SL/hifi/build/ext/makefiles/glm/project/include -- Found ZLIB: /usr/lib/x86_64-linux-gnu/libz.so (found version "1.2.8") CMake Error at /usr/share/cmake-3.5/Modules/FindPackageHandleStandardArgs.cmake:148 (message): Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR (missing: OPENSSL_LIBRARIES OPENSSL_INCLUDE_DIR) Call Stack (most recent call first): /usr/share/cmake-3.5/Modules/FindPackageHandleStandardArgs.cmake:388 (_FPHSA_FAILURE_MESSAGE) cmake/modules/FindOpenSSL.cmake:217 (find_package_handle_standard_args) libraries/networking/CMakeLists.txt:15 (find_package) -- Configuring incomplete, errors occurred! See also "/home/jmferrerm/trabajo/SL/hifi/build/CMakeFiles/CMakeOutput.log". --- BUILD_LINUX.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD_LINUX.md b/BUILD_LINUX.md index 55cd647f66..34ba41894c 100644 --- a/BUILD_LINUX.md +++ b/BUILD_LINUX.md @@ -3,4 +3,4 @@ Please read the [general build guide](BUILD.md) for information on dependencies ###Qt5 Dependencies Should you choose not to install Qt5 via a package manager that handles dependencies for you, you may be missing some Qt5 dependencies. On Ubuntu, for example, the following additional packages are required: - libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev + libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev From 409e785aa3d4baeac8d7eb70d5ab5852aeffb77c Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 9 Jan 2017 08:42:43 -0700 Subject: [PATCH 03/50] fix un-hover, add some comments --- scripts/system/pal.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 6428749bee..72955bf3ab 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -86,6 +86,10 @@ ExtendedOverlay.prototype.hover = function (hovering) { this.model.editOverlay({textures: textures(this.selected, hovering)}); } if (hovering) { + // un-hover the last hovering overlay + if (lastHoveringId && lastHoveringId != this.key) { + ExtendedOverlay.get(lastHoveringId).hover(false); + } lastHoveringId = this.key; } } @@ -116,7 +120,10 @@ ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator } } }; -ExtendedOverlay.applyPickRay = function (pickRay, cb, noHit) { // cb(overlay) on the one overlay intersected by pickRay, if any. + +// hit(overlay) on the one overlay intersected by pickRay, if any. +// noHit() if no ExtendedOverlay was intersected (helps with hover) +ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { // cb(overlay) on the one overlay intersected by pickRay, if any. var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. if (!pickedOverlay.intersects) { if (noHit) { @@ -126,7 +133,7 @@ ExtendedOverlay.applyPickRay = function (pickRay, cb, noHit) { // cb(overlay) on } ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours. if ((overlay.activeOverlay) === pickedOverlay.overlayID) { - cb(overlay); + hit(overlay); return true; } }); @@ -363,7 +370,7 @@ function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position // var overlayAtPoint = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); var pickRay = Camera.computePickRay(event.x, event.y); - var hit = ExtendedOverlay.applyPickRay(pickRay, function (overlay) { + ExtendedOverlay.applyPickRay(pickRay, function (overlay) { overlay.hover(true); }, function () { ExtendedOverlay.some(function (overlay) { overlay.hover(false); }); From 5c6ae6fe95ca1a6d71799f139f8d7484173a8dad Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 10 Jan 2017 14:47:34 -0700 Subject: [PATCH 04/50] HMD hover working now Had to hack a bit -- the trigger mappings were not firing so instead I just get the hand that is triggering (when HMD is active) and handle the hover almost as before, with mouse move events. Some thought about how to map a 'mouse move' event into something where 2 triggers can both be pressed at same time was in order. --- scripts/system/pal.js | 71 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 6a9ac1195b..ef65fb0a88 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -120,6 +120,11 @@ ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator } } }; +ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId (if any) + if (lastHoveringId) { + ExtendedOverlay.get(lastHoveringId).hover(false); + } +}; // hit(overlay) on the one overlay intersected by pickRay, if any. // noHit() if no ExtendedOverlay was intersected (helps with hover) @@ -366,16 +371,70 @@ function handleMouseEvent(mousePressEvent) { // handleClick if we get one. } handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y)); } -function handleMouseMoveEvent(event) { - // find out which overlay (if any) is over the mouse position - // var overlayAtPoint = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - var pickRay = Camera.computePickRay(event.x, event.y); +function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position + if (HMD.active) { + var hand = whichHand(); + if (hand != 0) { + print("pick ray for " + hand); + pickRay = controllerComputePickRay(hand); + } else { + // nothing should hover, so + ExtendedOverlay.unHover(); + return; + } + } else { + pickRay = Camera.computePickRay(event.x, event.y); + } + handleMouseMove(pickRay); +} +const TRIGGER_THRESHOLD = 0.85; +// for some reason, I could not get trigger mappings for the LT and RT to fire. So, since we can read the +// value of the RT and LT, and the messages come through as mouse moves, we have the hack below to figure out +// which hand is being triggered, and compute the pick ray accordingly. +// +function isPressed(hand) { // helper to see if the hand passed in has the trigger pulled + var controller = (hand === Controller.Standard.RightHand ? Controller.Standard.RT : Controller.Standard.LT); + return Controller.getValue(controller) > TRIGGER_THRESHOLD; +} +// helpful globals +var currentHandPressed = 0; +var hands = [Controller.Standard.RightHand, Controller.Standard.LeftHand]; + +function whichHand() { + // for HMD, decide which hand is the "mouse". The 'logic' is to use the first one that + // is triggered, until that one is released. There are interesting effects when both + // are held that this avoids. Mapping the 2 triggers and their lasers to one mouse move + // message is a bit problematic, this mitigates some of it. + if (HMD.active) { + var pressed = {}; + for (var i = 0; i 0.85) { + if (clicked > TRIGGER_THRESHOLD) { var pickRay = controllerComputePickRay(hand); handleClick(pickRay); } From 2d55ed265807eafa43a7d23cd34957ba848f90af Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 10 Jan 2017 15:51:24 -0700 Subject: [PATCH 05/50] Fix hover - probably my merge didn't get this line --- scripts/system/pal.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 05d8194c08..9104e96350 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -78,6 +78,7 @@ function textures(selected, hovering) { // so we don't have to traverse the overlays to get the last one var lastHoveringId = 0; ExtendedOverlay.prototype.hover = function (hovering) { + this.hovering = hovering; if (this.key === lastHoveringId) { if (hovering) { return; From 5fd11ade9cfbe9445480b947d2c4c16fd3627281 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 10 Jan 2017 16:00:19 -0700 Subject: [PATCH 06/50] minor tweak to hover color for glow effect --- scripts/system/pal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 9104e96350..d385b5331e 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -25,7 +25,7 @@ const HOVER_TEXTURES = { "idle-D": Script.resolvePath("./assets/models/Avatar-Ov const UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6}; const SELECTED_COLOR = {red: 0xF3, green: 0x91, blue: 0x29}; -const HOVER_COLOR = {red: 0xFF, green: 0xFF, blue: 0xFF}; // white for now +const HOVER_COLOR = {red: 0xD0, green: 0xD0, blue: 0xD0}; // almost white for now (function() { // BEGIN LOCAL_SCOPE From 4fade3a53eb07db6d2b7d980c832b99194ea606d Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 10 Jan 2017 16:12:02 -0700 Subject: [PATCH 07/50] CR feedback --- scripts/system/pal.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index d385b5331e..d45e1fc7bc 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -133,7 +133,7 @@ ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId // hit(overlay) on the one overlay intersected by pickRay, if any. // noHit() if no ExtendedOverlay was intersected (helps with hover) -ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { // cb(overlay) on the one overlay intersected by pickRay, if any. +ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. if (!pickedOverlay.intersects) { if (noHit) { @@ -377,6 +377,13 @@ function handleMouseEvent(mousePressEvent) { // handleClick if we get one. } handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y)); } +function handleMouseMove(pickRay) { // given the pickRay, just do the hover logic + ExtendedOverlay.applyPickRay(pickRay, function (overlay) { + overlay.hover(true); + }, function () { + ExtendedOverlay.unHover(); + }); +} function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position if (HMD.active) { var hand = whichHand(); @@ -433,14 +440,6 @@ function whichHand() { return currentHandPressed; } -function handleMouseMove(pickRay) { // given the pickRay, just do the hover logic - ExtendedOverlay.applyPickRay(pickRay, function (overlay) { - overlay.hover(true); - }, function () { - ExtendedOverlay.unHover(); - }); -} - // We get mouseMoveEvents from the handControllers, via handControllerPointer. // But we don't get mousePressEvents. var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); From 44699fc661e397ebe40ed68c2cefc08871c38e69 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 10 Jan 2017 16:21:13 -0800 Subject: [PATCH 08/50] modelToWorld rotation not in cluster matrices --- .../render-utils/src/MeshPartPayload.cpp | 26 +++++++------------ libraries/render-utils/src/MeshPartPayload.h | 7 +++-- libraries/render-utils/src/Model.cpp | 10 +++---- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 037d58a67b..66f4f577b7 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -63,8 +63,7 @@ void MeshPartPayload::updateMeshPart(const std::shared_ptr& d void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) { _transform = transform; - _offsetTransform = offsetTransform; - Transform::mult(_drawTransform, _transform, _offsetTransform); + Transform::mult(_drawTransform, _transform, offsetTransform); _worldBound = _localBound; _worldBound.transform(_drawTransform); } @@ -360,8 +359,8 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector& clusterMatrices) { - ModelMeshPartPayload::updateTransform(transform, offsetTransform); +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices) { + _transform = transform; if (clusterMatrices.size() > 0) { _worldBound = AABox(); @@ -371,8 +370,10 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf _worldBound += clusterBound; } - // clusterMatrix has world rotation but not world translation. - _worldBound.translate(transform.getTranslation()); + _worldBound.transform(transform); + if (clusterMatrices.size() == 1) { + _transform.worldTransform(Transform(clusterMatrices[0])); + } } } @@ -520,23 +521,15 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: // Still relying on the raw data from the model const Model::MeshState& state = _model->_meshStates.at(_meshIndex); - Transform transform; if (state.clusterBuffer) { if (canCauterize && _model->getCauterizeBones()) { batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.cauterizedClusterBuffer); } else { batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); } - } else { - if (canCauterize && _model->getCauterizeBones()) { - transform = Transform(state.cauterizedClusterMatrices[0]); - } else { - transform = Transform(state.clusterMatrices[0]); - } } - transform.preTranslate(_transform.getTranslation()); - batch.setModelTransform(transform); + batch.setModelTransform(_transform); } void ModelMeshPartPayload::startFade() { @@ -570,7 +563,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } - // When an individual mesh parts like this finishes its fade, we will mark the Model as + // When an individual mesh parts like this finishes its fade, we will mark the Model as // having render items that need updating bool nextIsFading = _isFading ? isStillFading() : false; bool startFading = !_isFading && !_hasFinishedFade && _hasStartedFade; @@ -592,6 +585,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // Bind the model transform and the skinCLusterMatrices if needed bool canCauterize = args->_renderMode != RenderArgs::SHADOW_RENDER_MODE; + // TODO: maybe get rid of this call? _model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation()); bindTransform(batch, locations, canCauterize); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 3b882a9bb9..a09bb8626d 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -56,13 +56,12 @@ public: model::Mesh::Part _drawPart; std::shared_ptr _drawMaterial; - + model::Box _localBound; Transform _drawTransform; Transform _transform; - Transform _offsetTransform; mutable model::Box _worldBound; - + bool _hasColorAttrib = false; size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } @@ -86,7 +85,7 @@ public: typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; - void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector& clusterMatrices); + void updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices); // Entity fade in void startFade(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ffbe1fb34c..bc74a073ab 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -245,7 +245,7 @@ void Model::updateRenderItems() { // update the model transform and bounding box for this render item. const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, state.clusterMatrices); + data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices); } } }); @@ -1167,7 +1167,6 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig->getJointTransform(geometry.neckJointIndex) * zeroScale; - glm::mat4 modelToWorld = glm::mat4_cast(modelOrientation); for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -1176,11 +1175,10 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); #if GLM_ARCH & GLM_ARCH_SSE2 glm::mat4 temp, out, inverseBindMatrix = cluster.inverseBindMatrix; - glm_mat4_mul((glm_vec4*)&modelToWorld, (glm_vec4*)&jointMatrix, (glm_vec4*)&temp); glm_mat4_mul((glm_vec4*)&temp, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); state.clusterMatrices[j] = out; -#else - state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; +#else + state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; #endif // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. @@ -1188,7 +1186,7 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointMatrix = cauterizeMatrix; } - state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; + state.cauterizedClusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; } } From 0a018e133dfcf63e80a54da41474165dd024d38d Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 10 Jan 2017 18:23:39 -0700 Subject: [PATCH 09/50] Blue laser should be enough to hover --- scripts/system/pal.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index d45e1fc7bc..ef3e37101a 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -400,14 +400,15 @@ function handleMouseMoveEvent(event) { // find out which overlay (if any) is ove } handleMouseMove(pickRay); } -const TRIGGER_THRESHOLD = 0.85; +const TRIGGER_CLICK_THRESHOLD = 0.85; +const TRIGGER_PRESS_THRESHOLD = 0.05; // for some reason, I could not get trigger mappings for the LT and RT to fire. So, since we can read the // value of the RT and LT, and the messages come through as mouse moves, we have the hack below to figure out // which hand is being triggered, and compute the pick ray accordingly. // function isPressed(hand) { // helper to see if the hand passed in has the trigger pulled var controller = (hand === Controller.Standard.RightHand ? Controller.Standard.RT : Controller.Standard.LT); - return Controller.getValue(controller) > TRIGGER_THRESHOLD; + return Controller.getValue(controller) > TRIGGER_PRESS_THRESHOLD; } // helpful globals var currentHandPressed = 0; @@ -451,7 +452,7 @@ function controllerComputePickRay(hand) { } function makeClickHandler(hand) { return function (clicked) { - if (clicked > TRIGGER_THRESHOLD) { + if (clicked > TRIGGER_CLICK_THRESHOLD) { var pickRay = controllerComputePickRay(hand); handleClick(pickRay); } From 530b176d09b955f46cb85eebf76dad7e8a6c0630 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Jan 2017 16:36:18 +1300 Subject: [PATCH 10/50] Replace Clara download button with own --- scripts/system/html/js/marketplacesInject.js | 52 +++++--------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 8dad85a66d..0fbb7d8bbb 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -111,61 +111,35 @@ element.setAttribute("href", href + parameters); } - // Replace download options with a single, "Download to High Fidelity" option. + // Remove unwanted buttons and replace download options with a single "Download to High Fidelity" button. var buttons = $("a.embed-button").parent("div"); - if (buttons.length > 0) { - var downloadFBX = buttons.find("a[data-extension=\'fbx\']")[0]; - downloadFBX.addEventListener("click", startAutoDownload); - var firstButton = buttons.children(":first-child")[0]; - buttons[0].insertBefore(downloadFBX, firstButton); - downloadFBX.setAttribute("class", "btn btn-primary download"); - downloadFBX.innerHTML = " Download to High Fidelity"; - buttons.children(":nth-child(2), .btn-group , .embed-button").each(function () { this.remove(); }); + var downloadFBX; + if (buttons.find("div.btn-group").length > 0) { + buttons.children(".btn-primary, .btn-group , .embed-button").each(function () { this.remove(); }); + if ($("#hifi-download-container").length === 0) { // Button hasn't been moved already. + downloadFBX = $(' Download to High Fidelity'); + buttons.prepend(downloadFBX); + downloadFBX[0].addEventListener("click", startAutoDownload); + } } // Move the "Download to High Fidelity" button to be more visible on tablet. if ($("#hifi-download-container").length === 0 && window.innerWidth < 700) { - // Moving the button stops the Clara.io download from starting so instead, make a visual copy in the right place - // and wire its click event to click the original button. var downloadContainer = $('
'); $(".top-title .col-sm-4").append(downloadContainer); - var downloadButton = $("a[data-extension=\'fbx\']").clone(); - downloadButton[0].addEventListener("click", function () { downloadFBX.click(); }); - downloadContainer.append(downloadButton); - downloadFBX.style.visibility = "hidden"; + downloadContainer.append(downloadFBX); } // Automatic download to High Fidelity. - var downloadTimer; - function startAutoDownload(event) { + function startAutoDownload() { if (!canWriteAssets) { console.log("Clara.io FBX file download cancelled because no permissions to write to Asset Server"); EventBridge.emitWebEvent(WARN_USER_NO_PERMISSIONS); event.stopPropagation(); + return; } - window.scrollTo(0, 0); // Scroll to top ready for history.back(). - if (!downloadTimer) { - downloadTimer = setInterval(autoDownload, 1000); - } - } - function autoDownload() { - if ($("div.download-body").length !== 0) { - var downloadButton = $("div.download-body a.download-file"); - if (downloadButton.length > 0) { - clearInterval(downloadTimer); - downloadTimer = null; - var href = downloadButton[0].href; - EventBridge.emitWebEvent(CLARA_IO_DOWNLOAD + " " + href); - console.log("Clara.io FBX file download initiated for " + href); - $("a.btn.cancel").click(); - history.back(); // Remove history item created by clicking "download". - }; - } else if ($("div#view-signup_login_dialog").length === 0) { - // Don't stop checking for button if user is asked to log in. - clearInterval(downloadTimer); - downloadTimer = null; - } + // TODO: Initiate download using Clara.io API. } } } From 71d6a5cb1d77dddfd3aa60488e30d592aa687515 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Jan 2017 21:33:03 +1300 Subject: [PATCH 11/50] Implement model request and download --- scripts/system/html/js/marketplacesInject.js | 59 +++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 0fbb7d8bbb..2db250c52a 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -133,13 +133,68 @@ // Automatic download to High Fidelity. function startAutoDownload() { if (!canWriteAssets) { - console.log("Clara.io FBX file download cancelled because no permissions to write to Asset Server"); + console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server"); EventBridge.emitWebEvent(WARN_USER_NO_PERMISSIONS); event.stopPropagation(); return; } - // TODO: Initiate download using Clara.io API. + // Obtain zip file to download for requested asset. + // Reference: https://clara.io/learn/sdk/api/export + var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true¢erScene=true&alignSceneGound=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; + var uuid = location.href.match(/\/view\/([a-z0-9\-]*)/)[1]; + var url = XMLHTTPREQUEST_URL.replace("{uuid}", uuid); + + var xmlHttpRequest = new XMLHttpRequest(); + var responseTextIndex = 0; + var statusMessage = ""; + var zipFileURL = ""; + + xmlHttpRequest.onreadystatechange = function () { + // Messages are appended to responseText; process the new one. + var message = this.responseText.slice(responseTextIndex); + + if (message.slice(0, 5) === "data:") { + var data = JSON.parse(message.slice(5)); + + // Extract status message. + if (data.hasOwnProperty("message") && data.message !== null) { + statusMessage = data.message; + console.log("Clara.io FBX: " + statusMessage); + } + + // Extract zip file URL. + if (data.hasOwnProperty("files") && data.files.length > 0) { + zipFileURL = data.files[0].url; + } + } + + responseTextIndex = this.responseText.length; + }; + + // Note: onprogress doesn't have computable total length. + + xmlHttpRequest.onload = function () { + var HTTP_OK = 200; + if (this.status !== HTTP_OK) { + console.log("ERROR: Clara.io FBX: Zip file request terminated with " + this.status + " " + + this.statusText); + return; + } + + if (zipFileURL == "") { + console.log("ERROR: Clara.io FBX: Zip file URL not found"); + return; + } + + EventBridge.emitWebEvent(CLARA_IO_DOWNLOAD + " " + zipFileURL); + console.log("Clara.io FBX: File download initiated for " + zipFileURL); + } + + console.log("Clara.io FBX: Request zip file for " + uuid); + xmlHttpRequest.open("POST", url, true); + xmlHttpRequest.setRequestHeader("Accept", "text/event-stream"); + xmlHttpRequest.send(); } } } From 7a01b856f7dd62430c044a0253d0b7b17a746ceb Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 11 Jan 2017 02:33:56 -0800 Subject: [PATCH 12/50] Factorization of the modelTransform out of the cluster matrices --- libraries/render-utils/src/MeshPartPayload.cpp | 4 ++-- libraries/render-utils/src/Model.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 66f4f577b7..bba658c75c 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -372,7 +372,7 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf _worldBound.transform(transform); if (clusterMatrices.size() == 1) { - _transform.worldTransform(Transform(clusterMatrices[0])); + _transform = _transform.worldTransform(Transform(clusterMatrices[0])); } } } @@ -586,7 +586,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // Bind the model transform and the skinCLusterMatrices if needed bool canCauterize = args->_renderMode != RenderArgs::SHADOW_RENDER_MODE; // TODO: maybe get rid of this call? - _model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation()); + // _model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation()); bindTransform(batch, locations, canCauterize); //Bind the index buffer and vertex buffer and Blend shapes if needed diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index bc74a073ab..b7e0492413 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1174,8 +1174,8 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); #if GLM_ARCH & GLM_ARCH_SSE2 - glm::mat4 temp, out, inverseBindMatrix = cluster.inverseBindMatrix; - glm_mat4_mul((glm_vec4*)&temp, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); + glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix; + glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); state.clusterMatrices[j] = out; #else state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; From e88f02d7b6c7931700b0400e7be7fcbbf3838253 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 11 Jan 2017 06:55:13 -0800 Subject: [PATCH 13/50] remove cruft --- libraries/render-utils/src/MeshPartPayload.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index bba658c75c..650b8934ee 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -562,7 +562,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { return; } - // When an individual mesh parts like this finishes its fade, we will mark the Model as // having render items that need updating bool nextIsFading = _isFading ? isStillFading() : false; @@ -585,8 +584,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // Bind the model transform and the skinCLusterMatrices if needed bool canCauterize = args->_renderMode != RenderArgs::SHADOW_RENDER_MODE; - // TODO: maybe get rid of this call? - // _model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation()); bindTransform(batch, locations, canCauterize); //Bind the index buffer and vertex buffer and Blend shapes if needed From 5d880487a031c56788a318e6285b60b9376669a7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 11 Jan 2017 06:56:07 -0800 Subject: [PATCH 14/50] cleanup class method declarations --- libraries/render-utils/src/MeshPartPayload.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index a09bb8626d..b048dc903f 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -34,7 +34,7 @@ public: virtual void updateMeshPart(const std::shared_ptr& drawMesh, int partIndex); virtual void notifyLocationChanged() {} - virtual void updateTransform(const Transform& transform, const Transform& offsetTransform); + void updateTransform(const Transform& transform, const Transform& offsetTransform); virtual void updateMaterial(model::MaterialPointer drawMaterial); @@ -100,7 +100,7 @@ public: // ModelMeshPartPayload functions to perform render void bindMesh(gpu::Batch& batch) const override; - void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool canCauterize) const override; + void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool canCauterize = true) const override; void initCache(); From 1804f33b98f4d52e14882b245c4aae51f0a6b71f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 11 Jan 2017 06:56:35 -0800 Subject: [PATCH 15/50] add assert and TODO comment --- libraries/render-utils/src/Model.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b7e0492413..b7848bc717 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -295,6 +295,7 @@ bool Model::updateGeometry() { if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { initJointStates(); + assert(_meshStates.empty()); const FBXGeometry& fbxGeometry = getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { @@ -304,6 +305,9 @@ bool Model::updateGeometry() { _meshStates.append(state); + // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index + // later in ModelMeshPayload, however the vast majority of meshes will not have them. + // TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes? auto buffer = std::make_shared(); if (!mesh.blendshapes.isEmpty()) { buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); From bd16824d55a574bd2182db913964e07d24168b8f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 11 Jan 2017 06:57:05 -0800 Subject: [PATCH 16/50] remove cruft: Model::_pupilDilation --- libraries/render-utils/src/Model.cpp | 1 - libraries/render-utils/src/Model.h | 4 ---- 2 files changed, 5 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b7848bc717..e0a378176f 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -92,7 +92,6 @@ Model::Model(RigPointer rig, QObject* parent) : _snapModelToRegistrationPoint(false), _snappedToRegistrationPoint(false), _cauterizeBones(false), - _pupilDilation(0.0f), _url(HTTP_INVALID_COM), _isVisible(true), _blendNumber(0), diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index d4387a6a9a..92b718d487 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -251,9 +251,6 @@ signals: protected: bool addedToScene() const { return _addedToScene; } - void setPupilDilation(float dilation) { _pupilDilation = dilation; } - float getPupilDilation() const { return _pupilDilation; } - void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } @@ -347,7 +344,6 @@ protected: void deleteGeometry(); void initJointTransforms(); - float _pupilDilation; QVector _blendshapeCoefficients; QUrl _url; From 9bdcdf73c16d0ea628395a811e9ffe402e0fdc8e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 12 Jan 2017 09:15:01 +1300 Subject: [PATCH 17/50] User must be logged into Clara.io site to download --- scripts/system/html/js/marketplacesInject.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 2db250c52a..c5d463875f 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -132,6 +132,8 @@ // Automatic download to High Fidelity. function startAutoDownload() { + + // User must be able to write to Asset Server. if (!canWriteAssets) { console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server"); EventBridge.emitWebEvent(WARN_USER_NO_PERMISSIONS); @@ -139,6 +141,13 @@ return; } + // User must be logged in. + var loginButton = $("#topnav a[href='/signup']"); + if (loginButton.length > 0) { + loginButton[0].click(); + return; + } + // Obtain zip file to download for requested asset. // Reference: https://clara.io/learn/sdk/api/export var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true¢erScene=true&alignSceneGound=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; From 87bdaae05acc2aa9fe82e71d80d1a183cbaa9846 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 11 Jan 2017 12:38:47 -0800 Subject: [PATCH 18/50] remove scale from ModelMeshPartPayload's transform --- libraries/render-utils/src/Model.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index e0a378176f..6292abea3b 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -216,10 +216,12 @@ void Model::updateRenderItems() { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); Transform modelTransform; - modelTransform.setScale(scale); modelTransform.setTranslation(self->_translation); modelTransform.setRotation(self->_rotation); + Transform scaledModelTransform(modelTransform); + scaledModelTransform.setScale(scale); + Transform modelMeshOffset; if (self->isLoaded()) { // includes model offset and unitScale. @@ -254,9 +256,9 @@ void Model::updateRenderItems() { Transform collisionMeshOffset; collisionMeshOffset.setIdentity(); foreach (auto itemID, self->_collisionRenderItems.keys()) { - pendingChanges.updateItem(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) { + pendingChanges.updateItem(itemID, [scaledModelTransform, collisionMeshOffset](MeshPartPayload& data) { // update the model transform for this render item. - data.updateTransform(modelTransform, collisionMeshOffset); + data.updateTransform(scaledModelTransform, collisionMeshOffset); }); } From 1e3a00d1fbb621548e20d856833c411972bb8dc9 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 11 Jan 2017 14:43:37 -0700 Subject: [PATCH 19/50] Enable the LT and RT signals, use them A bit cleaner. Still have to keep track of which hand is the mouse, but this removes some of the hacky code. --- scripts/system/pal.js | 75 ++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index ef3e37101a..48f44570fd 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -384,12 +384,16 @@ function handleMouseMove(pickRay) { // given the pickRay, just do the hover logi ExtendedOverlay.unHover(); }); } + +// handy global to keep track of which hand is the mouse (if any) +var currentHandPressed = 0; +const TRIGGER_CLICK_THRESHOLD = 0.85; +const TRIGGER_PRESS_THRESHOLD = 0.05; + function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position if (HMD.active) { - var hand = whichHand(); - if (hand != 0) { - print("pick ray for " + hand); - pickRay = controllerComputePickRay(hand); + if (currentHandPressed != 0) { + pickRay = controllerComputePickRay(currentHandPressed); } else { // nothing should hover, so ExtendedOverlay.unHover(); @@ -400,50 +404,27 @@ function handleMouseMoveEvent(event) { // find out which overlay (if any) is ove } handleMouseMove(pickRay); } -const TRIGGER_CLICK_THRESHOLD = 0.85; -const TRIGGER_PRESS_THRESHOLD = 0.05; -// for some reason, I could not get trigger mappings for the LT and RT to fire. So, since we can read the -// value of the RT and LT, and the messages come through as mouse moves, we have the hack below to figure out -// which hand is being triggered, and compute the pick ray accordingly. -// -function isPressed(hand) { // helper to see if the hand passed in has the trigger pulled - var controller = (hand === Controller.Standard.RightHand ? Controller.Standard.RT : Controller.Standard.LT); - return Controller.getValue(controller) > TRIGGER_PRESS_THRESHOLD; -} -// helpful globals -var currentHandPressed = 0; -var hands = [Controller.Standard.RightHand, Controller.Standard.LeftHand]; - -function whichHand() { - // for HMD, decide which hand is the "mouse". The 'logic' is to use the first one that - // is triggered, until that one is released. There are interesting effects when both - // are held that this avoids. Mapping the 2 triggers and their lasers to one mouse move - // message is a bit problematic, this mitigates some of it. - if (HMD.active) { - var pressed = {}; - for (var i = 0; i TRIGGER_PRESS_THRESHOLD; + if (currentHandPressed == 0) { + currentHandPressed = isPressed ? hand : 0; + return; } - return currentHandPressed; + if (currentHandPressed == hand) { + currentHandPressed = isPressed ? hand : 0; + return; + } + // otherwise, the other hand is still triggered + // so do nothing. } // We get mouseMoveEvents from the handControllers, via handControllerPointer. // But we don't get mousePressEvents. var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); +var triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press'); function controllerComputePickRay(hand) { var controllerPose = getControllerWorldLocation(hand, true); if (controllerPose.valid) { @@ -458,9 +439,15 @@ function makeClickHandler(hand) { } }; } +function makePressHandler(hand) { + return function (value) { + handleTriggerPressed(hand, value); + } +} triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); - +triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); +triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); // // Manage the connection between the button and the window. // @@ -484,6 +471,7 @@ function off() { isWired = false; } triggerMapping.disable(); // It's ok if we disable twice. + triggerPressMapping.disable(); // see above removeOverlays(); Users.requestsDomainListData = false; } @@ -497,6 +485,7 @@ function onClicked() { Controller.mousePressEvent.connect(handleMouseEvent); Controller.mouseMoveEvent.connect(handleMouseMoveEvent); triggerMapping.enable(); + triggerPressMapping.enable(); } else { off(); } From f235a52a6d5bc681e6e208d38cf495d6686f0c45 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 11 Jan 2017 13:54:25 -0800 Subject: [PATCH 20/50] Eagerly cache QML surfaces for Web3D overlays to prevent stutter on loading the tablet --- interface/src/Application.cpp | 6 ++ interface/src/ui/overlays/Web3DOverlay.cpp | 23 +++----- interface/src/ui/overlays/Web3DOverlay.h | 1 + .../gl/src/gl/OffscreenQmlSurfaceCache.cpp | 57 +++++++++++++++++++ .../gl/src/gl/OffscreenQmlSurfaceCache.h | 34 +++++++++++ 5 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 libraries/gl/src/gl/OffscreenQmlSurfaceCache.cpp create mode 100644 libraries/gl/src/gl/OffscreenQmlSurfaceCache.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ad869ddae3..b0502a88fc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -89,6 +89,7 @@ #include #include #include +#include #include #include #include @@ -512,6 +513,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(true, qApp, qApp); DependencyManager::set(); + DependencyManager::set(); return previousSessionCrashed; } @@ -2003,6 +2005,10 @@ void Application::initializeUi() { showCursor(compositorHelper->getAllowMouseCapture() ? Qt::BlankCursor : Qt::ArrowCursor); } }); + + // Pre-create a couple of Web3D overlays to speed up tablet UI + auto offscreenSurfaceCache = DependencyManager::get(); + offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); } void Application::paintGL() { diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index ee1a0f5256..c987735367 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -28,14 +28,15 @@ #include #include +#include static const float DPI = 30.47f; static const float INCHES_TO_METERS = 1.0f / 39.3701f; static const float METERS_TO_INCHES = 39.3701f; static const float OPAQUE_ALPHA_THRESHOLD = 0.99f; -QString const Web3DOverlay::TYPE = "web3d"; - +const QString Web3DOverlay::TYPE = "web3d"; +const QString Web3DOverlay::QML = "Web3DOverlay.qml"; Web3DOverlay::Web3DOverlay() : _dpi(DPI) { _touchDevice.setCapabilities(QTouchDevice::Position); _touchDevice.setType(QTouchDevice::TouchScreen); @@ -80,8 +81,9 @@ Web3DOverlay::~Web3DOverlay() { // is no longer valid auto webSurface = _webSurface; AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] { - webSurface->deleteLater(); + DependencyManager::get()->release(QML, webSurface); }); + _webSurface.reset(); } auto geometryCache = DependencyManager::get(); if (geometryCache) { @@ -109,23 +111,14 @@ void Web3DOverlay::render(RenderArgs* args) { QOpenGLContext * currentContext = QOpenGLContext::currentContext(); QSurface * currentSurface = currentContext->surface(); if (!_webSurface) { - auto deleter = [](OffscreenQmlSurface* webSurface) { - AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] { - webSurface->deleteLater(); - }); - }; - _webSurface = QSharedPointer(new OffscreenQmlSurface(), deleter); + _webSurface = DependencyManager::get()->acquire(QML); + _webSurface->setMaxFps(10); // FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces // and the current rendering load) - _webSurface->setMaxFps(10); - _webSurface->create(currentContext); - _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); - _webSurface->load("Web3DOverlay.qml"); _webSurface->resume(); + _webSurface->resize(QSize(_resolution.x, _resolution.y)); _webSurface->getRootItem()->setProperty("url", _url); _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL); - _webSurface->getRootContext()->setContextProperty("ApplicationInterface", qApp); - _webSurface->resize(QSize(_resolution.x, _resolution.y)); currentContext->makeCurrent(currentSurface); auto forwardPointerEvent = [=](unsigned int overlayID, const PointerEvent& event) { diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 45ec9fbc50..c389f5e6f9 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -21,6 +21,7 @@ class Web3DOverlay : public Billboard3DOverlay { Q_OBJECT public: + static const QString QML; static QString const TYPE; virtual QString getType() const override { return TYPE; } diff --git a/libraries/gl/src/gl/OffscreenQmlSurfaceCache.cpp b/libraries/gl/src/gl/OffscreenQmlSurfaceCache.cpp new file mode 100644 index 0000000000..ad370a1f43 --- /dev/null +++ b/libraries/gl/src/gl/OffscreenQmlSurfaceCache.cpp @@ -0,0 +1,57 @@ +// +// Created by Bradley Austin Davis on 2017-01-11 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "OffscreenQmlSurfaceCache.h" + +#include +#include + +#include +#include "OffscreenQmlSurface.h" + +OffscreenQmlSurfaceCache::OffscreenQmlSurfaceCache() { +} + +OffscreenQmlSurfaceCache::~OffscreenQmlSurfaceCache() { + _cache.clear(); +} + +QSharedPointer OffscreenQmlSurfaceCache::acquire(const QString& rootSource) { + auto& list = _cache[rootSource]; + if (list.empty()) { + list.push_back(buildSurface(rootSource)); + } + auto result = list.front(); + list.pop_front(); + return result; +} + +void OffscreenQmlSurfaceCache::reserve(const QString& rootSource, int count) { + auto& list = _cache[rootSource]; + while (list.size() < count) { + list.push_back(buildSurface(rootSource)); + } +} + +void OffscreenQmlSurfaceCache::release(const QString& rootSource, const QSharedPointer& surface) { + surface->pause(); + _cache[rootSource].push_back(surface); +} + +QSharedPointer OffscreenQmlSurfaceCache::buildSurface(const QString& rootSource) { + auto surface = QSharedPointer(new OffscreenQmlSurface()); + QOpenGLContext* currentContext = QOpenGLContext::currentContext(); + QSurface* currentSurface = currentContext->surface(); + surface->create(currentContext); + surface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); + surface->load(rootSource); + surface->getRootContext()->setContextProperty("ApplicationInterface", qApp); + surface->resize(QSize(100, 100)); + currentContext->makeCurrent(currentSurface); + return surface; +} + diff --git a/libraries/gl/src/gl/OffscreenQmlSurfaceCache.h b/libraries/gl/src/gl/OffscreenQmlSurfaceCache.h new file mode 100644 index 0000000000..02382a791b --- /dev/null +++ b/libraries/gl/src/gl/OffscreenQmlSurfaceCache.h @@ -0,0 +1,34 @@ +// +// Created by Bradley Austin Davis on 2017-01-11 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once +#ifndef hifi_OffscreenQmlSurfaceCache_h +#define hifi_OffscreenQmlSurfaceCahce_h + +#include "DependencyManager.h" + +#include + +class OffscreenQmlSurface; + +class OffscreenQmlSurfaceCache : public Dependency { + SINGLETON_DEPENDENCY + +public: + OffscreenQmlSurfaceCache(); + virtual ~OffscreenQmlSurfaceCache(); + + QSharedPointer acquire(const QString& rootSource); + void release(const QString& rootSource, const QSharedPointer& surface); + void reserve(const QString& rootSource, int count = 1); + +private: + QSharedPointer buildSurface(const QString& rootSource); + QHash>> _cache; +}; + +#endif From a808a8c7499287300cb21cda3c10376afce12757 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 14:23:18 -0800 Subject: [PATCH 21/50] revert "fix competition between seek mouse and lasers" --- .../controllers/handControllerPointer.js | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 4d478fbdd5..0623ddf100 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -20,9 +20,6 @@ // When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand // controller beam intersects the HUD. -var systemLaserOn = false; - - Script.include("../libraries/controllers.js"); // UTILITIES ------------- @@ -124,12 +121,6 @@ function ignoreMouseActivity() { if (!Reticle.allowMouseCapture) { return true; } - - // if the lasers are on, then reticle/mouse should be hidden and we can ignore it for seeking or depth updating - if (systemLaserOn) { - return true; - } - var pos = Reticle.position; if (!pos || (pos.x == -1 && pos.y == -1)) { return true; @@ -270,12 +261,6 @@ var ONE_MINUS_WEIGHTING = 1 - WEIGHTING; var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 20; function isShakingMouse() { // True if the person is waving the mouse around trying to find it. var now = Date.now(), mouse = Reticle.position, isShaking = false; - - // if the lasers are on, then we ignore mouse shaking - if (systemLaserOn) { - return false; - } - if (lastIntegration && (lastIntegration !== now)) { var velocity = Vec3.length(Vec3.subtract(mouse, lastMouse)) / (now - lastIntegration); averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * velocity); @@ -290,14 +275,9 @@ function isShakingMouse() { // True if the person is waving the mouse around try var NON_LINEAR_DIVISOR = 2; var MINIMUM_SEEK_DISTANCE = 0.1; function updateSeeking(doNotStartSeeking) { - // if the lasers are on, then we never do seeking - if (systemLaserOn) { - isSeeking = false; - return; - } - if (!doNotStartSeeking && (!Reticle.visible || isShakingMouse())) { if (!isSeeking) { + print('Start seeking mouse.'); isSeeking = true; } } // e.g., if we're about to turn it on with first movement. @@ -307,6 +287,7 @@ function updateSeeking(doNotStartSeeking) { averageMouseVelocity = lastIntegration = 0; var lookAt2D = HMD.getHUDLookAtPosition2D(); if (!lookAt2D) { // If this happens, something has gone terribly wrong. + print('Cannot seek without lookAt position'); isSeeking = false; return; // E.g., if parallel to location in HUD } @@ -322,6 +303,7 @@ function updateSeeking(doNotStartSeeking) { } var okX = !updateDimension('x'), okY = !updateDimension('y'); // Evaluate both. Don't short-circuit. if (okX && okY) { + print('Finished seeking mouse'); isSeeking = false; } else { Reticle.setPosition(copy); // Not setReticlePosition @@ -340,7 +322,7 @@ function updateMouseActivity(isClick) { return; } // Bug: mouse clicks should keep going. Just not hand controller clicks handControllerLockOut.update(now); - Reticle.visible = !systemLaserOn; + Reticle.visible = true; } function expireMouseCursor(now) { if (!isPointingAtOverlay() && mouseCursorActivity.expired(now)) { @@ -492,6 +474,7 @@ var LASER_ALPHA = 0.5; var LASER_SEARCH_COLOR_XYZW = {x: 10 / 255, y: 10 / 255, z: 255 / 255, w: LASER_ALPHA}; var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA}; var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1}; +var systemLaserOn = false; function clearSystemLaser() { if (!systemLaserOn) { return; From 357b4db1db3261fd25d55204b934e02cf0c39165 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 14:24:24 -0800 Subject: [PATCH 22/50] fix mouse vs lasers, again --- scripts/system/controllers/handControllerPointer.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 0623ddf100..2a64b856bc 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -20,6 +20,10 @@ // When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand // controller beam intersects the HUD. +var activeTrigger; +function isLaserOn() { + return activeTrigger.partial(); +} Script.include("../libraries/controllers.js"); // UTILITIES ------------- @@ -275,7 +279,7 @@ function isShakingMouse() { // True if the person is waving the mouse around try var NON_LINEAR_DIVISOR = 2; var MINIMUM_SEEK_DISTANCE = 0.1; function updateSeeking(doNotStartSeeking) { - if (!doNotStartSeeking && (!Reticle.visible || isShakingMouse())) { + if (!doNotStartSeeking && !isLaserOn() && (!Reticle.visible || isShakingMouse())) { if (!isSeeking) { print('Start seeking mouse.'); isSeeking = true; @@ -374,7 +378,7 @@ setupHandler(Controller.mouseDoublePressEvent, onMouseClick); var leftTrigger = new Trigger('left'); var rightTrigger = new Trigger('right'); -var activeTrigger = rightTrigger; +activeTrigger = rightTrigger; var activeHand = Controller.Standard.RightHand; var LEFT_HUD_LASER = 1; var RIGHT_HUD_LASER = 2; From e256f7cccc5186c98261ebd8c3688b57bf43387c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 11 Jan 2017 14:27:27 -0800 Subject: [PATCH 23/50] Remove sendqueue spam in traces --- libraries/networking/src/udt/SendQueue.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index 82f4084bb2..31d61bde5d 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -88,8 +88,6 @@ SendQueue::SendQueue(Socket* socket, HifiSockAddr dest) : _socket(socket), _destination(dest) { - PROFILE_ASYNC_BEGIN(network, "SendQueue", _destination.toString()); - // setup psuedo-random number generation for all instances of SendQueue static std::random_device rd; static std::mt19937 generator(rd()); @@ -108,7 +106,6 @@ SendQueue::SendQueue(Socket* socket, HifiSockAddr dest) : } SendQueue::~SendQueue() { - PROFILE_ASYNC_END(network, "SendQueue", _destination.toString()); } void SendQueue::queuePacket(std::unique_ptr packet) { @@ -229,8 +226,6 @@ void SendQueue::sendHandshake() { if (!_hasReceivedHandshakeACK) { // we haven't received a handshake ACK from the client, send another now auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, sizeof(SequenceNumber)); - PROFILE_ASYNC_BEGIN(network, "SendQueue:Handshake", _destination.toString()); - handshakePacket->writePrimitive(_initialSequenceNumber); _socket->writeBasePacket(*handshakePacket, _destination); @@ -246,8 +241,6 @@ void SendQueue::handshakeACK(SequenceNumber initialSequenceNumber) { std::lock_guard locker { _handshakeMutex }; _hasReceivedHandshakeACK = true; } - PROFILE_ASYNC_END(network, "SendQueue:Handshake", _destination.toString()); - // Notify on the handshake ACK condition _handshakeACKCondition.notify_one(); } From 27293c342ce5ec9de4c8904ad5e991fb07973aa4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 11 Jan 2017 14:27:46 -0800 Subject: [PATCH 24/50] Change present rate to be an average over 1/10th of a second --- .../src/display-plugins/OpenGLDisplayPlugin.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index ee0bcf91a9..f4efc0267b 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -114,10 +114,10 @@ protected: bool _vsyncEnabled { true }; QThread* _presentThread{ nullptr }; std::queue _newFrameQueue; - RateCounter<> _droppedFrameRate; - RateCounter<> _newFrameRate; - RateCounter<> _presentRate; - RateCounter<> _renderRate; + RateCounter<100> _droppedFrameRate; + RateCounter<100> _newFrameRate; + RateCounter<100> _presentRate; + RateCounter<100> _renderRate; gpu::FramePointer _currentFrame; gpu::Frame* _lastFrame { nullptr }; From b3e336de3dd0d2e696eff0b7aa63b30fd4d3e4d4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 11 Jan 2017 15:02:18 -0800 Subject: [PATCH 25/50] remove cruft: unused function arguments --- interface/src/avatar/SoftAttachmentModel.cpp | 13 +++++++++---- interface/src/avatar/SoftAttachmentModel.h | 2 +- libraries/render-utils/src/Model.cpp | 14 +++----------- libraries/render-utils/src/Model.h | 2 +- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/interface/src/avatar/SoftAttachmentModel.cpp b/interface/src/avatar/SoftAttachmentModel.cpp index c2e8dab2a1..6351495598 100644 --- a/interface/src/avatar/SoftAttachmentModel.cpp +++ b/interface/src/avatar/SoftAttachmentModel.cpp @@ -37,7 +37,7 @@ int SoftAttachmentModel::getJointIndexOverride(int i) const { // virtual // use the _rigOverride matrices instead of the Model::_rig -void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) { +void SoftAttachmentModel::updateClusterMatrices() { if (!_needsUpdateClusterMatrices) { return; } @@ -45,7 +45,6 @@ void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::qu const FBXGeometry& geometry = getFBXGeometry(); - glm::mat4 modelToWorld = glm::mat4_cast(modelOrientation); for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -53,7 +52,7 @@ void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::qu for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - // TODO: cache these look ups as an optimization + // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); glm::mat4 jointMatrix; if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride->getJointStateCount()) { @@ -61,7 +60,13 @@ void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::qu } else { jointMatrix = _rig->getJointTransform(cluster.jointIndex); } - state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; +#if GLM_ARCH & GLM_ARCH_SSE2 + glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix; + glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); + state.clusterMatrices[j] = out; +#else + state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; +#endif } // Once computed the cluster matrices, update the buffer(s) diff --git a/interface/src/avatar/SoftAttachmentModel.h b/interface/src/avatar/SoftAttachmentModel.h index 84a8b4cc66..cdf957514c 100644 --- a/interface/src/avatar/SoftAttachmentModel.h +++ b/interface/src/avatar/SoftAttachmentModel.h @@ -31,7 +31,7 @@ public: ~SoftAttachmentModel(); virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; - virtual void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) override; + virtual void updateClusterMatrices() override; protected: int getJointIndexOverride(int i) const; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6292abea3b..ccfea99066 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -222,19 +222,11 @@ void Model::updateRenderItems() { Transform scaledModelTransform(modelTransform); scaledModelTransform.setScale(scale); - Transform modelMeshOffset; - if (self->isLoaded()) { - // includes model offset and unitScale. - modelMeshOffset = Transform(self->_rig->getGeometryToRigTransform()); - } else { - modelMeshOffset.postTranslate(self->_offset); - } - uint32_t deleteGeometryCounter = self->_deleteGeometryCounter; render::PendingChanges pendingChanges; foreach (auto itemID, self->_modelMeshRenderItems.keys()) { - pendingChanges.updateItem(itemID, [modelTransform, modelMeshOffset, deleteGeometryCounter](ModelMeshPartPayload& data) { + pendingChanges.updateItem(itemID, [modelTransform, deleteGeometryCounter](ModelMeshPartPayload& data) { if (data._model && data._model->isLoaded()) { if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) { data.startFade(); @@ -242,7 +234,7 @@ void Model::updateRenderItems() { // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == data._model->_deleteGeometryCounter) { // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. - data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation()); + data._model->updateClusterMatrices(); // update the model transform and bounding box for this render item. const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); @@ -1157,7 +1149,7 @@ void Model::simulateInternal(float deltaTime) { } // virtual -void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) { +void Model::updateClusterMatrices() { PerformanceTimer perfTimer("Model::updateClusterMatrices"); if (!_needsUpdateClusterMatrices || !isLoaded()) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 92b718d487..55806b27e1 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -126,7 +126,7 @@ public: bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } virtual void simulate(float deltaTime, bool fullUpdate = true); - virtual void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation); + virtual void updateClusterMatrices(); /// Returns a reference to the shared geometry. const Geometry::Pointer& getGeometry() const { return _renderGeometry; } From e2130d33d9e70eb7f892715db0e4bc07fe0cf72f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 11 Jan 2017 15:10:44 -0800 Subject: [PATCH 26/50] speedup cauterizedClusterMatrices math --- libraries/render-utils/src/Model.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ccfea99066..55c8d92333 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1183,7 +1183,13 @@ void Model::updateClusterMatrices() { if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { jointMatrix = cauterizeMatrix; } +#if GLM_ARCH & GLM_ARCH_SSE2 + glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix; + glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out); + state.cauterizedClusterMatrices[j] = out; +#else state.cauterizedClusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix; +#endif } } From 5c068417329cbafb0cc4e849585ed404ffb13839 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 16:24:05 -0800 Subject: [PATCH 27/50] use update signal instead of timer --- scripts/system/controllers/handControllerPointer.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 2a64b856bc..1f8f0f53e6 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -571,12 +571,6 @@ function update() { Reticle.visible = false; } -var BASIC_TIMER_INTERVAL = 20; // 20ms = 50hz good enough -var updateIntervalTimer = Script.setInterval(function(){ - update(); -}, BASIC_TIMER_INTERVAL); - - // Check periodically for changes to setup. var SETTINGS_CHANGE_RECHECK_INTERVAL = 10 * 1000; // 10 seconds function checkSettings() { @@ -586,9 +580,10 @@ function checkSettings() { checkSettings(); var settingsChecker = Script.setInterval(checkSettings, SETTINGS_CHANGE_RECHECK_INTERVAL); +Script.update.connect(update); Script.scriptEnding.connect(function () { Script.clearInterval(settingsChecker); - Script.clearInterval(updateIntervalTimer); + Script.update.disconnect(update); OffscreenFlags.navigationFocusDisabled = false; }); From 9298bcf628b4c56108e491769a47adbfb6f3e546 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 16:32:15 -0800 Subject: [PATCH 28/50] remove log spam --- scripts/system/controllers/handControllerPointer.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 1f8f0f53e6..b7dfcaeb56 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -280,10 +280,7 @@ var NON_LINEAR_DIVISOR = 2; var MINIMUM_SEEK_DISTANCE = 0.1; function updateSeeking(doNotStartSeeking) { if (!doNotStartSeeking && !isLaserOn() && (!Reticle.visible || isShakingMouse())) { - if (!isSeeking) { - print('Start seeking mouse.'); - isSeeking = true; - } + isSeeking = true; } // e.g., if we're about to turn it on with first movement. if (!isSeeking) { return; @@ -291,7 +288,6 @@ function updateSeeking(doNotStartSeeking) { averageMouseVelocity = lastIntegration = 0; var lookAt2D = HMD.getHUDLookAtPosition2D(); if (!lookAt2D) { // If this happens, something has gone terribly wrong. - print('Cannot seek without lookAt position'); isSeeking = false; return; // E.g., if parallel to location in HUD } @@ -307,7 +303,6 @@ function updateSeeking(doNotStartSeeking) { } var okX = !updateDimension('x'), okY = !updateDimension('y'); // Evaluate both. Don't short-circuit. if (okX && okY) { - print('Finished seeking mouse'); isSeeking = false; } else { Reticle.setPosition(copy); // Not setReticlePosition From 88c2c534ad5b4656f7cc1523098a981eedabd5e4 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 17:01:46 -0800 Subject: [PATCH 29/50] don't set bogus values --- scripts/system/controllers/handControllerPointer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index b7dfcaeb56..2c732b4cdc 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -482,7 +482,6 @@ function clearSystemLaser() { HMD.disableExtraLaser(); systemLaserOn = false; weMovedReticle = true; - Reticle.position = { x: -1, y: -1 }; } function setColoredLaser() { // answer trigger state if lasers supported, else falsey. var color = (activeTrigger.state === 'full') ? LASER_TRIGGER_COLOR_XYZW : LASER_SEARCH_COLOR_XYZW; From c060fd6a68b3d669cca61d050d2bfd1a07f0998a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 11 Jan 2017 17:33:30 -0800 Subject: [PATCH 30/50] restore updateClusterMatrices() in render() --- libraries/render-utils/src/MeshPartPayload.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 650b8934ee..5c4c0890a7 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -584,6 +584,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // Bind the model transform and the skinCLusterMatrices if needed bool canCauterize = args->_renderMode != RenderArgs::SHADOW_RENDER_MODE; + _model->updateClusterMatrices(); bindTransform(batch, locations, canCauterize); //Bind the index buffer and vertex buffer and Blend shapes if needed From b41f8c754d2742fa3395a9d1c7f43d8d948280a7 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 12 Jan 2017 15:46:53 +1300 Subject: [PATCH 31/50] Add updateable message box to JavaScript API --- .../scripting/WindowScriptingInterface.cpp | 72 +++++++++++++++++++ .../src/scripting/WindowScriptingInterface.h | 16 +++++ 2 files changed, 88 insertions(+) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 765c9a8499..7cfbfb174e 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -60,6 +60,19 @@ WindowScriptingInterface::WindowScriptingInterface() { }); } +WindowScriptingInterface::~WindowScriptingInterface() { + QHashIterator i(_messageBoxes); + while (i.hasNext()) { + i.next(); + auto messageBox = i.value(); + disconnect(messageBox); + messageBox->setVisible(false); + messageBox->deleteLater(); + } + + _messageBoxes.clear(); +} + QScriptValue WindowScriptingInterface::hasFocus() { return qApp->hasFocus(); } @@ -210,3 +223,62 @@ void WindowScriptingInterface::shareSnapshot(const QString& path, const QUrl& hr bool WindowScriptingInterface::isPhysicsEnabled() { return qApp->isPhysicsEnabled(); } + +int WindowScriptingInterface::openMessageBox(QString title, QString text, int buttons, int defaultButton) { + if (QThread::currentThread() != thread()) { + int result; + QMetaObject::invokeMethod(this, "openMessageBox", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(int, result), + Q_ARG(QString, title), + Q_ARG(QString, text), + Q_ARG(int, buttons), + Q_ARG(int, defaultButton)); + return result; + } + + return createMessageBox(title, text, buttons, defaultButton); +} + +int WindowScriptingInterface::createMessageBox(QString title, QString text, int buttons, int defaultButton) { + auto messageBox = DependencyManager::get()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text, + static_cast>(buttons), static_cast(defaultButton)); + connect(messageBox, SIGNAL(selected(int)), this, SLOT(onMessageBoxSelected(int))); + + _lastMessageBoxID += 1; + _messageBoxes.insert(_lastMessageBoxID, messageBox); + + return _lastMessageBoxID; +} + +void WindowScriptingInterface::updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton) { + auto messageBox = _messageBoxes.value(id); + if (messageBox) { + messageBox->setProperty("title", title); + messageBox->setProperty("text", text); + messageBox->setProperty("buttons", buttons); + messageBox->setProperty("defaultButton", defaultButton); + } +} + +void WindowScriptingInterface::closeMessageBox(int id) { + auto messageBox = _messageBoxes.value(id); + if (messageBox) { + disconnect(messageBox); + messageBox->setVisible(false); + messageBox->deleteLater(); + _messageBoxes.remove(id); + } +} + +void WindowScriptingInterface::onMessageBoxSelected(int button) { + auto messageBox = qobject_cast(sender()); + auto keys = _messageBoxes.keys(messageBox); + if (keys.length() > 0) { + auto id = keys[0]; // Should be just one message box. + emit messageBoxClosed(id, button); + disconnect(messageBox); + messageBox->setVisible(false); + messageBox->deleteLater(); + _messageBoxes.remove(id); + } +} diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 6aa8707496..4652e00661 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -14,7 +14,9 @@ #include #include +#include #include +#include class CustomPromptResult { public: @@ -35,6 +37,7 @@ class WindowScriptingInterface : public QObject, public Dependency { Q_PROPERTY(int y READ getY) public: WindowScriptingInterface(); + ~WindowScriptingInterface(); int getInnerWidth(); int getInnerHeight(); int getX(); @@ -56,6 +59,13 @@ public slots: void shareSnapshot(const QString& path, const QUrl& href = QUrl("")); bool isPhysicsEnabled(); + int openMessageBox(QString title, QString text, int buttons, int defaultButton); + void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton); + void closeMessageBox(int id); + +private slots: + void onMessageBoxSelected(int button); + signals: void domainChanged(const QString& domainHostname); void svoImportRequested(const QString& url); @@ -64,9 +74,15 @@ signals: void snapshotShared(const QString& error); void processingGif(); + void messageBoxClosed(int id, int button); + private: QString getPreviousBrowseLocation() const; void setPreviousBrowseLocation(const QString& location); + + int createMessageBox(QString title, QString text, int buttons, int defaultButton); + QHash _messageBoxes; + int _lastMessageBoxID{ -1 }; }; #endif // hifi_WindowScriptingInterface_h From 37da800a1440e294faa8612875e8cee78b73f59d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 12 Jan 2017 15:47:41 +1300 Subject: [PATCH 32/50] Display Clara model preparation progress --- scripts/system/html/js/marketplacesInject.js | 35 +++++++++++-- scripts/system/marketplaces/marketplaces.js | 54 +++++++++++++++++++- 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index c5d463875f..d70575184c 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -14,12 +14,16 @@ // Event bridge messages. var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; + var CLARA_IO_STATUS = "CLARA.IO STATUS"; + var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; + var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; var GOTO_DIRECTORY = "GOTO_DIRECTORY"; var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; var canWriteAssets = false; + var xmlHttpRequest = null; function injectCommonCode(isDirectoryPage) { @@ -154,7 +158,7 @@ var uuid = location.href.match(/\/view\/([a-z0-9\-]*)/)[1]; var url = XMLHTTPREQUEST_URL.replace("{uuid}", uuid); - var xmlHttpRequest = new XMLHttpRequest(); + xmlHttpRequest = new XMLHttpRequest(); var responseTextIndex = 0; var statusMessage = ""; var zipFileURL = ""; @@ -170,6 +174,7 @@ if (data.hasOwnProperty("message") && data.message !== null) { statusMessage = data.message; console.log("Clara.io FBX: " + statusMessage); + EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); } // Extract zip file URL. @@ -186,21 +191,28 @@ xmlHttpRequest.onload = function () { var HTTP_OK = 200; if (this.status !== HTTP_OK) { - console.log("ERROR: Clara.io FBX: Zip file request terminated with " + this.status + " " - + this.statusText); + statusMessage = "Zip file request terminated with " + this.status + " " + this.statusText; + console.log("ERROR: Clara.io FBX: " + statusMessage); + EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); return; } - if (zipFileURL == "") { - console.log("ERROR: Clara.io FBX: Zip file URL not found"); + if (zipFileURL === "") { + statusMessage = "Download file URL not provided"; + console.log("ERROR: Clara.io FBX: " + statusMessage); + EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); return; } EventBridge.emitWebEvent(CLARA_IO_DOWNLOAD + " " + zipFileURL); console.log("Clara.io FBX: File download initiated for " + zipFileURL); + + xmlHttpRequest = null; } console.log("Clara.io FBX: Request zip file for " + uuid); + EventBridge.emitWebEvent(CLARA_IO_STATUS + " Initiating download"); + xmlHttpRequest.open("POST", url, true); xmlHttpRequest.setRequestHeader("Accept", "text/event-stream"); xmlHttpRequest.send(); @@ -248,12 +260,25 @@ EventBridge.emitWebEvent(QUERY_CAN_WRITE_ASSETS); } + function cancelClaraDownload() { + if (xmlHttpRequest) { + xmlHttpRequest.abort(); + xmlHttpRequest = null; + console.log("Clara.io FBX: File download cancelled"); + EventBridge.emitWebEvent(CLARA_IO_CANCELLED_DOWNLOAD); + } + } + function onLoad() { EventBridge.scriptEventReceived.connect(function (message) { if (message.slice(0, CAN_WRITE_ASSETS.length) === CAN_WRITE_ASSETS) { canWriteAssets = message.slice(-4) === "true"; } + + if (message.slice(0, CLARA_IO_CANCEL_DOWNLOAD.length) === CLARA_IO_CANCEL_DOWNLOAD) { + cancelClaraDownload(); + } }); var DIRECTORY = 0; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index c173683c74..53aef34600 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -15,17 +15,29 @@ Script.include("../libraries/WebTablet.js"); var toolIconUrl = Script.resolvePath("../assets/images/tools/"); -var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; +//var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; +var MARKETPLACE_URL = "https://clara.io/library?gameCheck=true&public=true"; var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); // Event bridge messages. var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; +var CLARA_IO_STATUS = "CLARA.IO STATUS"; +var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; +var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; var GOTO_DIRECTORY = "GOTO_DIRECTORY"; var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; + +var CLARA_DOWNLOAD_TITLE = "Preparing Download"; +var messageBox = null; +var isDownloadBeingCancelled = false; + +var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel +var NO_BUTTON = 0; // QMessageBox::NoButton + var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; var marketplaceWindow = new OverlayWebWindow({ @@ -36,18 +48,58 @@ var marketplaceWindow = new OverlayWebWindow({ visible: false }); marketplaceWindow.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL); + marketplaceWindow.webEventReceived.connect(function (message) { if (message === GOTO_DIRECTORY) { marketplaceWindow.setURL(MARKETPLACES_URL); + return; } if (message === QUERY_CAN_WRITE_ASSETS) { marketplaceWindow.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); + return; } if (message === WARN_USER_NO_PERMISSIONS) { Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); + return; + } + + if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { + if (isDownloadBeingCancelled) { + return; + } + + var text = message.slice(CLARA_IO_STATUS.length); + if (messageBox === null) { + messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } else { + Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } + return; + } + + if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { + if (messageBox) { + Window.closeMessageBox(messageBox); + messageBox = null; + } + return; + } + + if (message === CLARA_IO_CANCELLED_DOWNLOAD) { + isDownloadBeingCancelled = false; } }); +function onMessageBoxClosed(id, button) { + if (id === messageBox && button === CANCEL_BUTTON) { + isDownloadBeingCancelled = true; + messageBox = null; + marketplaceWindow.emitScriptEvent(CLARA_IO_CANCEL_DOWNLOAD); + } +} + +Window.messageBoxClosed.connect(onMessageBoxClosed); + var toolHeight = 50; var toolWidth = 50; var TOOLBAR_MARGIN_Y = 0; From 8cd7e1b3771dbab2f3da6ce49b2cfa7eab7bf48c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 12 Jan 2017 15:48:25 +1300 Subject: [PATCH 33/50] Improve user messages --- interface/src/Application.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 681d160821..6631111910 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5642,18 +5642,18 @@ void Application::showAssetServerWidget(QString filePath) { } void Application::addAssetToWorldFromURL(QString url) { - qInfo(interfaceapp) << "Download asset and add to world from" << url; + qInfo(interfaceapp) << "Download model and add to world from" << url; QString filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL. if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { QString errorInfo = "You do not have permissions to write to the Asset Server."; - qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; + qWarning(interfaceapp) << "Error downloading model: " + errorInfo; addAssetToWorldError(filename, errorInfo); return; } - addAssetToWorldInfo(filename, "Downloading asset file " + filename + "."); + addAssetToWorldInfo(filename, "Downloading model file " + filename + "."); auto request = ResourceManager::createResourceRequest(nullptr, QUrl(url)); connect(request, &ResourceRequest::finished, this, &Application::addAssetToWorldFromURLRequestFinished); @@ -5668,7 +5668,7 @@ void Application::addAssetToWorldFromURLRequestFinished() { QString filename = url.section("filename=", 1, 1); // Filename from trailing "?filename=" URL parameter. if (result == ResourceRequest::Success) { - qInfo(interfaceapp) << "Downloaded asset from" << url; + qInfo(interfaceapp) << "Downloaded model from" << url; QTemporaryDir temporaryDir; temporaryDir.setAutoRemove(false); if (temporaryDir.isValid()) { @@ -5720,7 +5720,7 @@ void Application::addAssetToWorld(QString filePath) { // Test repeated because possibly different code paths. if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { QString errorInfo = "You do not have permissions to write to the Asset Server."; - qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; + qWarning(interfaceapp) << "Error downloading model: " + errorInfo; addAssetToWorldError(filename, errorInfo); return; } @@ -5741,7 +5741,7 @@ void Application::addAssetToWorldWithNewMapping(QString filePath, QString mappin } else if (result != GetMappingRequest::NoError) { QString errorInfo = "Could not map asset name: " + mapping.left(mapping.length() - QString::number(copy).length() - 1); - qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; + qWarning(interfaceapp) << "Error downloading model: " + errorInfo; addAssetToWorldError(filenameFromPath(filePath), errorInfo); } else if (copy < MAX_COPY_COUNT - 1) { if (copy > 0) { @@ -5753,7 +5753,7 @@ void Application::addAssetToWorldWithNewMapping(QString filePath, QString mappin } else { QString errorInfo = "Too many copies of asset name: " + mapping.left(mapping.length() - QString::number(copy).length() - 1); - qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; + qWarning(interfaceapp) << "Error downloading model: " + errorInfo; addAssetToWorldError(filenameFromPath(filePath), errorInfo); } request->deleteLater(); @@ -5767,8 +5767,8 @@ void Application::addAssetToWorldUpload(QString filePath, QString mapping) { auto upload = DependencyManager::get()->createUpload(filePath); QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable { if (upload->getError() != AssetUpload::NoError) { - QString errorInfo = "Could not upload asset to the Asset Server."; - qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; + QString errorInfo = "Could not upload model to the Asset Server."; + qWarning(interfaceapp) << "Error downloading model: " + errorInfo; addAssetToWorldError(filenameFromPath(filePath), errorInfo); } else { addAssetToWorldSetMapping(filePath, mapping, hash); @@ -5793,7 +5793,7 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q connect(request, &SetMappingRequest::finished, this, [=](SetMappingRequest* request) mutable { if (request->getError() != SetMappingRequest::NoError) { QString errorInfo = "Could not set asset mapping."; - qWarning(interfaceapp) << "Error downloading asset: " + errorInfo; + qWarning(interfaceapp) << "Error downloading model: " + errorInfo; addAssetToWorldError(filenameFromPath(filePath), errorInfo); } else { addAssetToWorldAddEntity(filePath, mapping); @@ -5820,8 +5820,8 @@ void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) { // on. But FBX dimensions may be in cm, so we monitor for the dimension change and rescale again if warranted. if (entityID == QUuid()) { - QString errorInfo = "Could not add asset " + mapping + " to world."; - qWarning(interfaceapp) << "Could not add asset to world: " + errorInfo; + QString errorInfo = "Could not add model " + mapping + " to world."; + qWarning(interfaceapp) << "Could not add model to world: " + errorInfo; addAssetToWorldError(filenameFromPath(filePath), errorInfo); } else { // Monitor when asset is rendered in world so that can resize if necessary. @@ -5864,7 +5864,7 @@ void Application::addAssetToWorldCheckModelSize() { auto scale = std::min(MAXIMUM_DIMENSION / dimensions.x, std::min(MAXIMUM_DIMENSION / dimensions.y, MAXIMUM_DIMENSION / dimensions.z)); dimensions *= scale; - qInfo(interfaceapp) << "Asset" << name << "auto-resized from" << previousDimensions << " to " << dimensions; + qInfo(interfaceapp) << "Model" << name << "auto-resized from" << previousDimensions << " to " << dimensions; doResize = true; item = _addAssetToWorldResizeList.erase(item); // Finished with this entity; advance to next. @@ -5879,7 +5879,7 @@ void Application::addAssetToWorldCheckModelSize() { // Rescale all dimensions. const glm::vec3 UNIT_DIMENSIONS = glm::vec3(1.0f, 1.0f, 1.0f); dimensions = UNIT_DIMENSIONS; - qInfo(interfaceapp) << "Asset" << name << "auto-resize timed out; resized to " << dimensions; + qInfo(interfaceapp) << "Model" << name << "auto-resize timed out; resized to " << dimensions; doResize = true; item = _addAssetToWorldResizeList.erase(item); // Finished with this entity; advance to next. @@ -5932,7 +5932,7 @@ void Application::addAssetToWorldInfo(QString modelName, QString infoText) { if (!_addAssetToWorldErrorTimer.isActive()) { if (!_addAssetToWorldMessageBox) { _addAssetToWorldMessageBox = DependencyManager::get()->createMessageBox(OffscreenUi::ICON_INFORMATION, - "Downloading Asset", "", QMessageBox::NoButton, QMessageBox::NoButton); + "Downloading Model", "", QMessageBox::NoButton, QMessageBox::NoButton); connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed())); } @@ -5997,7 +5997,6 @@ void Application::addAssetToWorldInfoTimeout() { } } - void Application::addAssetToWorldError(QString modelName, QString errorText) { // Displays the most recent error message for a few seconds. @@ -6016,7 +6015,7 @@ void Application::addAssetToWorldError(QString modelName, QString errorText) { if (!_addAssetToWorldMessageBox) { _addAssetToWorldMessageBox = DependencyManager::get()->createMessageBox(OffscreenUi::ICON_INFORMATION, - "Downloading Asset", "", QMessageBox::NoButton, QMessageBox::NoButton); + "Downloading Model", "", QMessageBox::NoButton, QMessageBox::NoButton); connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed())); } From 9587b6a80e0def7ad36d5401ba51907cddd31131 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 12 Jan 2017 20:53:57 +1300 Subject: [PATCH 34/50] Fix initial marketplace URL --- scripts/system/marketplaces/marketplaces.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 53aef34600..ad4edce927 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -15,8 +15,7 @@ Script.include("../libraries/WebTablet.js"); var toolIconUrl = Script.resolvePath("../assets/images/tools/"); -//var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var MARKETPLACE_URL = "https://clara.io/library?gameCheck=true&public=true"; +var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); From cbe8ce0c198f96118262f6504b9b88442b04532f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 12 Jan 2017 21:33:32 +1300 Subject: [PATCH 35/50] Improve handling of zip file generation failing --- scripts/system/html/js/marketplacesInject.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index d70575184c..df6cd137ea 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -180,6 +180,9 @@ // Extract zip file URL. if (data.hasOwnProperty("files") && data.files.length > 0) { zipFileURL = data.files[0].url; + if (zipFileURL.slice(-4) !== ".zip") { + console.log(JSON.stringify(data)); // Data for debugging. + } } } @@ -197,9 +200,9 @@ return; } - if (zipFileURL === "") { - statusMessage = "Download file URL not provided"; - console.log("ERROR: Clara.io FBX: " + statusMessage); + if (zipFileURL.slice(-4) !== ".zip") { + statusMessage = "Error creating zip file for download"; + console.log("ERROR: Clara.io FBX: " + statusMessage + ": " + zipFileURL); EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); return; } From 6e622b37195750406e955900dd39087736ae7a6b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 12 Jan 2017 21:35:24 +1300 Subject: [PATCH 36/50] Don't request model be centered as work-around for downloads failing --- scripts/system/html/js/marketplacesInject.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index df6cd137ea..903b5a45b2 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -154,7 +154,12 @@ // Obtain zip file to download for requested asset. // Reference: https://clara.io/learn/sdk/api/export - var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true¢erScene=true&alignSceneGound=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; + + //var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true¢erScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; + // 12 Jan 21017: Remove "¢erScene=true" option because it causes the Clara.io site to not generate zip files + // for some models (e.g., "Julia" and "Ur Draug"). + var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; + var uuid = location.href.match(/\/view\/([a-z0-9\-]*)/)[1]; var url = XMLHTTPREQUEST_URL.replace("{uuid}", uuid); From 288c069687b6d90e739ed5882c66127004a5745d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 12 Jan 2017 22:31:09 +1300 Subject: [PATCH 37/50] Make cancelling downloads more robust --- scripts/system/html/js/marketplacesInject.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 903b5a45b2..fbffed24d3 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -24,6 +24,7 @@ var canWriteAssets = false; var xmlHttpRequest = null; + var isDownloading = false; // Explicitly track download request status. function injectCommonCode(isDirectoryPage) { @@ -172,7 +173,7 @@ // Messages are appended to responseText; process the new one. var message = this.responseText.slice(responseTextIndex); - if (message.slice(0, 5) === "data:") { + if (isDownloading && message.slice(0, 5) === "data:") { // Ignore messages in flight after finished/cancelled. var data = JSON.parse(message.slice(5)); // Extract status message. @@ -197,6 +198,11 @@ // Note: onprogress doesn't have computable total length. xmlHttpRequest.onload = function () { + + if (!isDownloading) { + return; + } + var HTTP_OK = 200; if (this.status !== HTTP_OK) { statusMessage = "Zip file request terminated with " + this.status + " " + this.statusText; @@ -216,8 +222,11 @@ console.log("Clara.io FBX: File download initiated for " + zipFileURL); xmlHttpRequest = null; + isDownloading = false; } + isDownloading = true; + console.log("Clara.io FBX: Request zip file for " + uuid); EventBridge.emitWebEvent(CLARA_IO_STATUS + " Initiating download"); @@ -269,6 +278,8 @@ } function cancelClaraDownload() { + isDownloading = false; + if (xmlHttpRequest) { xmlHttpRequest.abort(); xmlHttpRequest = null; From d45e6f00cbbe012e28baee53745c2e81f137e508 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 12 Jan 2017 22:35:42 +1300 Subject: [PATCH 38/50] Prepare only one download at a time --- scripts/system/html/js/marketplacesInject.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index fbffed24d3..bce6a0e5d9 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -138,6 +138,12 @@ // Automatic download to High Fidelity. function startAutoDownload() { + // One file request at a time. + if (isDownloading) { + console.log("WARNIKNG: Clara.io FBX: Prepare only one download at a time"); + return; + } + // User must be able to write to Asset Server. if (!canWriteAssets) { console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server"); From 2de83d42bc16a1cf98116280c09fb3c004afaded Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 12 Jan 2017 09:19:08 -0800 Subject: [PATCH 39/50] Add process and system CPU usage to traces --- interface/src/Application.cpp | 75 +++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ad869ddae3..62a4b2c438 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3298,6 +3298,66 @@ bool Application::shouldPaint(float nsecsElapsed) { return true; } +#ifdef Q_OS_WIN +#include +#include +#include +#pragma comment(lib, "pdh.lib") + +static ULARGE_INTEGER lastCPU, lastSysCPU, lastUserCPU; +static int numProcessors; +static HANDLE self; +static PDH_HQUERY cpuQuery; +static PDH_HCOUNTER cpuTotal; + +void initCpuUsage() { + SYSTEM_INFO sysInfo; + FILETIME ftime, fsys, fuser; + + GetSystemInfo(&sysInfo); + numProcessors = sysInfo.dwNumberOfProcessors; + + GetSystemTimeAsFileTime(&ftime); + memcpy(&lastCPU, &ftime, sizeof(FILETIME)); + + self = GetCurrentProcess(); + GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser); + memcpy(&lastSysCPU, &fsys, sizeof(FILETIME)); + memcpy(&lastUserCPU, &fuser, sizeof(FILETIME)); + + PdhOpenQuery(NULL, NULL, &cpuQuery); + PdhAddCounter(cpuQuery, "\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal); + PdhCollectQueryData(cpuQuery); +} + +void getCpuUsage(vec3& systemAndUser) { + FILETIME ftime, fsys, fuser; + ULARGE_INTEGER now, sys, user; + + GetSystemTimeAsFileTime(&ftime); + memcpy(&now, &ftime, sizeof(FILETIME)); + + GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser); + memcpy(&sys, &fsys, sizeof(FILETIME)); + memcpy(&user, &fuser, sizeof(FILETIME)); + systemAndUser.x = (sys.QuadPart - lastSysCPU.QuadPart); + systemAndUser.y = (user.QuadPart - lastUserCPU.QuadPart); + systemAndUser /= (float)(now.QuadPart - lastCPU.QuadPart); + systemAndUser /= (float)numProcessors; + systemAndUser *= 100.0f; + lastCPU = now; + lastUserCPU = user; + lastSysCPU = sys; + + PDH_FMT_COUNTERVALUE counterVal; + PdhCollectQueryData(cpuQuery); + PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal); + systemAndUser.z = (float)counterVal.doubleValue; +} + +#endif + + void Application::idle(float nsecsElapsed) { PerformanceTimer perfTimer("idle"); @@ -3314,6 +3374,18 @@ void Application::idle(float nsecsElapsed) { connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop); } +#ifdef Q_OS_WIN + static std::once_flag once; + std::call_once(once, [] { + initCpuUsage(); + }); + + vec3 kernelUserAndSystem; + getCpuUsage(kernelUserAndSystem); + PROFILE_COUNTER(app, "cpuProcess", { { "system", kernelUserAndSystem.x }, { "user", kernelUserAndSystem.y } }); + PROFILE_COUNTER(app, "cpuSystem", { { "system", kernelUserAndSystem.z } }); +#endif + auto displayPlugin = getActiveDisplayPlugin(); if (displayPlugin) { PROFILE_COUNTER_IF_CHANGED(app, "present", float, displayPlugin->presentRate()); @@ -3324,6 +3396,9 @@ void Application::idle(float nsecsElapsed) { PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get()->getStat("Processing").toInt()); PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get()->getStat("PendingProcessing").toInt()); + + + PROFILE_RANGE(app, __FUNCTION__); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { From 8e1a70891d802b40ea35779cc877ac64e4b785bf Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Jan 2017 10:59:28 -0800 Subject: [PATCH 40/50] Checkpoint after cherry-picking from existing PR --- interface/resources/qml/controls-uit/CheckBox.qml | 1 - interface/resources/qml/controls-uit/Table.qml | 4 ++-- interface/resources/qml/hifi/LetterboxMessage.qml | 6 +++--- interface/resources/qml/hifi/NameCard.qml | 8 ++++---- interface/resources/qml/hifi/Pal.qml | 1 + 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index d3cbb87e5b..09a0e04148 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -16,7 +16,6 @@ import "../styles-uit" Original.CheckBox { id: checkBox - HifiConstants { id: hifi } property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index 8e685dc253..3054c496bc 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -109,7 +109,7 @@ TableView { handle: Item { id: scrollbarHandle - implicitWidth: 6 + implicitWidth: 9 Rectangle { anchors { fill: parent @@ -124,7 +124,7 @@ TableView { } scrollBarBackground: Item { - implicitWidth: 9 + implicitWidth: 12 Rectangle { anchors { fill: parent diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml index 290cff6634..c8adcdcb74 100644 --- a/interface/resources/qml/hifi/LetterboxMessage.qml +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -15,7 +15,7 @@ import "../styles-uit" Item { property alias text: popupText.text - property real radius: hifi.dimensions.borderRadius + property real popupRadius: hifi.dimensions.borderRadius visible: false id: letterbox anchors.fill: parent @@ -23,13 +23,13 @@ Item { anchors.fill: parent color: "black" opacity: 0.5 - radius: radius + radius: popupRadius } Rectangle { width: Math.max(parent.width * 0.75, 400) height: popupText.contentHeight*1.5 anchors.centerIn: parent - radius: radius + radius: popupRadius color: "white" FiraSansSemiBold { id: popupText diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 0ad8bfd57e..b6448fa23c 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -9,7 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 import "../styles-uit" @@ -128,9 +127,10 @@ Row { start: Qt.point(0, 0) end: Qt.point(parent.width, 0) gradient: Gradient { - GradientStop { position: 0.05; color: "#00CFEF" } - GradientStop { position: 0.5; color: "#9450A5" } - GradientStop { position: 0.95; color: "#EA4C5F" } + GradientStop { position: 0.0; color: "#2c8e72" } + GradientStop { position: 0.9; color: "#1fc6a6" } + GradientStop { position: 0.91; color: "#ea4c5f" } + GradientStop { position: 1.0; color: "#ea4c5f" } } } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 7ad322bfa4..51e3eac7f2 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -425,6 +425,7 @@ Item { if (selected) { table.selection.clear(); // for now, no multi-select table.selection.select(userIndex); + table.positionViewAtRow(userIndex, ListView.Visible); } else { table.selection.deselect(userIndex); } From 4dee0efdd3b9619f029ef78dac71481d4b867bf0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 13 Jan 2017 09:12:49 +1300 Subject: [PATCH 41/50] Use more reliable Clara.io API parameters --- scripts/system/html/js/marketplacesInject.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index bce6a0e5d9..94e2a68460 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -163,9 +163,9 @@ // Reference: https://clara.io/learn/sdk/api/export //var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true¢erScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; - // 12 Jan 21017: Remove "¢erScene=true" option because it causes the Clara.io site to not generate zip files - // for some models (e.g., "Julia" and "Ur Draug"). - var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; + // 13 Jan 2017: Specify FBX version 5 and remove some options in order to make Clara.io site more likely to + // be successful in generating zip files. + var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?fbxUnit=Meter&fbxVersion=5&fbxEmbedTextures=true&imageFormat=WebGL"; var uuid = location.href.match(/\/view\/([a-z0-9\-]*)/)[1]; var url = XMLHTTPREQUEST_URL.replace("{uuid}", uuid); From b7e95a348690cb6084f200ca8bc712704808b6b0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Jan 2017 14:14:28 -0800 Subject: [PATCH 42/50] Updates from Alan --- .../resources/qml/controls-uit/Table.qml | 29 ++++++++---- interface/resources/qml/hifi/NameCard.qml | 8 +++- interface/resources/qml/hifi/Pal.qml | 47 ++++++++++++++----- .../qml/styles-uit/HifiConstants.qml | 20 +++++--- 4 files changed, 74 insertions(+), 30 deletions(-) diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index 3054c496bc..c7e0809b29 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -109,35 +109,46 @@ TableView { handle: Item { id: scrollbarHandle - implicitWidth: 9 + implicitWidth: hifi.dimensions.scrollbarHandleWidth Rectangle { anchors { fill: parent topMargin: 3 bottomMargin: 3 // "" - leftMargin: 2 // Move it right - rightMargin: -2 // "" + leftMargin: 1 // Move it right + rightMargin: -1 // "" } - radius: 3 + radius: hifi.dimensions.scrollbarHandleWidth/2 color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark } } scrollBarBackground: Item { - implicitWidth: 12 + implicitWidth: hifi.dimensions.scrollbarBackgroundWidth Rectangle { anchors { fill: parent margins: -1 // Expand - topMargin: headerVisible ? -hifi.dimensions.tableHeaderHeight : -1 + topMargin: -1 + } + color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight : hifi.colors.tableScrollBackgroundDark + + // Extend header color above scrollbar background + Rectangle { + anchors { + top: parent.top + topMargin: -hifi.dimensions.tableHeaderHeight + left: parent.left + right: parent.right + } + height: hifi.dimensions.tableHeaderHeight + color: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark + visible: headerVisible } - color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark - Rectangle { // Extend header bottom border anchors { top: parent.top - topMargin: hifi.dimensions.tableHeaderHeight - 1 left: parent.left right: parent.right } diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index b6448fa23c..a0dde8bacc 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -67,6 +67,8 @@ Row { size: thisNameCard.displayTextHeight // Text Positioning verticalAlignment: Text.AlignVCenter + // Style + color: hifi.colors.darkGray } // UserName Text @@ -82,6 +84,8 @@ Row { size: thisNameCard.usernameTextHeight // Text Positioning verticalAlignment: Text.AlignVCenter + // Style + color: hifi.colors.baseGray } // Spacer @@ -104,7 +108,7 @@ Row { // Anchors anchors.fill: parent // Style - color: "#dbdbdb" // Very appropriate hex value here + color: "#c5c5c5" radius: parent.radius } // Rectangle for the VU meter audio level @@ -113,7 +117,7 @@ Row { // Size width: (thisNameCard.audioLevel) * parent.width // Style - color: "#dbdbdb" // Very appropriate hex value here + color: "#c5c5c5" radius: parent.radius // Anchors anchors.bottom: parent.bottom diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 51e3eac7f2..66dce622ff 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -16,29 +16,46 @@ import QtQuick.Controls 1.4 import "../styles-uit" import "../controls-uit" as HifiControls -Item { +Rectangle { id: pal // Size width: parent.width height: parent.height + // Style + color: "#E3E3E3" // Properties property int myCardHeight: 70 property int rowHeight: 70 property int actionButtonWidth: 75 - property int nameCardWidth: width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 + property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring. property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities. property bool iAmAdmin: false + // This is the container for the PAL + Rectangle { + id: palContainer + // Size + width: pal.width - 50 + height: pal.height - 50 + // Style + color: pal.color + // Anchors + anchors.centerIn: pal + // Properties + radius: hifi.dimensions.borderRadius + // This contains the current user's NameCard and will contain other information in the future Rectangle { id: myInfo // Size - width: pal.width + width: palContainer.width height: myCardHeight + 20 + // Style + color: pal.color // Anchors - anchors.top: pal.top + anchors.top: palContainer.top // Properties radius: hifi.dimensions.borderRadius // This NameCard refers to the current user's NameCard (the one above the table) @@ -57,15 +74,15 @@ Item { } // Rectangles used to cover up rounded edges on bottom of MyInfo Rectangle Rectangle { - color: "#FFFFFF" - width: pal.width + color: pal.color + width: palContainer.width height: 10 anchors.top: myInfo.bottom anchors.left: parent.left } Rectangle { - color: "#FFFFFF" - width: pal.width + color: pal.color + width: palContainer.width height: 10 anchors.bottom: table.top anchors.left: parent.left @@ -74,7 +91,7 @@ Item { Rectangle { id: adminTab // Size - width: actionButtonWidth * 2 + 2 + width: 2*actionButtonWidth + hifi.dimensions.scrollbarBackgroundWidth + 2 height: 40 // Anchors anchors.bottom: myInfo.bottom @@ -98,6 +115,7 @@ Item { anchors.topMargin: 8 anchors.left: parent.left anchors.right: parent.right + anchors.rightMargin: hifi.dimensions.scrollbarBackgroundWidth // Style font.capitalization: Font.AllUppercase color: hifi.colors.redHighlight @@ -110,8 +128,8 @@ Item { HifiControls.Table { id: table // Size - height: pal.height - myInfo.height - 4 - width: pal.width - 4 + height: palContainer.height - myInfo.height - 4 + width: palContainer.width - 4 // Anchors anchors.left: parent.left anchors.top: myInfo.bottom @@ -154,6 +172,8 @@ Item { TableViewColumn { visible: iAmAdmin role: "kick" + // The hacky spaces used to center text over the button, since I don't know how to apply a margin + // to column header text. title: "BAN" width: actionButtonWidth movable: false @@ -168,7 +188,7 @@ Item { // Size height: rowHeight color: styleData.selected - ? "#afafaf" + ? hifi.colors.orangeHighlight : styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd } @@ -353,7 +373,7 @@ Item { width: 20 height: 28 anchors.right: adminTab.right - anchors.rightMargin: 31 + anchors.rightMargin: 31 + hifi.dimensions.scrollbarBackgroundWidth anchors.top: adminTab.top anchors.topMargin: 2 RalewayRegular { @@ -379,6 +399,7 @@ Item { LetterboxMessage { id: letterboxMessage } + } function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml var data = optionalData || userModelData, length = data.length; diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index cb99dc6005..7eca1aa725 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -59,11 +59,17 @@ Item { readonly property color faintGray: "#e3e3e3" readonly property color primaryHighlight: "#00b4ef" readonly property color blueHighlight: "#00b4ef" - readonly property color blueAccent: "#1080b8" - readonly property color redHighlight: "#e2334d" - readonly property color redAccent: "#b70a37" + readonly property color blueAccent: "#0093C5" + readonly property color redHighlight: "#EA4C5F" + readonly property color redAccent: "#C62147" readonly property color greenHighlight: "#1ac567" - readonly property color greenShadow: "#2c8e72" + readonly property color greenShadow: "#359D85" + readonly property color orangeHighlight: "#FFC49C" + readonly property color orangeAccent: "#FF6309" + readonly property color indigoHighlight: "#C0D2FF" + readonly property color indigoAccent: "#9495FF" + readonly property color magentaHighlight: "#EF93D1" + readonly property color magentaAccent: "#A2277C" // Semitransparent readonly property color darkGray30: "#4d121212" readonly property color darkGray0: "#00121212" @@ -95,9 +101,9 @@ Item { readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background readonly property color tableBackgroundLight: tableRowLightEven readonly property color tableBackgroundDark: tableRowDarkEven - readonly property color tableScrollHandleLight: "#8F8F8F" + readonly property color tableScrollHandleLight: "#DDDDDD" readonly property color tableScrollHandleDark: "#707070" - readonly property color tableScrollBackgroundLight: tableRowLightEven + readonly property color tableScrollBackgroundLight: tableRowLightOdd readonly property color tableScrollBackgroundDark: "#323232" readonly property color checkboxLightStart: "#ffffff" readonly property color checkboxLightFinish: "#afafaf" @@ -151,6 +157,8 @@ Item { readonly property real controlLineHeight: 28 // Height of spinbox control on 1920 x 1080 monitor readonly property real controlInterlineHeight: 21 // 75% of controlLineHeight readonly property vector2d menuPadding: Qt.vector2d(14, 12) + readonly property real scrollbarBackgroundWidth: 18 + readonly property real scrollbarHandleWidth: scrollbarBackgroundWidth - 2 } Item { From 6f2386eabd02dc0b0560d50bd4a0324934426bcb Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 13 Jan 2017 11:20:00 +1300 Subject: [PATCH 43/50] Fix processing of Clara.io status messages --- scripts/system/html/js/marketplacesInject.js | 48 ++++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 94e2a68460..65f722826f 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -172,29 +172,46 @@ xmlHttpRequest = new XMLHttpRequest(); var responseTextIndex = 0; - var statusMessage = ""; var zipFileURL = ""; xmlHttpRequest.onreadystatechange = function () { - // Messages are appended to responseText; process the new one. + // Messages are appended to responseText; process the new ones. var message = this.responseText.slice(responseTextIndex); + var statusMessage = ""; - if (isDownloading && message.slice(0, 5) === "data:") { // Ignore messages in flight after finished/cancelled. - var data = JSON.parse(message.slice(5)); + if (isDownloading) { // Ignore messages in flight after finished/cancelled. + var lines = message.split(/[\n\r]+/); - // Extract status message. - if (data.hasOwnProperty("message") && data.message !== null) { - statusMessage = data.message; - console.log("Clara.io FBX: " + statusMessage); - EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); + for (var i = 0, length = lines.length; i < length; i++) { + if (lines[i].slice(0, 5) === "data:") { + // Parse line. + var data; + try { + data = JSON.parse(lines[i].slice(5)); + } + catch (e) { + data = {}; + } + + // Extract status message. + if (data.hasOwnProperty("message") && data.message !== null) { + statusMessage = data.message; + console.log("Clara.io FBX: " + statusMessage); + } + + // Extract zip file URL. + if (data.hasOwnProperty("files") && data.files.length > 0) { + zipFileURL = data.files[0].url; + if (zipFileURL.slice(-4) !== ".zip") { + console.log(JSON.stringify(data)); // Data for debugging. + } + } + } } - // Extract zip file URL. - if (data.hasOwnProperty("files") && data.files.length > 0) { - zipFileURL = data.files[0].url; - if (zipFileURL.slice(-4) !== ".zip") { - console.log(JSON.stringify(data)); // Data for debugging. - } + if (statusMessage !== "") { + // Update the UI with the most recent status message. + EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); } } @@ -204,6 +221,7 @@ // Note: onprogress doesn't have computable total length. xmlHttpRequest.onload = function () { + var statusMessage = ""; if (!isDownloading) { return; From 659badd8bf335067b09f25b3fbcb5ae7b8fbb1ff Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 9 Jan 2017 21:46:26 -0800 Subject: [PATCH 44/50] Change minimum threadpool size 1 --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7245f00493..04e8bcb9ef 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -197,7 +197,7 @@ static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16; // For processing on QThreadPool, we target a number of threads after reserving some // based on how many are being consumed by the application and the display plugin. However, // we will never drop below the 'min' value -static const int MIN_PROCESSING_THREAD_POOL_SIZE = 2; +static const int MIN_PROCESSING_THREAD_POOL_SIZE = 1; static const QString SNAPSHOT_EXTENSION = ".jpg"; static const QString SVO_EXTENSION = ".svo"; From 632989e031f557f4f4793662e6a9c4638c5f5b25 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 13 Jan 2017 12:09:22 +1300 Subject: [PATCH 45/50] Fix progress messages not displaying in HMD --- scripts/system/marketplaces/marketplaces.js | 34 ++++++++++++--------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index ad4edce927..cea22d50e0 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -48,13 +48,25 @@ var marketplaceWindow = new OverlayWebWindow({ }); marketplaceWindow.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL); -marketplaceWindow.webEventReceived.connect(function (message) { +function onWebEventReceived(message) { if (message === GOTO_DIRECTORY) { - marketplaceWindow.setURL(MARKETPLACES_URL); + var url = MARKETPLACES_URL; + if (marketplaceWindow.visible) { + marketplaceWindow.setURL(url); + } + if (marketplaceWebTablet) { + marketplaceWebTablet.setURL(url); + } return; } if (message === QUERY_CAN_WRITE_ASSETS) { - marketplaceWindow.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); + var canWriteAssets = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); + if (marketplaceWindow.visible) { + marketplaceWindow.emitScriptEvent(canWriteAssets); + } + if (marketplaceWebTablet) { + marketplaceWebTablet.getOverlayObject().emitScriptEvent(canWriteAssets); + } return; } if (message === WARN_USER_NO_PERMISSIONS) { @@ -87,7 +99,9 @@ marketplaceWindow.webEventReceived.connect(function (message) { if (message === CLARA_IO_CANCELLED_DOWNLOAD) { isDownloadBeingCancelled = false; } -}); +} + +marketplaceWindow.webEventReceived.connect(onWebEventReceived); function onMessageBoxClosed(id, button) { if (id === messageBox && button === CANCEL_BUTTON) { @@ -122,17 +136,7 @@ function showMarketplace() { marketplaceWebTablet = new WebTablet(MARKETPLACE_URL_INITIAL, null, null, true); Settings.setValue(persistenceKey, marketplaceWebTablet.pickle()); marketplaceWebTablet.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL); - marketplaceWebTablet.getOverlayObject().webEventReceived.connect(function (message) { - if (message === GOTO_DIRECTORY) { - marketplaceWebTablet.setURL(MARKETPLACES_URL); - } - if (message === QUERY_CAN_WRITE_ASSETS) { - marketplaceWebTablet.getOverlayObject().emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); - } - if (message === WARN_USER_NO_PERMISSIONS) { - Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); - } - }); + marketplaceWebTablet.getOverlayObject().webEventReceived.connect(onWebEventReceived); } else { marketplaceWindow.setURL(MARKETPLACE_URL_INITIAL); marketplaceWindow.setVisible(true); From ad5822d96d51f663b860bd88a62baad86ab78595 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 13 Jan 2017 12:26:42 +1300 Subject: [PATCH 46/50] Fix first progress message box not closing --- scripts/system/marketplaces/marketplaces.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index cea22d50e0..d5530e7db2 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -89,7 +89,7 @@ function onWebEventReceived(message) { } if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { - if (messageBox) { + if (messageBox !== null) { Window.closeMessageBox(messageBox); messageBox = null; } From d7ab7316a89286c370e9adb0775e9b8dcc69a261 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 13 Jan 2017 12:28:53 +1300 Subject: [PATCH 47/50] Tidying --- scripts/system/html/js/marketplacesInject.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 65f722826f..0944c4113e 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -80,10 +80,10 @@ // Add button links. $('#exploreClaraMarketplace').on('click', function () { - window.location = "https://clara.io/library?gameCheck=true&public=true" + window.location = "https://clara.io/library?gameCheck=true&public=true"; }); $('#exploreHifiMarketplace').on('click', function () { - window.location = "http://www.highfidelity.com/marketplace" + window.location = "http://www.highfidelity.com/marketplace"; }); } @@ -218,7 +218,7 @@ responseTextIndex = this.responseText.length; }; - // Note: onprogress doesn't have computable total length. + // Note: onprogress doesn't have computable total length so can't use it to determine % complete. xmlHttpRequest.onload = function () { var statusMessage = ""; @@ -236,7 +236,7 @@ } if (zipFileURL.slice(-4) !== ".zip") { - statusMessage = "Error creating zip file for download"; + statusMessage = "Error creating zip file for download."; console.log("ERROR: Clara.io FBX: " + statusMessage + ": " + zipFileURL); EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); return; From 2383592678c8db6e4a2da782f70ea3d9232e9da0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 12 Jan 2017 13:51:45 -0800 Subject: [PATCH 48/50] Improve FBX parsing performance by bulk loading of arrays --- libraries/fbx/src/FBXReader_Node.cpp | 69 ++++++++++++++++++---------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Node.cpp b/libraries/fbx/src/FBXReader_Node.cpp index 85850a748e..435c4d830b 100644 --- a/libraries/fbx/src/FBXReader_Node.cpp +++ b/libraries/fbx/src/FBXReader_Node.cpp @@ -43,31 +43,54 @@ template QVariant readBinaryArray(QDataStream& in, int& position) { position += sizeof(quint32) * 3; QVector values; - const unsigned int DEFLATE_ENCODING = 1; - if (encoding == DEFLATE_ENCODING) { - // preface encoded data with uncompressed length - QByteArray compressed(sizeof(quint32) + compressedLength, 0); - *((quint32*)compressed.data()) = qToBigEndian(arrayLength * sizeof(T)); - in.readRawData(compressed.data() + sizeof(quint32), compressedLength); - position += compressedLength; - QByteArray uncompressed = qUncompress(compressed); - if (uncompressed.isEmpty()) { // answers empty byte array if corrupt - throw QString("corrupt fbx file"); - } - QDataStream uncompressedIn(uncompressed); - uncompressedIn.setByteOrder(QDataStream::LittleEndian); - uncompressedIn.setVersion(QDataStream::Qt_4_5); // for single/double precision switch - for (quint32 i = 0; i < arrayLength; i++) { - T value; - uncompressedIn >> value; - values.append(value); + if ((int)QSysInfo::ByteOrder == (int)in.byteOrder()) { + values.resize(arrayLength); + const unsigned int DEFLATE_ENCODING = 1; + QByteArray arrayData; + if (encoding == DEFLATE_ENCODING) { + // preface encoded data with uncompressed length + QByteArray compressed(sizeof(quint32) + compressedLength, 0); + *((quint32*)compressed.data()) = qToBigEndian(arrayLength * sizeof(T)); + in.readRawData(compressed.data() + sizeof(quint32), compressedLength); + position += compressedLength; + arrayData = qUncompress(compressed); + if (arrayData.isEmpty() || arrayData.size() != (sizeof(T) * arrayLength)) { // answers empty byte array if corrupt + throw QString("corrupt fbx file"); + } + } else { + arrayData.resize(sizeof(T) * arrayLength); + position += sizeof(T) * arrayLength; + in.readRawData(arrayData.data(), arrayData.size()); } + memcpy(&values[0], arrayData.constData(), arrayData.size()); } else { - for (quint32 i = 0; i < arrayLength; i++) { - T value; - in >> value; - position += streamSize(); - values.append(value); + values.reserve(arrayLength); + const unsigned int DEFLATE_ENCODING = 1; + if (encoding == DEFLATE_ENCODING) { + // preface encoded data with uncompressed length + QByteArray compressed(sizeof(quint32) + compressedLength, 0); + *((quint32*)compressed.data()) = qToBigEndian(arrayLength * sizeof(T)); + in.readRawData(compressed.data() + sizeof(quint32), compressedLength); + position += compressedLength; + QByteArray uncompressed = qUncompress(compressed); + if (uncompressed.isEmpty()) { // answers empty byte array if corrupt + throw QString("corrupt fbx file"); + } + QDataStream uncompressedIn(uncompressed); + uncompressedIn.setByteOrder(QDataStream::LittleEndian); + uncompressedIn.setVersion(QDataStream::Qt_4_5); // for single/double precision switch + for (quint32 i = 0; i < arrayLength; i++) { + T value; + uncompressedIn >> value; + values.append(value); + } + } else { + for (quint32 i = 0; i < arrayLength; i++) { + T value; + in >> value; + position += streamSize(); + values.append(value); + } } } return QVariant::fromValue(values); From 29b160d685ee5b5b38656d8d89ea4cf1ec5f38d9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 12 Jan 2017 16:16:02 -0800 Subject: [PATCH 49/50] Reduce temp qvariant creation and qvariant conversion --- libraries/fbx/src/FBXReader.cpp | 172 ++++++++++++++++++--------- libraries/fbx/src/FBXReader_Mesh.cpp | 14 ++- 2 files changed, 123 insertions(+), 63 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 0171b95a3d..5272969e6b 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -555,8 +555,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } else if (subobject.name == "Properties70") { foreach (const FBXNode& subsubobject, subobject.children) { + static const QVariant APPLICATION_NAME = QVariant(QByteArray("Original|ApplicationName")); if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 && - subsubobject.properties.at(0) == "Original|ApplicationName") { + subsubobject.properties.at(0) == APPLICATION_NAME) { geometry.applicationName = subsubobject.properties.at(4).toString(); } } @@ -571,10 +572,12 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS int index = 4; foreach (const FBXNode& subobject, object.children) { if (subobject.name == propertyName) { - QString subpropName = subobject.properties.at(0).toString(); - if (subpropName == "UnitScaleFactor") { + static const QVariant UNIT_SCALE_FACTOR = QByteArray("UnitScaleFactor"); + static const QVariant AMBIENT_COLOR = QByteArray("AmbientColor"); + const auto& subpropName = subobject.properties.at(0); + if (subpropName == UNIT_SCALE_FACTOR) { unitScaleFactor = subobject.properties.at(index).toFloat(); - } else if (subpropName == "AmbientColor") { + } else if (subpropName == AMBIENT_COLOR) { ambientColor = getVec3(subobject.properties, index); } } @@ -672,66 +675,87 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS index = 4; } if (properties) { - foreach (const FBXNode& property, subobject.children) { + static const QVariant GEOMETRIC_TRANSLATION = QByteArray("GeometricTranslation"); + static const QVariant GEOMETRIC_ROTATION = QByteArray("GeometricRotation"); + static const QVariant GEOMETRIC_SCALING = QByteArray("GeometricScaling"); + static const QVariant LCL_TRANSLATION = QByteArray("Lcl Translation"); + static const QVariant LCL_ROTATION = QByteArray("Lcl Rotation"); + static const QVariant LCL_SCALING = QByteArray("Lcl Scaling"); + static const QVariant ROTATION_MAX = QByteArray("RotationMax"); + static const QVariant ROTATION_MAX_X = QByteArray("RotationMaxX"); + static const QVariant ROTATION_MAX_Y = QByteArray("RotationMaxY"); + static const QVariant ROTATION_MAX_Z = QByteArray("RotationMaxZ"); + static const QVariant ROTATION_MIN = QByteArray("RotationMin"); + static const QVariant ROTATION_MIN_X = QByteArray("RotationMinX"); + static const QVariant ROTATION_MIN_Y = QByteArray("RotationMinY"); + static const QVariant ROTATION_MIN_Z = QByteArray("RotationMinZ"); + static const QVariant ROTATION_OFFSET = QByteArray("RotationOffset"); + static const QVariant ROTATION_PIVOT = QByteArray("RotationPivot"); + static const QVariant SCALING_OFFSET = QByteArray("ScalingOffset"); + static const QVariant SCALING_PIVOT = QByteArray("ScalingPivot"); + static const QVariant PRE_ROTATION = QByteArray("PreRotation"); + static const QVariant POST_ROTATION = QByteArray("PostRotation"); + foreach(const FBXNode& property, subobject.children) { + const auto& childProperty = property.properties.at(0); if (property.name == propertyName) { - if (property.properties.at(0) == "Lcl Translation") { + if (childProperty == LCL_TRANSLATION) { translation = getVec3(property.properties, index); - } else if (property.properties.at(0) == "RotationOffset") { + } else if (childProperty == ROTATION_OFFSET) { rotationOffset = getVec3(property.properties, index); - } else if (property.properties.at(0) == "RotationPivot") { + } else if (childProperty == ROTATION_PIVOT) { rotationPivot = getVec3(property.properties, index); - } else if (property.properties.at(0) == "PreRotation") { + } else if (childProperty == PRE_ROTATION) { preRotation = getVec3(property.properties, index); - } else if (property.properties.at(0) == "Lcl Rotation") { + } else if (childProperty == LCL_ROTATION) { rotation = getVec3(property.properties, index); - } else if (property.properties.at(0) == "PostRotation") { + } else if (childProperty == POST_ROTATION) { postRotation = getVec3(property.properties, index); - } else if (property.properties.at(0) == "ScalingPivot") { + } else if (childProperty == SCALING_PIVOT) { scalePivot = getVec3(property.properties, index); - } else if (property.properties.at(0) == "Lcl Scaling") { + } else if (childProperty == LCL_SCALING) { scale = getVec3(property.properties, index); - } else if (property.properties.at(0) == "ScalingOffset") { + } else if (childProperty == SCALING_OFFSET) { scaleOffset = getVec3(property.properties, index); // NOTE: these rotation limits are stored in degrees (NOT radians) - } else if (property.properties.at(0) == "RotationMin") { + } else if (childProperty == ROTATION_MIN) { rotationMin = getVec3(property.properties, index); - } else if (property.properties.at(0) == "RotationMax") { + } else if (childProperty == ROTATION_MAX) { rotationMax = getVec3(property.properties, index); - } else if (property.properties.at(0) == "RotationMinX") { + } else if (childProperty == ROTATION_MIN_X) { rotationMinX = property.properties.at(index).toBool(); - } else if (property.properties.at(0) == "RotationMinY") { + } else if (childProperty == ROTATION_MIN_Y) { rotationMinY = property.properties.at(index).toBool(); - } else if (property.properties.at(0) == "RotationMinZ") { + } else if (childProperty == ROTATION_MIN_Z) { rotationMinZ = property.properties.at(index).toBool(); - } else if (property.properties.at(0) == "RotationMaxX") { + } else if (childProperty == ROTATION_MAX_X) { rotationMaxX = property.properties.at(index).toBool(); - } else if (property.properties.at(0) == "RotationMaxY") { + } else if (childProperty == ROTATION_MAX_Y) { rotationMaxY = property.properties.at(index).toBool(); - } else if (property.properties.at(0) == "RotationMaxZ") { + } else if (childProperty == ROTATION_MAX_Z) { rotationMaxZ = property.properties.at(index).toBool(); - } else if (property.properties.at(0) == "GeometricTranslation") { + } else if (childProperty == GEOMETRIC_TRANSLATION) { geometricTranslation = getVec3(property.properties, index); hasGeometricOffset = true; - } else if (property.properties.at(0) == "GeometricRotation") { + } else if (childProperty == GEOMETRIC_ROTATION) { geometricRotation = getVec3(property.properties, index); hasGeometricOffset = true; - } else if (property.properties.at(0) == "GeometricScaling") { + } else if (childProperty == GEOMETRIC_SCALING) { geometricScaling = getVec3(property.properties, index); hasGeometricOffset = true; } @@ -842,20 +866,26 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS propertyName = "P"; index = 4; foreach (const FBXNode& property, subobject.children) { + static const QVariant UV_SET = QByteArray("UVSet"); + static const QVariant CURRENT_TEXTURE_BLEND_MODE = QByteArray("CurrentTextureBlendMode"); + static const QVariant USE_MATERIAL = QByteArray("UseMaterial"); + static const QVariant TRANSLATION = QByteArray("Translation"); + static const QVariant ROTATION = QByteArray("Rotation"); + static const QVariant SCALING = QByteArray("Scaling"); if (property.name == propertyName) { QString v = property.properties.at(0).toString(); - if (property.properties.at(0) == "UVSet") { + if (property.properties.at(0) == UV_SET) { std::string uvName = property.properties.at(index).toString().toStdString(); tex.assign(tex.UVSet, property.properties.at(index).toString()); - } else if (property.properties.at(0) == "CurrentTextureBlendMode") { + } else if (property.properties.at(0) == CURRENT_TEXTURE_BLEND_MODE) { tex.assign(tex.currentTextureBlendMode, property.properties.at(index).value()); - } else if (property.properties.at(0) == "UseMaterial") { + } else if (property.properties.at(0) == USE_MATERIAL) { tex.assign(tex.useMaterial, property.properties.at(index).value()); - } else if (property.properties.at(0) == "Translation") { + } else if (property.properties.at(0) == TRANSLATION) { tex.assign(tex.translation, getVec3(property.properties, index)); - } else if (property.properties.at(0) == "Rotation") { + } else if (property.properties.at(0) == ROTATION) { tex.assign(tex.rotation, getVec3(property.properties, index)); - } else if (property.properties.at(0) == "Scaling") { + } else if (property.properties.at(0) == SCALING) { tex.assign(tex.scaling, getVec3(property.properties, index)); if (tex.scaling.x == 0.0f) { tex.scaling.x = 1.0f; @@ -931,87 +961,114 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (properties) { std::vector unknowns; + static const QVariant DIFFUSE_COLOR = QByteArray("DiffuseColor"); + static const QVariant DIFFUSE_FACTOR = QByteArray("DiffuseFactor"); + static const QVariant DIFFUSE = QByteArray("Diffuse"); + static const QVariant SPECULAR_COLOR = QByteArray("SpecularColor"); + static const QVariant SPECULAR_FACTOR = QByteArray("SpecularFactor"); + static const QVariant SPECULAR = QByteArray("Specular"); + static const QVariant EMISSIVE_COLOR = QByteArray("EmissiveColor"); + static const QVariant EMISSIVE_FACTOR = QByteArray("EmissiveFactor"); + static const QVariant EMISSIVE = QByteArray("Emissive"); + static const QVariant AMBIENT_FACTOR = QByteArray("AmbientFactor"); + static const QVariant SHININESS = QByteArray("Shininess"); + static const QVariant OPACITY = QByteArray("Shininess"); + static const QVariant MAYA_USE_NORMAL_MAP = QByteArray("Maya|use_normal_map"); + static const QVariant MAYA_BASE_COLOR = QByteArray("Maya|base_color"); + static const QVariant MAYA_USE_COLOR_MAP = QByteArray("Maya|use_color_map"); + static const QVariant MAYA_ROUGHNESS = QByteArray("Maya|roughness"); + static const QVariant MAYA_USE_ROUGHNESS_MAP = QByteArray("Maya|use_roughness_map"); + static const QVariant MAYA_METALLIC = QByteArray("Maya|metallic"); + static const QVariant MAYA_USE_METALLIC_MAP = QByteArray("Maya|use_metallic_map"); + static const QVariant MAYA_EMISSIVE = QByteArray("Maya|emissive"); + static const QVariant MAYA_EMISSIVE_INTENSITY = QByteArray("Maya|emissive_intensity"); + static const QVariant MAYA_USE_EMISSIVE_MAP = QByteArray("Maya|use_emissive_map"); + static const QVariant MAYA_USE_AO_MAP = QByteArray("Maya|use_ao_map"); + + + + foreach(const FBXNode& property, subobject.children) { if (property.name == propertyName) { - if (property.properties.at(0) == "DiffuseColor") { + if (property.properties.at(0) == DIFFUSE_COLOR) { material.diffuseColor = getVec3(property.properties, index); - } else if (property.properties.at(0) == "DiffuseFactor") { + } else if (property.properties.at(0) == DIFFUSE_FACTOR) { material.diffuseFactor = property.properties.at(index).value(); - } else if (property.properties.at(0) == "Diffuse") { + } else if (property.properties.at(0) == DIFFUSE) { // NOTE: this is uneeded but keep it for now for debug // material.diffuseColor = getVec3(property.properties, index); // material.diffuseFactor = 1.0; - } else if (property.properties.at(0) == "SpecularColor") { + } else if (property.properties.at(0) == SPECULAR_COLOR) { material.specularColor = getVec3(property.properties, index); - } else if (property.properties.at(0) == "SpecularFactor") { + } else if (property.properties.at(0) == SPECULAR_FACTOR) { material.specularFactor = property.properties.at(index).value(); - } else if (property.properties.at(0) == "Specular") { + } else if (property.properties.at(0) == SPECULAR) { // NOTE: this is uneeded but keep it for now for debug // material.specularColor = getVec3(property.properties, index); // material.specularFactor = 1.0; - } else if (property.properties.at(0) == "EmissiveColor") { + } else if (property.properties.at(0) == EMISSIVE_COLOR) { material.emissiveColor = getVec3(property.properties, index); - } else if (property.properties.at(0) == "EmissiveFactor") { + } else if (property.properties.at(0) == EMISSIVE_FACTOR) { material.emissiveFactor = property.properties.at(index).value(); - } else if (property.properties.at(0) == "Emissive") { + } else if (property.properties.at(0) == EMISSIVE) { // NOTE: this is uneeded but keep it for now for debug // material.emissiveColor = getVec3(property.properties, index); // material.emissiveFactor = 1.0; - } else if (property.properties.at(0) == "AmbientFactor") { + } else if (property.properties.at(0) == AMBIENT_FACTOR) { material.ambientFactor = property.properties.at(index).value(); // Detected just for BLender AO vs lightmap - } else if (property.properties.at(0) == "Shininess") { + } else if (property.properties.at(0) == SHININESS) { material.shininess = property.properties.at(index).value(); - } else if (property.properties.at(0) == "Opacity") { + } else if (property.properties.at(0) == OPACITY) { material.opacity = property.properties.at(index).value(); } // Sting Ray Material Properties!!!! - else if (property.properties.at(0) == "Maya|use_normal_map") { + else if (property.properties.at(0) == MAYA_USE_NORMAL_MAP) { material.isPBSMaterial = true; material.useNormalMap = (bool)property.properties.at(index).value(); - } else if (property.properties.at(0) == "Maya|base_color") { + } else if (property.properties.at(0) == MAYA_BASE_COLOR) { material.isPBSMaterial = true; material.diffuseColor = getVec3(property.properties, index); - } else if (property.properties.at(0) == "Maya|use_color_map") { + } else if (property.properties.at(0) == MAYA_USE_COLOR_MAP) { material.isPBSMaterial = true; material.useAlbedoMap = (bool) property.properties.at(index).value(); - } else if (property.properties.at(0) == "Maya|roughness") { + } else if (property.properties.at(0) == MAYA_ROUGHNESS) { material.isPBSMaterial = true; material.roughness = property.properties.at(index).value(); - } else if (property.properties.at(0) == "Maya|use_roughness_map") { + } else if (property.properties.at(0) == MAYA_USE_ROUGHNESS_MAP) { material.isPBSMaterial = true; material.useRoughnessMap = (bool)property.properties.at(index).value(); - } else if (property.properties.at(0) == "Maya|metallic") { + } else if (property.properties.at(0) == MAYA_METALLIC) { material.isPBSMaterial = true; material.metallic = property.properties.at(index).value(); - } else if (property.properties.at(0) == "Maya|use_metallic_map") { + } else if (property.properties.at(0) == MAYA_USE_METALLIC_MAP) { material.isPBSMaterial = true; material.useMetallicMap = (bool)property.properties.at(index).value(); - } else if (property.properties.at(0) == "Maya|emissive") { + } else if (property.properties.at(0) == MAYA_EMISSIVE) { material.isPBSMaterial = true; material.emissiveColor = getVec3(property.properties, index); - } else if (property.properties.at(0) == "Maya|emissive_intensity") { + } else if (property.properties.at(0) == MAYA_EMISSIVE_INTENSITY) { material.isPBSMaterial = true; material.emissiveIntensity = property.properties.at(index).value(); - } else if (property.properties.at(0) == "Maya|use_emissive_map") { + } else if (property.properties.at(0) == MAYA_USE_EMISSIVE_MAP) { material.isPBSMaterial = true; material.useEmissiveMap = (bool)property.properties.at(index).value(); - } else if (property.properties.at(0) == "Maya|use_ao_map") { + } else if (property.properties.at(0) == MAYA_USE_AO_MAP) { material.isPBSMaterial = true; material.useOcclusionMap = (bool)property.properties.at(index).value(); @@ -1116,9 +1173,11 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS #endif } } else if (child.name == "Connections") { + static const QVariant OO = QByteArray("OO"); + static const QVariant OP = QByteArray("OP"); foreach (const FBXNode& connection, child.children) { if (connection.name == "C" || connection.name == "Connect") { - if (connection.properties.at(0) == "OO") { + if (connection.properties.at(0) == OO) { QString childID = getID(connection.properties, 1); QString parentID = getID(connection.properties, 2); ooChildToParent.insert(childID, parentID); @@ -1132,8 +1191,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS _lightmapOffset = glm::clamp((*lightIt).second.color.x, 0.f, 1.f); } } - } - if (connection.properties.at(0) == "OP") { + } else if (connection.properties.at(0) == OP) { int counter = 0; QByteArray type = connection.properties.at(3).toByteArray().toLower(); if (type.contains("DiffuseFactor")) { diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 5a824aa284..8eb31b0731 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -171,7 +171,8 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn QVector materials; QVector textures; bool isMaterialPerPolygon = false; - + static const QVariant BY_VERTICE = QByteArray("ByVertice"); + static const QVariant INDEX_TO_DIRECT = QByteArray("IndexToDirect"); foreach (const FBXNode& child, object.children) { if (child.name == "Vertices") { data.vertices = createVec3Vector(getDoubleVector(child)); @@ -189,10 +190,10 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn } else if (subdata.name == "NormalsIndex") { data.normalIndices = getIntVector(subdata); - } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == "ByVertice") { + } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == BY_VERTICE) { data.normalsByVertex = true; - } else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == "IndexToDirect") { + } else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == INDEX_TO_DIRECT) { indexToDirect = true; } } @@ -209,10 +210,10 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn } else if (subdata.name == "ColorsIndex") { data.colorIndices = getIntVector(subdata); - } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == "ByVertice") { + } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == BY_VERTICE) { data.colorsByVertex = true; - } else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == "IndexToDirect") { + } else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == INDEX_TO_DIRECT) { indexToDirect = true; } } @@ -298,11 +299,12 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn } } } else if (child.name == "LayerElementMaterial") { + static const QVariant BY_POLYGON = QByteArray("ByPolygon"); foreach (const FBXNode& subdata, child.children) { if (subdata.name == "Materials") { materials = getIntVector(subdata); } else if (subdata.name == "MappingInformationType") { - if (subdata.properties.at(0) == "ByPolygon") + if (subdata.properties.at(0) == BY_POLYGON) isMaterialPerPolygon = true; } else { isMaterialPerPolygon = false; From 42e13d1e9771107be1a9574cca0b3d8b8f761940 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 14 Jan 2017 10:26:13 -0800 Subject: [PATCH 50/50] Fix audio failures on systems that return incorrect/unsupported mixFormat --- libraries/audio-client/src/AudioClient.cpp | 42 +++++++++------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 1aa4439de9..1e3dc11338 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -424,35 +424,25 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, adjustedAudioFormat = desiredAudioFormat; // - // Attempt the device sample rate in decreasing order of preference. + // Attempt the device sample rate and channel count in decreasing order of preference. // - if (audioDevice.supportedSampleRates().contains(48000)) { - adjustedAudioFormat.setSampleRate(48000); - } else if (audioDevice.supportedSampleRates().contains(44100)) { - adjustedAudioFormat.setSampleRate(44100); - } else if (audioDevice.supportedSampleRates().contains(32000)) { - adjustedAudioFormat.setSampleRate(32000); - } else if (audioDevice.supportedSampleRates().contains(24000)) { - adjustedAudioFormat.setSampleRate(24000); - } else if (audioDevice.supportedSampleRates().contains(16000)) { - adjustedAudioFormat.setSampleRate(16000); - } else if (audioDevice.supportedSampleRates().contains(96000)) { - adjustedAudioFormat.setSampleRate(96000); - } else if (audioDevice.supportedSampleRates().contains(192000)) { - adjustedAudioFormat.setSampleRate(192000); - } else if (audioDevice.supportedSampleRates().contains(88200)) { - adjustedAudioFormat.setSampleRate(88200); - } else if (audioDevice.supportedSampleRates().contains(176400)) { - adjustedAudioFormat.setSampleRate(176400); + const int sampleRates[] = { 48000, 44100, 32000, 24000, 16000, 96000, 192000, 88200, 176400 }; + const int inputChannels[] = { 1, 2, 4, 6, 8 }; // prefer mono + const int outputChannels[] = { 2, 4, 6, 8, 1 }; // prefer stereo, downmix as last resort + + for (int channelCount : (desiredAudioFormat.channelCount() == 1 ? inputChannels : outputChannels)) { + for (int sampleRate : sampleRates) { + + adjustedAudioFormat.setChannelCount(channelCount); + adjustedAudioFormat.setSampleRate(sampleRate); + + if (audioDevice.isFormatSupported(adjustedAudioFormat)) { + return true; + } + } } - if (adjustedAudioFormat != desiredAudioFormat) { - // return the nearest in case it needs 2 channels - adjustedAudioFormat = audioDevice.nearestFormat(adjustedAudioFormat); - return true; - } else { - return false; - } + return false; // a supported format could not be found } bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationSamples, unsigned int numSourceSamples,