From 7db86204d191fbfa61e2950484cd689c9a6e709d Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 8 Feb 2017 00:01:48 +0000 Subject: [PATCH 1/7] general function to load QML for javascript for tablet (cherry picked from commit b50e20c5457151368b7ead81bf44ce678de9dbe2) --- libraries/script-engine/src/TabletScriptingInterface.cpp | 8 ++++++++ libraries/script-engine/src/TabletScriptingInterface.h | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index 2414ec469f..e7f8ebe2cb 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -212,6 +212,14 @@ void TabletProxy::gotoMenuScreen() { } } +void TabletProxy::loadQMLSource(const QVariant& path) { + if (_qmlTabletRoot) { + if (_state != State::QML) { + QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, path)); + _state = State::QML; + } + } +} void TabletProxy::gotoHomeScreen() { if (_qmlTabletRoot) { if (_state != State::Home) { diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index 4fe2c44c10..a005152fa9 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -89,6 +89,8 @@ public: Q_INVOKABLE void gotoWebScreen(const QString& url); Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl); + Q_INVOKABLE void loadQMLSource(const QVariant& path); + /**jsdoc * Creates a new button, adds it to this and returns it. * @function TabletProxy#addButton @@ -149,7 +151,7 @@ protected: QQuickItem* _qmlTabletRoot { nullptr }; QObject* _qmlOffscreenSurface { nullptr }; - enum class State { Uninitialized, Home, Web, Menu }; + enum class State { Uninitialized, Home, Web, Menu, QML }; State _state { State::Uninitialized }; }; From a209d0372a1a5ec587371351d2b7690cba065977 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 8 Feb 2017 17:27:19 -0800 Subject: [PATCH 2/7] First cut of pal on tablet. --- interface/resources/qml/hifi/Pal.qml | 6 +- .../resources/qml/hifi/tablet/TabletRoot.qml | 13 ++++ interface/src/ui/overlays/Web3DOverlay.cpp | 7 ++ libraries/gl/src/gl/OffscreenQmlSurface.cpp | 12 ++++ libraries/gl/src/gl/OffscreenQmlSurface.h | 5 ++ .../src/TabletScriptingInterface.cpp | 23 +++++++ .../src/TabletScriptingInterface.h | 23 +++++++ scripts/system/pal.js | 66 ++++++++++++++----- 8 files changed, 138 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 923b09b9ef..20376d3fc0 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -16,6 +16,8 @@ import QtQuick.Controls 1.4 import "../styles-uit" import "../controls-uit" as HifiControls +// references HMD, Users, UserActivityLogger from root context + Rectangle { id: pal // Size @@ -35,7 +37,9 @@ Rectangle { // Keep a local list of per-avatar gainSliderValueDBs. Far faster than fetching this data from the server. // NOTE: if another script modifies the per-avatar gain, this value won't be accurate! property var gainSliderValueDB: ({}); - + + HifiConstants { id: hifi } + // The letterbox used for popup messages LetterboxMessage { id: letterboxMessage diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index cfda92e774..0260bd6a01 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -18,6 +18,16 @@ Item { loader.item.scriptURL = injectedJavaScriptUrl; } + // used to send a message from qml to interface script. + signal sendToScript(var message); + + // used to receive messages from interface script + function fromScript(message) { + if (loader.item.hasOwnProperty("fromScript")) { + loader.item.fromScript(message); + } + } + SoundEffect { id: buttonClickSound volume: 0.1 @@ -55,6 +65,9 @@ Item { } }); } + if (loader.item.hasOwnProperty("sendToScript")) { + loader.item.sendToScript.connect(tabletRoot.sendToScript); + } loader.item.forceActiveFocus(); } } diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index f33ef24c0d..a381b90bfb 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -23,11 +23,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -149,6 +152,10 @@ void Web3DOverlay::loadSourceURL() { _webSurface->load(_url, [&](QQmlContext* context, QObject* obj) {}); _webSurface->resume(); + _webSurface->getRootContext()->setContextProperty("Users", DependencyManager::get().data()); + _webSurface->getRootContext()->setContextProperty("HMD", DependencyManager::get().data()); + _webSurface->getRootContext()->setContextProperty("UserActivityLogger", DependencyManager::get().data()); + if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") { auto tabletScriptingInterface = DependencyManager::get(); auto flags = tabletScriptingInterface->getFlags(); diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 8af115ebcb..447b9d56aa 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -604,6 +604,9 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::functionsetParentItem(_quickWindow->contentItem()); @@ -952,4 +955,13 @@ void OffscreenQmlSurface::emitWebEvent(const QVariant& message) { } } +void OffscreenQmlSurface::sendToQml(const QVariant& message) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "emitQmlEvent", Qt::QueuedConnection, Q_ARG(QVariant, message)); + } else if (_rootItem) { + // call fromScript method on qml root + QMetaObject::invokeMethod(_rootItem, "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message)); + } +} + #include "OffscreenQmlSurface.moc" diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.h b/libraries/gl/src/gl/OffscreenQmlSurface.h index f6168e7b6d..efd35fce8b 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.h +++ b/libraries/gl/src/gl/OffscreenQmlSurface.h @@ -107,6 +107,11 @@ signals: void scriptEventReceived(const QVariant& message); void webEventReceived(const QVariant& message); + // qml event bridge +public slots: + void sendToQml(const QVariant& message); +signals: + void fromQml(QVariant message); protected: bool filterEnabled(QObject* originalDestination, QEvent* event) const; diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index e7f8ebe2cb..616d751bf7 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -183,6 +183,18 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr _qmlTabletRoot = qmlTabletRoot; if (_qmlTabletRoot && _qmlOffscreenSurface) { QObject::connect(_qmlOffscreenSurface, SIGNAL(webEventReceived(QVariant)), this, SIGNAL(webEventReceived(QVariant))); + + // forward qml surface events to interface js + connect(dynamic_cast(_qmlOffscreenSurface), &OffscreenQmlSurface::fromQml, [this](QVariant message) { + if (message.canConvert()) { + emit fromQml(qvariant_cast(message).toVariant()); + } else if (message.canConvert()) { + emit fromQml(message.toString()); + } else { + qWarning() << "fromQml: Unsupported message type " << message; + } + }); + gotoHomeScreen(); QMetaObject::invokeMethod(_qmlTabletRoot, "setUsername", Q_ARG(const QVariant&, QVariant(getUsername()))); @@ -197,6 +209,7 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr } else { removeButtonsFromHomeScreen(); _state = State::Uninitialized; + emit screenChanged(QVariant("Closed"), QVariant("")); } } @@ -208,6 +221,7 @@ void TabletProxy::gotoMenuScreen() { QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToMenuScreen()), Qt::DirectConnection); QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(VRMENU_SOURCE_URL))); _state = State::Menu; + emit screenChanged(QVariant("Menu"), QVariant(VRMENU_SOURCE_URL)); } } } @@ -217,6 +231,7 @@ void TabletProxy::loadQMLSource(const QVariant& path) { if (_state != State::QML) { QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, path)); _state = State::QML; + emit screenChanged(QVariant("QML"), path); } } } @@ -228,6 +243,7 @@ void TabletProxy::gotoHomeScreen() { QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(TABLET_SOURCE_URL))); QMetaObject::invokeMethod(_qmlTabletRoot, "playButtonClickSound"); _state = State::Home; + emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); } } } @@ -244,6 +260,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS if (_state != State::Web) { QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL))); _state = State::Web; + emit screenChanged(QVariant("Web"), QVariant(url)); } QMetaObject::invokeMethod(_qmlTabletRoot, "loadWebUrl", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl))); @@ -306,6 +323,12 @@ void TabletProxy::emitScriptEvent(QVariant msg) { } } +void TabletProxy::sendToQml(QVariant msg) { + if (_qmlOffscreenSurface) { + QMetaObject::invokeMethod(_qmlOffscreenSurface, "sendToQml", Qt::AutoConnection, Q_ARG(QVariant, msg)); + } +} + void TabletProxy::addButtonsToHomeScreen() { auto tablet = getQmlTablet(); if (!tablet) { diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index a005152fa9..93f5bcf6ba 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -122,6 +122,13 @@ public: */ Q_INVOKABLE void emitScriptEvent(QVariant msg); + /**jsdoc + * Used to send an event to the qml embedded in the tablet + * @function TabletProxy#sendToQml + * @param msg {object|string} + */ + Q_INVOKABLE void sendToQml(QVariant msg); + Q_INVOKABLE bool onHomeScreen(); QObject* getTabletSurface(); @@ -139,6 +146,22 @@ signals: */ void webEventReceived(QVariant msg); + /**jsdoc + * Signaled when this tablet receives an event from the qml embedded in the tablet + * @function TabletProxy#fromQml + * @param msg {object|string} + * @returns {Signal} + */ + void fromQml(QVariant msg); + + /**jsdoc + * Signales when this tablet screen changes. + * @function TabletProxy#screenChanged + * @param type {string} - "Home", "Web", "Menu", "QML", "Closed" + * @param url {string} - only valid for Web and QML. + */ + void screenChanged(QVariant type, QVariant url); + private slots: void addButtonsToHomeScreen(); void addButtonsToMenuScreen(); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index adbde0ef5c..5f02be83e5 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -203,8 +203,8 @@ var pal = new OverlayWindow({ height: 640, visible: false }); -pal.fromQml.connect(function (message) { // messages are {method, params}, like json-rpc. See also sendToQml. - print('From PAL QML:', JSON.stringify(message)); +function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml. + print('AJT: From PAL QML:', JSON.stringify(message)); switch (message.method) { case 'selected': selectedIds = message.params; @@ -234,6 +234,7 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like } break; case 'refresh': + print("AJT: REFRESH!"); removeOverlays(); populateUserList(message.params); UserActivityLogger.palAction("refresh", ""); @@ -259,7 +260,15 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like default: print('Unrecognized message from Pal.qml:', JSON.stringify(message)); } -}); +} + +function sendToQml(message) { + if (Settings.getValue("HUDUIEnabled")) { + pal.sendToQml(message); + } else { + tablet.sendToQml(message); + } +} // // Main operations. @@ -298,10 +307,10 @@ function populateUserList(selectData) { data.push(avatarPalDatum); print('PAL data:', JSON.stringify(avatarPalDatum)); }); - pal.sendToQml({ method: 'users', params: data }); + sendToQml({ method: 'users', params: data }); if (selectData) { selectData[2] = true; - pal.sendToQml({ method: 'select', params: selectData }); + sendToQml({ method: 'select', params: selectData }); } } @@ -322,7 +331,7 @@ function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { } print('Username Data:', JSON.stringify(data)); // Ship the data off to QML - pal.sendToQml({ method: 'updateUsername', params: data }); + sendToQml({ method: 'updateUsername', params: data }); } var pingPong = true; @@ -396,7 +405,7 @@ function handleClick(pickRay) { ExtendedOverlay.applyPickRay(pickRay, function (overlay) { // Don't select directly. Tell qml, who will give us back a list of ids. var message = {method: 'select', params: [[overlay.key], !overlay.selected, false]}; - pal.sendToQml(message); + sendToQml(message); return true; }); } @@ -492,6 +501,7 @@ if (Settings.getValue("HUDUIEnabled")) { visible: true, alpha: 0.9 }); + pal.fromQml.connect(fromQml); } else { tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); button = tablet.addButton({ @@ -499,7 +509,9 @@ if (Settings.getValue("HUDUIEnabled")) { icon: "icons/tablet-icons/people-i.svg", sortOrder: 7 }); + tablet.fromQml.connect(fromQml); } + var isWired = false; var audioTimer; var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) @@ -518,10 +530,26 @@ function off() { Users.requestsDomainListData = false; } function onClicked() { - if (!pal.visible) { + if (Settings.getValue("HUDUIEnabled")) { + if (!pal.visible) { + Users.requestsDomainListData = true; + populateUserList(); + pal.raise(); + isWired = true; + Script.update.connect(updateOverlays); + Controller.mousePressEvent.connect(handleMouseEvent); + Controller.mouseMoveEvent.connect(handleMouseMoveEvent); + triggerMapping.enable(); + triggerPressMapping.enable(); + audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS); + } else { + off(); + } + pal.setVisible(!pal.visible); + } else { + tablet.loadQMLSource("../Pal.qml"); Users.requestsDomainListData = true; populateUserList(); - pal.raise(); isWired = true; Script.update.connect(updateOverlays); Controller.mousePressEvent.connect(handleMouseEvent); @@ -529,10 +557,7 @@ function onClicked() { triggerMapping.enable(); triggerPressMapping.enable(); audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS); - } else { - off(); } - pal.setVisible(!pal.visible); } // @@ -550,7 +575,7 @@ function receiveMessage(channel, messageString, senderID) { if (!pal.visible) { onClicked(); } - pal.sendToQml(message); // Accepts objects, not just strings. + sendToQml(message); // Accepts objects, not just strings. break; default: print('Unrecognized PAL message', messageString); @@ -607,13 +632,13 @@ function createAudioInterval(interval) { var userId = id || 0; param[userId] = level; }); - pal.sendToQml({method: 'updateAudioLevel', params: param}); + sendToQml({method: 'updateAudioLevel', params: param}); }, interval); } function avatarDisconnected(nodeID) { // remove from the pal list - pal.sendToQml({method: 'avatarDisconnected', params: [nodeID]}); + sendToQml({method: 'avatarDisconnected', params: [nodeID]}); } // // Button state. @@ -624,11 +649,20 @@ function onVisibleChanged() { button.clicked.connect(onClicked); pal.visibleChanged.connect(onVisibleChanged); pal.closed.connect(off); + +if (!Settings.getValue("HUDUIEnabled")) { + tablet.screenChanged.connect(function (type, url) { + if (type !== "QML" || url !== "../Pal.qml") { + off(); + } + }); +} + Users.usernameFromIDReply.connect(usernameFromIDReply); Users.avatarDisconnected.connect(avatarDisconnected); function clearLocalQMLDataAndClosePAL() { - pal.sendToQml({ method: 'clearLocalQMLData' }); + sendToQml({ method: 'clearLocalQMLData' }); if (pal.visible) { onClicked(); // Close the PAL } From 7cc30793289ba178d49e1a5ec2cccf064fe1d505 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 8 Feb 2017 17:58:24 -0800 Subject: [PATCH 3/7] Tightened up margins for the tablet. --- interface/resources/qml/hifi/Pal.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 20376d3fc0..29f0146608 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -28,7 +28,7 @@ Rectangle { // Properties property int myCardHeight: 90 property int rowHeight: 70 - property int actionButtonWidth: 75 + property int actionButtonWidth: 55 property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth property var myData: ({displayName: "", userName: "", audioLevel: 0.0, admin: true}) // 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. @@ -58,8 +58,8 @@ Rectangle { property bool punctuationMode: false id: palContainer // Size - width: pal.width - 50 - height: pal.height - 50 + width: pal.width - 10 + height: pal.height - 10 // Style color: pal.color // Anchors @@ -401,7 +401,7 @@ Rectangle { width: 20 height: 28 anchors.right: adminTab.right - anchors.rightMargin: 31 + hifi.dimensions.scrollbarBackgroundWidth + anchors.rightMargin: 10 + hifi.dimensions.scrollbarBackgroundWidth anchors.top: adminTab.top anchors.topMargin: 2 RalewayRegular { From 090f6321899f8f57c8ecb6dc39d08d87d4d83b38 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 8 Feb 2017 18:16:48 -0800 Subject: [PATCH 4/7] Pal: Moved keyboard tray to be flush with bottom of window. --- interface/resources/qml/hifi/Pal.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 29f0146608..0c7104fba5 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -426,6 +426,8 @@ Rectangle { onExited: adminHelpText.color = hifi.colors.redHighlight } } + } + HifiControls.Keyboard { id: keyboard raised: myCard.currentlyEditingDisplayName && HMD.active @@ -436,7 +438,7 @@ Rectangle { right: parent.right } } - } + // Timer used when selecting table rows that aren't yet present in the model // (i.e. when selecting avatars using edit.js or sphere overlays) Timer { From c2b832fb9c6857f6178ad77bfa5078901b5e9275 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 8 Feb 2017 18:55:22 -0800 Subject: [PATCH 5/7] removed debug prints from pal.js --- scripts/system/pal.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 5f02be83e5..2e07a2d431 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -204,7 +204,6 @@ var pal = new OverlayWindow({ visible: false }); function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml. - print('AJT: From PAL QML:', JSON.stringify(message)); switch (message.method) { case 'selected': selectedIds = message.params; @@ -234,7 +233,6 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See } break; case 'refresh': - print("AJT: REFRESH!"); removeOverlays(); populateUserList(message.params); UserActivityLogger.palAction("refresh", ""); From b3a46c3518c61f01c8f73d824788438671d52953 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 8 Feb 2017 18:56:16 -0800 Subject: [PATCH 6/7] Bug fix for TabletProxy::loadQMLSource() --- libraries/script-engine/src/TabletScriptingInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index 616d751bf7..71f0073ead 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -229,6 +229,7 @@ void TabletProxy::gotoMenuScreen() { void TabletProxy::loadQMLSource(const QVariant& path) { if (_qmlTabletRoot) { if (_state != State::QML) { + removeButtonsFromHomeScreen(); QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, path)); _state = State::QML; emit screenChanged(QVariant("QML"), path); From e25ad0f332837b7594cdd62b3fa1fbf1222c6f26 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 8 Feb 2017 18:56:58 -0800 Subject: [PATCH 7/7] Fix debug assert in FBXReader. --- libraries/fbx/src/FBXReader_Node.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader_Node.cpp b/libraries/fbx/src/FBXReader_Node.cpp index 435c4d830b..fb5f6ce893 100644 --- a/libraries/fbx/src/FBXReader_Node.cpp +++ b/libraries/fbx/src/FBXReader_Node.cpp @@ -62,7 +62,9 @@ template QVariant readBinaryArray(QDataStream& in, int& position) { position += sizeof(T) * arrayLength; in.readRawData(arrayData.data(), arrayData.size()); } - memcpy(&values[0], arrayData.constData(), arrayData.size()); + if (arrayData.size() > 0) { + memcpy(&values[0], arrayData.constData(), arrayData.size()); + } } else { values.reserve(arrayLength); const unsigned int DEFLATE_ENCODING = 1;