From 5fbf10c2f390918f126ea287c52302df8038add3 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 8 Oct 2018 23:48:12 -0700 Subject: [PATCH 001/125] INtroducing the LODEnabled flag on the render item in order prevent LOD to hide important objects regardless of the LOD level. --- interface/src/ui/overlays/ModelOverlay.cpp | 22 ++++++++++++++++++++++ interface/src/ui/overlays/ModelOverlay.h | 3 +++ libraries/render-utils/src/Model.cpp | 11 +++++++++++ libraries/render-utils/src/Model.h | 3 +++ libraries/render/src/render/CullTask.cpp | 6 +++--- libraries/render/src/render/Item.h | 9 +++++++++ scripts/system/libraries/WebTablet.js | 3 ++- 7 files changed, 53 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index eee8222051..2379685252 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -127,6 +127,11 @@ void ModelOverlay::update(float deltatime) { _model->setGroupCulled(_isGroupCulled, scene); metaDirty = true; } + if (_lodEnabledDirty) { + _lodEnabledDirty = false; + _model->setLODEnabled(_isLODEnabled, scene); + metaDirty = true; + } if (metaDirty) { transaction.updateItem(getRenderItemID(), [](Overlay& data) {}); } @@ -191,6 +196,14 @@ void ModelOverlay::setGroupCulled(bool groupCulled) { } } +void ModelOverlay::setLODEnabled(bool lodEnabled) { + if (lodEnabled != _isLODEnabled) { + _isLODEnabled = lodEnabled; + _lodEnabledDirty = true; + } +} + + void ModelOverlay::setProperties(const QVariantMap& properties) { auto origPosition = getWorldPosition(); auto origRotation = getWorldOrientation(); @@ -248,6 +261,12 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { setGroupCulled(groupCulledValue.toBool()); } + auto lodEnabledValue = properties["isLODEnabled"]; + if (lodEnabledValue.isValid() && lodEnabledValue.canConvert(QVariant::Bool)) { + setLODEnabled(lodEnabledValue.toBool()); + } + + // jointNames is read-only. // jointPositions is read-only. // jointOrientations is read-only. @@ -765,5 +784,8 @@ render::ItemKey ModelOverlay::getKey() { if (_isGroupCulled) { builder.withMetaCullGroup(); } + if (!_isLODEnabled) { + builder.withLODDisabled(); + } return builder.build(); } \ No newline at end of file diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index bd922e258a..dc127afe48 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -69,6 +69,7 @@ public: void setDrawInFront(bool drawInFront) override; void setDrawHUDLayer(bool drawHUDLayer) override; void setGroupCulled(bool groupCulled); + void setLODEnabled(bool lodEnabled); void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override; void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override; @@ -130,6 +131,8 @@ private: bool _drawInHUDDirty { false }; bool _isGroupCulled { false }; bool _groupCulledDirty { false }; + bool _isLODEnabled{ true }; + bool _lodEnabledDirty{ true }; void processMaterials(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a700c200f8..8b50f2e420 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -973,6 +973,17 @@ bool Model::isGroupCulled() const { return _renderItemKeyGlobalFlags.isSubMetaCulled(); } +void Model::setLODEnabled(bool isLODEnabled, const render::ScenePointer& scene) { + if (Model::isLODEnabled() != isLODEnabled) { + auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags); + _renderItemKeyGlobalFlags = (isLODEnabled ? keyBuilder.withLODEnabled() : keyBuilder.withLODDisabled()); + updateRenderItemsKey(scene); + } +} +bool Model::isLODEnabled() const { + return _renderItemKeyGlobalFlags.isLODEnabled(); +} + const render::ItemKey Model::getRenderItemKeyGlobalFlags() const { return _renderItemKeyGlobalFlags; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index e42da4ecb1..954cc57e0b 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -119,6 +119,9 @@ public: bool isGroupCulled() const; void setGroupCulled(bool isGroupCulled, const render::ScenePointer& scene = nullptr); + bool isLODEnabled() const; + void setLODEnabled(bool isLODEnabled, const render::ScenePointer& scene = nullptr); + bool canCastShadow() const; void setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene = nullptr); diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index b2930032a3..ad9f6b7076 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -205,7 +205,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, if (!srcFilter.selectsNothing()) { auto filter = render::ItemFilter::Builder(srcFilter).withoutSubMetaCulled().build(); - // Now get the bound, and + // Now get the bound, and // filter individually against the _filter // visibility cull if partially selected ( octree cell contianing it was partial) // distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item) @@ -294,7 +294,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, auto& item = scene->getItem(id); if (filter.test(item.getKey())) { ItemBound itemBound(id, item.getBound()); - if (test.solidAngleTest(itemBound.bound)) { + if (item.getKey().isLODDisabled() || test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { item.fetchMetaSubItemBounds(outItems, (*scene)); @@ -329,7 +329,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, if (filter.test(item.getKey())) { ItemBound itemBound(id, item.getBound()); if (test.frustumTest(itemBound.bound)) { - if (test.solidAngleTest(itemBound.bound)) { + if (item.getKey().isLODDisabled() || test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { item.fetchMetaSubItemBounds(outItems, (*scene)); diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 88e85c604a..a30067cb5c 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -101,6 +101,7 @@ public: SHADOW_CASTER, // Item cast shadows META_CULL_GROUP, // As a meta item, the culling of my sub items is based solely on my bounding box and my visibility in the view SUB_META_CULLED, // As a sub item of a meta render item set as cull group, need to be set to my culling to the meta render it + LOD_DISABLED, // Item LOD behavior is disabled, it won't be LODed because of lod behavior in the view FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against LAST_TAG_BIT = FIRST_TAG_BIT + NUM_TAGS, @@ -160,6 +161,8 @@ public: Builder& withoutMetaCullGroup() { _flags.reset(META_CULL_GROUP); return (*this); } Builder& withSubMetaCulled() { _flags.set(SUB_META_CULLED); return (*this); } Builder& withoutSubMetaCulled() { _flags.reset(SUB_META_CULLED); return (*this); } + Builder& withLODDisabled() { _flags.set(LOD_DISABLED); return (*this); } + Builder& withLODEnabled() { _flags.reset(LOD_DISABLED); return (*this); } Builder& withTag(Tag tag) { _flags.set(FIRST_TAG_BIT + tag); return (*this); } // Set ALL the tags in one call using the Tag bits @@ -203,6 +206,9 @@ public: bool isSubMetaCulled() const { return _flags[SUB_META_CULLED]; } void setSubMetaCulled(bool metaCulled) { (metaCulled ? _flags.set(SUB_META_CULLED) : _flags.reset(SUB_META_CULLED)); } + bool isLODEnabled() const { return !_flags[LOD_DISABLED]; } + bool isLODDisabled() const { return _flags[LOD_DISABLED]; } + bool isTag(Tag tag) const { return _flags[FIRST_TAG_BIT + tag]; } uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); } @@ -274,6 +280,9 @@ public: Builder& withoutSubMetaCulled() { _value.reset(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); } Builder& withSubMetaCulled() { _value.set(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); } + Builder& withLODEnabled() { _value.reset(ItemKey::LOD_DISABLED); _mask.set(ItemKey::LOD_DISABLED); return (*this); } + Builder& withLODDisabled() { _value.set(ItemKey::LOD_DISABLED); _mask.set(ItemKey::LOD_DISABLED); return (*this); } + Builder& withoutTag(ItemKey::Tag tagIndex) { _value.reset(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); } Builder& withTag(ItemKey::Tag tagIndex) { _value.set(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); } // Set ALL the tags in one call using the Tag bits and the Tag bits touched diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index c201a251d0..0d13874e4c 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -135,7 +135,8 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth }, parentID: MyAvatar.SELF_ID, visible: visible, - isGroupCulled: true + isGroupCulled: true, + isLODEnabled: false }; // compute position, rotation & parentJointIndex of the tablet From 7de784ce27d6c9d41e3847e74caf778d90b71c87 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 10 Oct 2018 12:32:55 -0700 Subject: [PATCH 002/125] First steps (definitely not working) --- assignment-client/src/Agent.cpp | 2 +- interface/src/Application.cpp | 14 ++- interface/src/assets/ATPAssetMigrator.cpp | 4 +- .../scripting/SpeechScriptingInterface.cpp | 96 +++++++++++++++++++ .../src/scripting/SpeechScriptingInterface.h | 76 +++++++++++++++ .../src/RenderableWebEntityItem.cpp | 2 +- libraries/entities/src/EntityEditFilters.cpp | 2 +- .../src/model-networking/TextureCache.cpp | 4 +- libraries/networking/src/AddressManager.cpp | 10 +- libraries/networking/src/DomainHandler.cpp | 2 +- .../networking/src/NetworkingConstants.h | 10 +- libraries/networking/src/ResourceCache.cpp | 2 +- libraries/networking/src/ResourceManager.cpp | 18 ++-- 13 files changed, 209 insertions(+), 33 deletions(-) create mode 100644 interface/src/scripting/SpeechScriptingInterface.cpp create mode 100644 interface/src/scripting/SpeechScriptingInterface.h diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 639e9f924b..ee21fff8c0 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -216,7 +216,7 @@ void Agent::requestScript() { } // make sure this is not a script request for the file scheme - if (scriptURL.scheme() == URL_SCHEME_FILE) { + if (scriptURL.scheme() == HIFI_URL_SCHEME_FILE) { qWarning() << "Cannot load script for Agent from local filesystem."; scriptRequestFinished(); return; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index aa2b382c58..74532ef53a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -182,6 +182,7 @@ #include "scripting/RatesScriptingInterface.h" #include "scripting/SelectionScriptingInterface.h" #include "scripting/WalletScriptingInterface.h" +#include "scripting/SpeechScriptingInterface.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif @@ -528,11 +529,11 @@ bool isDomainURL(QUrl url) { if (url.scheme() == URL_SCHEME_HIFI) { return true; } - if (url.scheme() != URL_SCHEME_FILE) { + if (url.scheme() != HIFI_URL_SCHEME_FILE) { // TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can // be loaded over http(s) - // && url.scheme() != URL_SCHEME_HTTP && - // url.scheme() != URL_SCHEME_HTTPS + // && url.scheme() != HIFI_URL_SCHEME_HTTP && + // url.scheme() != HIFI_URL_SCHEME_HTTPS return false; } if (url.path().endsWith(".json", Qt::CaseInsensitive) || @@ -943,6 +944,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); @@ -1024,8 +1026,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // If the URL scheme is http(s) or ftp, then use as is, else - treat it as a local file // This is done so as not break previous command line scripts - if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP || - testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) { + if (testScriptPath.left(HIFI_URL_SCHEME_HTTP.length()) == HIFI_URL_SCHEME_HTTP || + testScriptPath.left(HIFI_URL_SCHEME_FTP.length()) == HIFI_URL_SCHEME_FTP) { setProperty(hifi::properties::TEST, QUrl::fromUserInput(testScriptPath)); } else if (QFileInfo(testScriptPath).exists()) { @@ -3127,6 +3129,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get().data()); surfaceContext->setContextProperty("Wallet", DependencyManager::get().data()); surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); + surfaceContext->setContextProperty("Speech", DependencyManager::get().data()); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); @@ -6797,6 +6800,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("Wallet", DependencyManager::get().data()); scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get().data()); scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); + scriptEngine->registerGlobalObject("Speech", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue); diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 45ac80b054..423a4b8509 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -121,8 +121,8 @@ void ATPAssetMigrator::loadEntityServerFile() { QUrl migrationURL = QUrl(migrationURLString); if (!_ignoredUrls.contains(migrationURL) - && (migrationURL.scheme() == URL_SCHEME_HTTP || migrationURL.scheme() == URL_SCHEME_HTTPS - || migrationURL.scheme() == URL_SCHEME_FILE || migrationURL.scheme() == URL_SCHEME_FTP)) { + && (migrationURL.scheme() == HIFI_URL_SCHEME_HTTP || migrationURL.scheme() == HIFI_URL_SCHEME_HTTPS + || migrationURL.scheme() == HIFI_URL_SCHEME_FILE || migrationURL.scheme() == HIFI_URL_SCHEME_FTP)) { if (_pendingReplacements.contains(migrationURL)) { // we already have a request out for this asset, just store the QJsonValueRef diff --git a/interface/src/scripting/SpeechScriptingInterface.cpp b/interface/src/scripting/SpeechScriptingInterface.cpp new file mode 100644 index 0000000000..a38e1aa824 --- /dev/null +++ b/interface/src/scripting/SpeechScriptingInterface.cpp @@ -0,0 +1,96 @@ +// +// SpeechScriptingInterface.cpp +// interface/src/scripting +// +// Created by Zach Fox on 2018-10-10. +// Copyright 2018 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 "SpeechScriptingInterface.h" +#include "avatar/AvatarManager.h" +#include + +SpeechScriptingInterface::SpeechScriptingInterface() { + // + // Create text to speech engine + // + HRESULT hr = m_tts.CoCreateInstance(CLSID_SpVoice); + if (FAILED(hr)) { + ATLTRACE(TEXT("Text-to-speech creation failed.\n")); + AtlThrow(hr); + } + + // + // Get token corresponding to default voice + // + hr = SpGetDefaultTokenFromCategoryId(SPCAT_VOICES, &m_voiceToken, FALSE); + if (FAILED(hr)) { + ATLTRACE(TEXT("Can't get default voice token.\n")); + AtlThrow(hr); + } + + // + // Set default voice + // + hr = m_tts->SetVoice(m_voiceToken); + if (FAILED(hr)) { + ATLTRACE(TEXT("Can't set default voice.\n")); + AtlThrow(hr); + } + + WAVEFORMATEX fmt; + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nSamplesPerSec = 48000; + fmt.wBitsPerSample = 16; + fmt.nChannels = 1; + fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; + fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; + fmt.cbSize = 0; + + BYTE* pcontent = new BYTE[1024 * 1000]; + + cpIStream = SHCreateMemStream(NULL, 0); + hr = outputStream->SetBaseStream(cpIStream, SPDFID_WaveFormatEx, &fmt); + + hr = m_tts->SetOutput(outputStream, true); + if (FAILED(hr)) { + ATLTRACE(TEXT("Can't set output stream.\n")); + AtlThrow(hr); + } +} + +SpeechScriptingInterface::~SpeechScriptingInterface() { + +} + +void SpeechScriptingInterface::speakText(const QString& textToSpeak) { + ULONG streamNumber; + HRESULT hr = m_tts->Speak(reinterpret_cast(textToSpeak.utf16()), + SPF_IS_NOT_XML | SPF_ASYNC | SPF_PURGEBEFORESPEAK, + &streamNumber); + if (FAILED(hr)) { + ATLTRACE(TEXT("Speak failed.\n")); + AtlThrow(hr); + } + + m_tts->WaitUntilDone(-1); + + outputStream->GetBaseStream(&cpIStream); + ULARGE_INTEGER StreamSize; + StreamSize.LowPart = 0; + hr = IStream_Size(cpIStream, &StreamSize); + + DWORD dwSize = StreamSize.QuadPart; + char* buf1 = new char[dwSize + 1]; + hr = IStream_Read(cpIStream, buf1, dwSize); + + QByteArray byteArray = QByteArray::QByteArray(buf1, (int)dwSize); + AudioInjectorOptions options; + + options.position = DependencyManager::get()->getMyAvatarPosition(); + + AudioInjector::playSound(byteArray, options); +} diff --git a/interface/src/scripting/SpeechScriptingInterface.h b/interface/src/scripting/SpeechScriptingInterface.h new file mode 100644 index 0000000000..311bd80605 --- /dev/null +++ b/interface/src/scripting/SpeechScriptingInterface.h @@ -0,0 +1,76 @@ +// SpeechScriptingInterface.h +// interface/src/scripting +// +// Created by Zach Fox on 2018-10-10. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_SpeechScriptingInterface_h +#define hifi_SpeechScriptingInterface_h + +#include +#include + +#include // SAPI +#include // SAPI Helper + +class SpeechScriptingInterface : public QObject, public Dependency { + Q_OBJECT + +public: + SpeechScriptingInterface(); + ~SpeechScriptingInterface(); + + Q_INVOKABLE void speakText(const QString& textToSpeak); + +private: + + class CComAutoInit { + public: + // Initializes COM using CoInitialize. + // On failure, signals error using AtlThrow. + CComAutoInit() { + HRESULT hr = ::CoInitialize(NULL); + if (FAILED(hr)) { + ATLTRACE(TEXT("CoInitialize() failed in CComAutoInit constructor (hr=0x%08X).\n"), hr); + AtlThrow(hr); + } + } + + // Initializes COM using CoInitializeEx. + // On failure, signals error using AtlThrow. + explicit CComAutoInit(__in DWORD dwCoInit) { + HRESULT hr = ::CoInitializeEx(NULL, dwCoInit); + if (FAILED(hr)) { + ATLTRACE(TEXT("CoInitializeEx() failed in CComAutoInit constructor (hr=0x%08X).\n"), hr); + AtlThrow(hr); + } + } + + // Uninitializes COM using CoUninitialize. + ~CComAutoInit() { ::CoUninitialize(); } + + // + // Ban copy + // + private: + CComAutoInit(const CComAutoInit&); + }; + + // COM initialization and cleanup (must precede other COM related data members) + CComAutoInit m_comInit; + + // Text to speech engine + CComPtr m_tts; + + // Default voice token + CComPtr m_voiceToken; + + CComPtr outputStream; + CComPtr cpIStream; +}; + +#endif // hifi_SpeechScriptingInterface_h diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index bc9ac84c91..ac5e43e558 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -54,7 +54,7 @@ WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString& const QUrl url(urlString); auto scheme = url.scheme(); - if (scheme == URL_SCHEME_ABOUT || scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || + if (scheme == HIFI_URL_SCHEME_ABOUT || scheme == HIFI_URL_SCHEME_HTTP || scheme == HIFI_URL_SCHEME_HTTPS || urlString.toLower().endsWith(".htm") || urlString.toLower().endsWith(".html")) { return ContentType::HtmlContent; } diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index 94df7eb465..9a3f056e04 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -183,7 +183,7 @@ void EntityEditFilters::addFilter(EntityItemID entityID, QString filterURL) { } // The following should be abstracted out for use in Agent.cpp (and maybe later AvatarMixer.cpp) - if (scriptURL.scheme().isEmpty() || (scriptURL.scheme() == URL_SCHEME_FILE)) { + if (scriptURL.scheme().isEmpty() || (scriptURL.scheme() == HIFI_URL_SCHEME_FILE)) { qWarning() << "Cannot load script from local filesystem, because assignment may be on a different computer."; scriptRequestFinished(entityID); return; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index e8aec5e60e..11a5b2f167 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -329,7 +329,7 @@ _maxNumPixels(100) static bool isLocalUrl(const QUrl& url) { auto scheme = url.scheme(); - return (scheme == URL_SCHEME_FILE || scheme == URL_SCHEME_QRC || scheme == RESOURCE_SCHEME); + return (scheme == HIFI_URL_SCHEME_FILE || scheme == URL_SCHEME_QRC || scheme == RESOURCE_SCHEME); } NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) : @@ -502,7 +502,7 @@ void NetworkTexture::handleLocalRequestCompleted() { void NetworkTexture::makeLocalRequest() { const QString scheme = _activeUrl.scheme(); QString path; - if (scheme == URL_SCHEME_FILE) { + if (scheme == HIFI_URL_SCHEME_FILE) { path = PathUtils::expandToLocalDataAbsolutePath(_activeUrl).toLocalFile(); } else { path = ":" + _activeUrl.path(); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index f8ab8ceaec..e6957728e8 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -155,12 +155,12 @@ void AddressManager::goForward() { void AddressManager::storeCurrentAddress() { auto url = currentAddress(); - if (url.scheme() == URL_SCHEME_FILE || + if (url.scheme() == HIFI_URL_SCHEME_FILE || (url.scheme() == URL_SCHEME_HIFI && !url.host().isEmpty())) { // TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can // be loaded over http(s) - // url.scheme() == URL_SCHEME_HTTP || - // url.scheme() == URL_SCHEME_HTTPS || + // url.scheme() == HIFI_URL_SCHEME_HTTP || + // url.scheme() == HIFI_URL_SCHEME_HTTPS || bool isInErrorState = DependencyManager::get()->getDomainHandler().isInErrorState(); if (isConnected()) { if (isInErrorState) { @@ -331,11 +331,11 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { emit lookupResultsFinished(); return true; - } else if (lookupUrl.scheme() == URL_SCHEME_FILE) { + } else if (lookupUrl.scheme() == HIFI_URL_SCHEME_FILE) { // TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can // be loaded over http(s) // lookupUrl.scheme() == URL_SCHEME_HTTP || - // lookupUrl.scheme() == URL_SCHEME_HTTPS || + // lookupUrl.scheme() == HIFI_URL_SCHEME_HTTPS || // TODO once a file can return a connection refusal if there were to be some kind of load error, we'd // need to store the previous domain tried in _lastVisitedURL. For now , do not store it. diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 615546b410..3dda182989 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -194,7 +194,7 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) { _sockAddr.clear(); // if this is a file URL we need to see if it has a ~ for us to expand - if (domainURL.scheme() == URL_SCHEME_FILE) { + if (domainURL.scheme() == HIFI_URL_SCHEME_FILE) { domainURL = PathUtils::expandToLocalDataAbsolutePath(domainURL); } } diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 839e269fd4..302e0efa02 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -30,14 +30,14 @@ namespace NetworkingConstants { QUrl METAVERSE_SERVER_URL(); } -const QString URL_SCHEME_ABOUT = "about"; +const QString HIFI_URL_SCHEME_ABOUT = "about"; const QString URL_SCHEME_HIFI = "hifi"; const QString URL_SCHEME_HIFIAPP = "hifiapp"; const QString URL_SCHEME_QRC = "qrc"; -const QString URL_SCHEME_FILE = "file"; -const QString URL_SCHEME_HTTP = "http"; -const QString URL_SCHEME_HTTPS = "https"; -const QString URL_SCHEME_FTP = "ftp"; +const QString HIFI_URL_SCHEME_FILE = "file"; +const QString HIFI_URL_SCHEME_HTTP = "http"; +const QString HIFI_URL_SCHEME_HTTPS = "https"; +const QString HIFI_URL_SCHEME_FTP = "ftp"; const QString URL_SCHEME_ATP = "atp"; #endif // hifi_NetworkingConstants_h diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index aed9f3b0e5..1328606be4 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -114,7 +114,7 @@ QSharedPointer ResourceCacheSharedItems::getHighestPendingRequest() { // Check load priority float priority = resource->getLoadPriority(); - bool isFile = resource->getURL().scheme() == URL_SCHEME_FILE; + bool isFile = resource->getURL().scheme() == HIFI_URL_SCHEME_FILE; if (priority >= highestPriority && (isFile || !currentHighestIsFile)) { highestPriority = priority; highestIndex = i; diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index 553f0d0a61..40d6570f48 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -82,10 +82,10 @@ const QSet& getKnownUrls() { static std::once_flag once; std::call_once(once, [] { knownUrls.insert(URL_SCHEME_QRC); - knownUrls.insert(URL_SCHEME_FILE); - knownUrls.insert(URL_SCHEME_HTTP); - knownUrls.insert(URL_SCHEME_HTTPS); - knownUrls.insert(URL_SCHEME_FTP); + knownUrls.insert(HIFI_URL_SCHEME_FILE); + knownUrls.insert(HIFI_URL_SCHEME_HTTP); + knownUrls.insert(HIFI_URL_SCHEME_HTTPS); + knownUrls.insert(HIFI_URL_SCHEME_FTP); knownUrls.insert(URL_SCHEME_ATP); }); return knownUrls; @@ -97,7 +97,7 @@ QUrl ResourceManager::normalizeURL(const QUrl& originalUrl) { if (!getKnownUrls().contains(scheme)) { // check the degenerative file case: on windows we can often have urls of the form c:/filename // this checks for and works around that case. - QUrl urlWithFileScheme{ URL_SCHEME_FILE + ":///" + url.toString() }; + QUrl urlWithFileScheme{ HIFI_URL_SCHEME_FILE + ":///" + url.toString() }; if (!urlWithFileScheme.toLocalFile().isEmpty()) { return urlWithFileScheme; } @@ -118,9 +118,9 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q ResourceRequest* request = nullptr; - if (scheme == URL_SCHEME_FILE || scheme == URL_SCHEME_QRC) { + if (scheme == HIFI_URL_SCHEME_FILE || scheme == URL_SCHEME_QRC) { request = new FileResourceRequest(normalizedURL); - } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { + } else if (scheme == HIFI_URL_SCHEME_HTTP || scheme == HIFI_URL_SCHEME_HTTPS || scheme == HIFI_URL_SCHEME_FTP) { request = new HTTPResourceRequest(normalizedURL); } else if (scheme == URL_SCHEME_ATP) { if (!_atpSupportEnabled) { @@ -143,10 +143,10 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q bool ResourceManager::resourceExists(const QUrl& url) { auto scheme = url.scheme(); - if (scheme == URL_SCHEME_FILE) { + if (scheme == HIFI_URL_SCHEME_FILE) { QFileInfo file{ url.toString() }; return file.exists(); - } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { + } else if (scheme == HIFI_URL_SCHEME_HTTP || scheme == HIFI_URL_SCHEME_HTTPS || scheme == HIFI_URL_SCHEME_FTP) { auto& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request{ url }; From f1446532d02eca2ee160e83bb5d2b122a1b48d38 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 10 Oct 2018 16:13:23 -0700 Subject: [PATCH 003/125] Almost working --- .../scripting/SpeechScriptingInterface.cpp | 106 ++++++++++++------ .../src/scripting/SpeechScriptingInterface.h | 7 +- 2 files changed, 75 insertions(+), 38 deletions(-) diff --git a/interface/src/scripting/SpeechScriptingInterface.cpp b/interface/src/scripting/SpeechScriptingInterface.cpp index a38e1aa824..b9c7718075 100644 --- a/interface/src/scripting/SpeechScriptingInterface.cpp +++ b/interface/src/scripting/SpeechScriptingInterface.cpp @@ -19,8 +19,7 @@ SpeechScriptingInterface::SpeechScriptingInterface() { // HRESULT hr = m_tts.CoCreateInstance(CLSID_SpVoice); if (FAILED(hr)) { - ATLTRACE(TEXT("Text-to-speech creation failed.\n")); - AtlThrow(hr); + qDebug() << "Text-to-speech engine creation failed."; } // @@ -28,8 +27,7 @@ SpeechScriptingInterface::SpeechScriptingInterface() { // hr = SpGetDefaultTokenFromCategoryId(SPCAT_VOICES, &m_voiceToken, FALSE); if (FAILED(hr)) { - ATLTRACE(TEXT("Can't get default voice token.\n")); - AtlThrow(hr); + qDebug() << "Can't get default voice token."; } // @@ -37,28 +35,7 @@ SpeechScriptingInterface::SpeechScriptingInterface() { // hr = m_tts->SetVoice(m_voiceToken); if (FAILED(hr)) { - ATLTRACE(TEXT("Can't set default voice.\n")); - AtlThrow(hr); - } - - WAVEFORMATEX fmt; - fmt.wFormatTag = WAVE_FORMAT_PCM; - fmt.nSamplesPerSec = 48000; - fmt.wBitsPerSample = 16; - fmt.nChannels = 1; - fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; - fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; - fmt.cbSize = 0; - - BYTE* pcontent = new BYTE[1024 * 1000]; - - cpIStream = SHCreateMemStream(NULL, 0); - hr = outputStream->SetBaseStream(cpIStream, SPDFID_WaveFormatEx, &fmt); - - hr = m_tts->SetOutput(outputStream, true); - if (FAILED(hr)) { - ATLTRACE(TEXT("Can't set output stream.\n")); - AtlThrow(hr); + qDebug() << "Can't set default voice."; } } @@ -66,30 +43,91 @@ SpeechScriptingInterface::~SpeechScriptingInterface() { } +class ReleaseOnExit { +public: + ReleaseOnExit(IUnknown* p) : m_p(p) {} + ~ReleaseOnExit() { + if (m_p) { + m_p->Release(); + } + } + +private: + IUnknown* m_p; +}; + void SpeechScriptingInterface::speakText(const QString& textToSpeak) { + WAVEFORMATEX fmt; + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nSamplesPerSec = 44100; + fmt.wBitsPerSample = 16; + fmt.nChannels = 1; + fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; + fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; + fmt.cbSize = 0; + + IStream* pStream = NULL; + + ISpStream* pSpStream = nullptr; + HRESULT hr = CoCreateInstance(CLSID_SpStream, nullptr, CLSCTX_ALL, __uuidof(ISpStream), (void**)&pSpStream); + if (FAILED(hr)) { + qDebug() << "CoCreateInstance failed."; + } + ReleaseOnExit rSpStream(pSpStream); + + pStream = SHCreateMemStream(NULL, 0); + if (nullptr == pStream) { + qDebug() << "SHCreateMemStream failed."; + } + + hr = pSpStream->SetBaseStream(pStream, SPDFID_WaveFormatEx, &fmt); + if (FAILED(hr)) { + qDebug() << "Can't set base stream."; + } + + hr = m_tts->SetOutput(pSpStream, true); + if (FAILED(hr)) { + qDebug() << "Can't set output stream."; + } + + ReleaseOnExit rStream(pStream); + ULONG streamNumber; - HRESULT hr = m_tts->Speak(reinterpret_cast(textToSpeak.utf16()), + hr = m_tts->Speak(reinterpret_cast(textToSpeak.utf16()), SPF_IS_NOT_XML | SPF_ASYNC | SPF_PURGEBEFORESPEAK, &streamNumber); if (FAILED(hr)) { - ATLTRACE(TEXT("Speak failed.\n")); - AtlThrow(hr); + qDebug() << "Speak failed."; } m_tts->WaitUntilDone(-1); - outputStream->GetBaseStream(&cpIStream); + hr = pSpStream->GetBaseStream(&pStream); + if (FAILED(hr)) { + qDebug() << "Couldn't get base stream."; + } + + hr = IStream_Reset(pStream); + if (FAILED(hr)) { + qDebug() << "Couldn't reset stream."; + } + ULARGE_INTEGER StreamSize; StreamSize.LowPart = 0; - hr = IStream_Size(cpIStream, &StreamSize); + hr = IStream_Size(pStream, &StreamSize); DWORD dwSize = StreamSize.QuadPart; char* buf1 = new char[dwSize + 1]; - hr = IStream_Read(cpIStream, buf1, dwSize); + memset(buf1, 0, dwSize + 1); + + hr = IStream_Read(pStream, buf1, dwSize); + if (FAILED(hr)) { + qDebug() << "Couldn't read from stream."; + } + + QByteArray byteArray = QByteArray::QByteArray(buf1, dwSize); - QByteArray byteArray = QByteArray::QByteArray(buf1, (int)dwSize); AudioInjectorOptions options; - options.position = DependencyManager::get()->getMyAvatarPosition(); AudioInjector::playSound(byteArray, options); diff --git a/interface/src/scripting/SpeechScriptingInterface.h b/interface/src/scripting/SpeechScriptingInterface.h index 311bd80605..ad6777e339 100644 --- a/interface/src/scripting/SpeechScriptingInterface.h +++ b/interface/src/scripting/SpeechScriptingInterface.h @@ -13,7 +13,9 @@ #include #include - +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif #include // SAPI #include // SAPI Helper @@ -68,9 +70,6 @@ private: // Default voice token CComPtr m_voiceToken; - - CComPtr outputStream; - CComPtr cpIStream; }; #endif // hifi_SpeechScriptingInterface_h From d8c9712dd2cfb404878eb830f7060015fbfc37c6 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 10 Oct 2018 16:19:11 -0700 Subject: [PATCH 004/125] It's working! --- interface/src/scripting/SpeechScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/SpeechScriptingInterface.cpp b/interface/src/scripting/SpeechScriptingInterface.cpp index b9c7718075..3b3ecf728d 100644 --- a/interface/src/scripting/SpeechScriptingInterface.cpp +++ b/interface/src/scripting/SpeechScriptingInterface.cpp @@ -59,7 +59,7 @@ private: void SpeechScriptingInterface::speakText(const QString& textToSpeak) { WAVEFORMATEX fmt; fmt.wFormatTag = WAVE_FORMAT_PCM; - fmt.nSamplesPerSec = 44100; + fmt.nSamplesPerSec = 24000; fmt.wBitsPerSample = 16; fmt.nChannels = 1; fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; From 5d4de3d3b0130aa7c6b00c5aa83fe12af5d808af Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 10 Oct 2018 17:36:38 -0700 Subject: [PATCH 005/125] I love it. --- interface/src/scripting/SpeechScriptingInterface.cpp | 9 ++++++--- interface/src/scripting/SpeechScriptingInterface.h | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/interface/src/scripting/SpeechScriptingInterface.cpp b/interface/src/scripting/SpeechScriptingInterface.cpp index 3b3ecf728d..b8e0f5c3e8 100644 --- a/interface/src/scripting/SpeechScriptingInterface.cpp +++ b/interface/src/scripting/SpeechScriptingInterface.cpp @@ -11,7 +11,6 @@ #include "SpeechScriptingInterface.h" #include "avatar/AvatarManager.h" -#include SpeechScriptingInterface::SpeechScriptingInterface() { // @@ -94,7 +93,7 @@ void SpeechScriptingInterface::speakText(const QString& textToSpeak) { ULONG streamNumber; hr = m_tts->Speak(reinterpret_cast(textToSpeak.utf16()), - SPF_IS_NOT_XML | SPF_ASYNC | SPF_PURGEBEFORESPEAK, + SPF_IS_XML | SPF_ASYNC | SPF_PURGEBEFORESPEAK, &streamNumber); if (FAILED(hr)) { qDebug() << "Speak failed."; @@ -130,5 +129,9 @@ void SpeechScriptingInterface::speakText(const QString& textToSpeak) { AudioInjectorOptions options; options.position = DependencyManager::get()->getMyAvatarPosition(); - AudioInjector::playSound(byteArray, options); + lastSound = AudioInjector::playSound(byteArray, options); +} + +void SpeechScriptingInterface::stopLastSpeech() { + lastSound->stop(); } diff --git a/interface/src/scripting/SpeechScriptingInterface.h b/interface/src/scripting/SpeechScriptingInterface.h index ad6777e339..c683a1a3c6 100644 --- a/interface/src/scripting/SpeechScriptingInterface.h +++ b/interface/src/scripting/SpeechScriptingInterface.h @@ -18,6 +18,7 @@ #endif #include // SAPI #include // SAPI Helper +#include class SpeechScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -27,9 +28,9 @@ public: ~SpeechScriptingInterface(); Q_INVOKABLE void speakText(const QString& textToSpeak); + Q_INVOKABLE void stopLastSpeech(); private: - class CComAutoInit { public: // Initializes COM using CoInitialize. @@ -70,6 +71,8 @@ private: // Default voice token CComPtr m_voiceToken; + + AudioInjectorPointer lastSound; }; #endif // hifi_SpeechScriptingInterface_h From daeedc6ef1ee64472d66ef52decf4d6380c210f9 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 11 Oct 2018 17:10:14 -0700 Subject: [PATCH 006/125] Lots of progress today --- interface/src/Application.cpp | 11 ++- ...nterface.cpp => TTSScriptingInterface.cpp} | 58 ++++++++---- ...ingInterface.h => TTSScriptingInterface.h} | 19 ++-- libraries/audio-client/src/AudioClient.cpp | 88 ++++++++++--------- libraries/audio-client/src/AudioClient.h | 3 + 5 files changed, 111 insertions(+), 68 deletions(-) rename interface/src/scripting/{SpeechScriptingInterface.cpp => TTSScriptingInterface.cpp} (64%) rename interface/src/scripting/{SpeechScriptingInterface.h => TTSScriptingInterface.h} (79%) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 74532ef53a..728fea8c10 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -182,7 +182,7 @@ #include "scripting/RatesScriptingInterface.h" #include "scripting/SelectionScriptingInterface.h" #include "scripting/WalletScriptingInterface.h" -#include "scripting/SpeechScriptingInterface.h" +#include "scripting/TTSScriptingInterface.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif @@ -944,7 +944,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); @@ -1179,6 +1179,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [&audioIO](recording::Frame::ConstPointer frame) { audioIO->handleRecordedAudioInput(frame->data); }); + + auto TTS = DependencyManager::get().data(); + connect(TTS, &TTSScriptingInterface::ttsSampleCreated, audioIO, &AudioClient::handleTTSAudioInput); connect(audioIO, &AudioClient::inputReceived, [](const QByteArray& audio) { static auto recorder = DependencyManager::get(); @@ -3129,7 +3132,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get().data()); surfaceContext->setContextProperty("Wallet", DependencyManager::get().data()); surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); - surfaceContext->setContextProperty("Speech", DependencyManager::get().data()); + surfaceContext->setContextProperty("TextToSpeech", DependencyManager::get().data()); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); @@ -6800,7 +6803,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("Wallet", DependencyManager::get().data()); scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get().data()); scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); - scriptEngine->registerGlobalObject("Speech", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("TextToSpeech", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue); diff --git a/interface/src/scripting/SpeechScriptingInterface.cpp b/interface/src/scripting/TTSScriptingInterface.cpp similarity index 64% rename from interface/src/scripting/SpeechScriptingInterface.cpp rename to interface/src/scripting/TTSScriptingInterface.cpp index b8e0f5c3e8..fdbb37e586 100644 --- a/interface/src/scripting/SpeechScriptingInterface.cpp +++ b/interface/src/scripting/TTSScriptingInterface.cpp @@ -1,6 +1,6 @@ // -// SpeechScriptingInterface.cpp -// interface/src/scripting +// TTSScriptingInterface.cpp +// libraries/audio-client/src/scripting // // Created by Zach Fox on 2018-10-10. // Copyright 2018 High Fidelity, Inc. @@ -9,10 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "SpeechScriptingInterface.h" +#include "TTSScriptingInterface.h" #include "avatar/AvatarManager.h" -SpeechScriptingInterface::SpeechScriptingInterface() { +TTSScriptingInterface::TTSScriptingInterface() { // // Create text to speech engine // @@ -38,8 +38,7 @@ SpeechScriptingInterface::SpeechScriptingInterface() { } } -SpeechScriptingInterface::~SpeechScriptingInterface() { - +TTSScriptingInterface::~TTSScriptingInterface() { } class ReleaseOnExit { @@ -55,7 +54,28 @@ private: IUnknown* m_p; }; -void SpeechScriptingInterface::speakText(const QString& textToSpeak) { +void TTSScriptingInterface::testTone(const bool& alsoInject) { + QByteArray byteArray(480000, 0); + _lastSoundByteArray.resize(0); + _lastSoundByteArray.resize(480000); + + int32_t a = 0; + int16_t* samples = reinterpret_cast(byteArray.data()); + for (a = 0; a < 240000; a++) { + int16_t temp = (glm::sin(glm::radians((float)a))) * 32768; + samples[a] = temp; + } + emit ttsSampleCreated(_lastSoundByteArray); + + if (alsoInject) { + AudioInjectorOptions options; + options.position = DependencyManager::get()->getMyAvatarPosition(); + + _lastSoundAudioInjector = AudioInjector::playSound(_lastSoundByteArray, options); + } +} + +void TTSScriptingInterface::speakText(const QString& textToSpeak, const bool& alsoInject) { WAVEFORMATEX fmt; fmt.wFormatTag = WAVE_FORMAT_PCM; fmt.nSamplesPerSec = 24000; @@ -92,9 +112,8 @@ void SpeechScriptingInterface::speakText(const QString& textToSpeak) { ReleaseOnExit rStream(pStream); ULONG streamNumber; - hr = m_tts->Speak(reinterpret_cast(textToSpeak.utf16()), - SPF_IS_XML | SPF_ASYNC | SPF_PURGEBEFORESPEAK, - &streamNumber); + hr = m_tts->Speak(reinterpret_cast(textToSpeak.utf16()), SPF_IS_XML | SPF_ASYNC | SPF_PURGEBEFORESPEAK, + &streamNumber); if (FAILED(hr)) { qDebug() << "Speak failed."; } @@ -124,14 +143,21 @@ void SpeechScriptingInterface::speakText(const QString& textToSpeak) { qDebug() << "Couldn't read from stream."; } - QByteArray byteArray = QByteArray::QByteArray(buf1, dwSize); + _lastSoundByteArray.resize(0); + _lastSoundByteArray.append(buf1, dwSize); - AudioInjectorOptions options; - options.position = DependencyManager::get()->getMyAvatarPosition(); + emit ttsSampleCreated(_lastSoundByteArray); - lastSound = AudioInjector::playSound(byteArray, options); + if (alsoInject) { + AudioInjectorOptions options; + options.position = DependencyManager::get()->getMyAvatarPosition(); + + _lastSoundAudioInjector = AudioInjector::playSound(_lastSoundByteArray, options); + } } -void SpeechScriptingInterface::stopLastSpeech() { - lastSound->stop(); +void TTSScriptingInterface::stopLastSpeech() { + if (_lastSoundAudioInjector) { + _lastSoundAudioInjector->stop(); + } } diff --git a/interface/src/scripting/SpeechScriptingInterface.h b/interface/src/scripting/TTSScriptingInterface.h similarity index 79% rename from interface/src/scripting/SpeechScriptingInterface.h rename to interface/src/scripting/TTSScriptingInterface.h index c683a1a3c6..cb9c6c8c3e 100644 --- a/interface/src/scripting/SpeechScriptingInterface.h +++ b/interface/src/scripting/TTSScriptingInterface.h @@ -1,5 +1,5 @@ -// SpeechScriptingInterface.h -// interface/src/scripting +// TTSScriptingInterface.h +// libraries/audio-client/src/scripting // // Created by Zach Fox on 2018-10-10. // Copyright 2018 High Fidelity, Inc. @@ -20,16 +20,20 @@ #include // SAPI Helper #include -class SpeechScriptingInterface : public QObject, public Dependency { +class TTSScriptingInterface : public QObject, public Dependency { Q_OBJECT public: - SpeechScriptingInterface(); - ~SpeechScriptingInterface(); + TTSScriptingInterface(); + ~TTSScriptingInterface(); - Q_INVOKABLE void speakText(const QString& textToSpeak); + Q_INVOKABLE void testTone(const bool& alsoInject = false); + Q_INVOKABLE void speakText(const QString& textToSpeak, const bool& alsoInject = false); Q_INVOKABLE void stopLastSpeech(); +signals: + void ttsSampleCreated(QByteArray outputArray); + private: class CComAutoInit { public: @@ -72,7 +76,8 @@ private: // Default voice token CComPtr m_voiceToken; - AudioInjectorPointer lastSound; + QByteArray _lastSoundByteArray; + AudioInjectorPointer _lastSoundByteArray; }; #endif // hifi_SpeechScriptingInterface_h diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index d00bc29054..96f1c97878 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1135,6 +1135,46 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { } } +void AudioClient::processAudioAndAddToRingBuffer(QByteArray& inputByteArray, const uchar& channelCount, const qint32& bytesForDuration) { + // input samples required to produce exactly NETWORK_FRAME_SAMPLES of output + const int inputSamplesRequired = + (_inputToNetworkResampler ? _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) + : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * + channelCount; + + const auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); + + handleLocalEchoAndReverb(inputByteArray); + + _inputRingBuffer.writeData(inputByteArray.data(), inputByteArray.size()); + + float audioInputMsecsRead = inputByteArray.size() / (float)(bytesForDuration); + _stats.updateInputMsRead(audioInputMsecsRead); + + const int numNetworkBytes = + _isStereoInput ? AudioConstants::NETWORK_FRAME_BYTES_STEREO : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; + const int numNetworkSamples = + _isStereoInput ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; + + static int16_t networkAudioSamples[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; + + while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) { + if (_muted) { + _inputRingBuffer.shiftReadPosition(inputSamplesRequired); + } else { + _inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired); + possibleResampling(_inputToNetworkResampler, inputAudioSamples.get(), networkAudioSamples, inputSamplesRequired, + numNetworkSamples, channelCount, _desiredInputFormat.channelCount()); + } + int bytesInInputRingBuffer = _inputRingBuffer.samplesAvailable() * AudioConstants::SAMPLE_SIZE; + float msecsInInputRingBuffer = bytesInInputRingBuffer / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); + _stats.updateInputMsUnplayed(msecsInInputRingBuffer); + + QByteArray audioBuffer(reinterpret_cast(networkAudioSamples), numNetworkBytes); + handleAudioInput(audioBuffer); + } +} + void AudioClient::handleMicAudioInput() { if (!_inputDevice || _isPlayingBackRecording) { return; @@ -1144,47 +1184,8 @@ void AudioClient::handleMicAudioInput() { _inputReadsSinceLastCheck++; #endif - // input samples required to produce exactly NETWORK_FRAME_SAMPLES of output - const int inputSamplesRequired = (_inputToNetworkResampler ? - _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) : - AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * _inputFormat.channelCount(); - - const auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); - QByteArray inputByteArray = _inputDevice->readAll(); - - handleLocalEchoAndReverb(inputByteArray); - - _inputRingBuffer.writeData(inputByteArray.data(), inputByteArray.size()); - - float audioInputMsecsRead = inputByteArray.size() / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); - _stats.updateInputMsRead(audioInputMsecsRead); - - const int numNetworkBytes = _isStereoInput - ? AudioConstants::NETWORK_FRAME_BYTES_STEREO - : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; - const int numNetworkSamples = _isStereoInput - ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO - : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; - - static int16_t networkAudioSamples[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; - - while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) { - if (_muted) { - _inputRingBuffer.shiftReadPosition(inputSamplesRequired); - } else { - _inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired); - possibleResampling(_inputToNetworkResampler, - inputAudioSamples.get(), networkAudioSamples, - inputSamplesRequired, numNetworkSamples, - _inputFormat.channelCount(), _desiredInputFormat.channelCount()); - } - int bytesInInputRingBuffer = _inputRingBuffer.samplesAvailable() * AudioConstants::SAMPLE_SIZE; - float msecsInInputRingBuffer = bytesInInputRingBuffer / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); - _stats.updateInputMsUnplayed(msecsInInputRingBuffer); - - QByteArray audioBuffer(reinterpret_cast(networkAudioSamples), numNetworkBytes); - handleAudioInput(audioBuffer); - } + processAudioAndAddToRingBuffer(_inputDevice->readAll(), _inputFormat.channelCount(), + _inputFormat.bytesForDuration(USECS_PER_MSEC)); } void AudioClient::handleDummyAudioInput() { @@ -1201,6 +1202,11 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { handleAudioInput(audioBuffer); } +void AudioClient::handleTTSAudioInput(const QByteArray& audio) { + QByteArray audioBuffer(audio); + processAudioAndAddToRingBuffer(audioBuffer, 1, 48); +} + void AudioClient::prepareLocalAudioInjectors(std::unique_ptr localAudioLock) { bool doSynchronously = localAudioLock.operator bool(); if (!localAudioLock) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 5e7f1fb8a0..170a355abe 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -197,6 +197,7 @@ public slots: void checkInputTimeout(); void handleDummyAudioInput(); void handleRecordedAudioInput(const QByteArray& audio); + void handleTTSAudioInput(const QByteArray& audio); void reset(); void audioMixerKilled(); @@ -289,6 +290,8 @@ private: float azimuthForSource(const glm::vec3& relativePosition); float gainForSource(float distance, float volume); + void processAudioAndAddToRingBuffer(QByteArray& inputByteArray, const uchar& channelCount, const qint32& bytesForDuration); + #ifdef Q_OS_ANDROID QTimer _checkInputTimer; long _inputReadsSinceLastCheck = 0l; From 53226e7924d109be6a1a763da0793d721bbe32be Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 12 Oct 2018 11:19:52 -0700 Subject: [PATCH 007/125] Prevent overflows; still not working --- interface/src/scripting/TTSScriptingInterface.h | 2 +- libraries/audio-client/src/AudioClient.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/interface/src/scripting/TTSScriptingInterface.h b/interface/src/scripting/TTSScriptingInterface.h index cb9c6c8c3e..c1fffe67d1 100644 --- a/interface/src/scripting/TTSScriptingInterface.h +++ b/interface/src/scripting/TTSScriptingInterface.h @@ -77,7 +77,7 @@ private: CComPtr m_voiceToken; QByteArray _lastSoundByteArray; - AudioInjectorPointer _lastSoundByteArray; + AudioInjectorPointer _lastSoundAudioInjector; }; #endif // hifi_SpeechScriptingInterface_h diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 96f1c97878..12da7ea3be 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1167,7 +1167,7 @@ void AudioClient::processAudioAndAddToRingBuffer(QByteArray& inputByteArray, con numNetworkSamples, channelCount, _desiredInputFormat.channelCount()); } int bytesInInputRingBuffer = _inputRingBuffer.samplesAvailable() * AudioConstants::SAMPLE_SIZE; - float msecsInInputRingBuffer = bytesInInputRingBuffer / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); + float msecsInInputRingBuffer = bytesInInputRingBuffer / (float)(bytesForDuration); _stats.updateInputMsUnplayed(msecsInInputRingBuffer); QByteArray audioBuffer(reinterpret_cast(networkAudioSamples), numNetworkBytes); @@ -1204,7 +1204,12 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { void AudioClient::handleTTSAudioInput(const QByteArray& audio) { QByteArray audioBuffer(audio); - processAudioAndAddToRingBuffer(audioBuffer, 1, 48); + while (audioBuffer.size() > 0) { + QByteArray part; + part.append(audioBuffer.data(), AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + audioBuffer.remove(0, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + processAudioAndAddToRingBuffer(part, 1, 48); + } } void AudioClient::prepareLocalAudioInjectors(std::unique_ptr localAudioLock) { From 34befd4a52e085bbf548a200e507cc34afe07b3c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 12 Oct 2018 12:14:51 -0700 Subject: [PATCH 008/125] Just to make sure, writes data back to a WAV file --- libraries/audio-client/src/AudioClient.cpp | 378 ++++++++++----------- 1 file changed, 188 insertions(+), 190 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 12da7ea3be..858f6e738c 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -67,7 +68,7 @@ static const int CHECK_INPUT_READS_MSECS = 2000; static const int MIN_READS_TO_CONSIDER_INPUT_ALIVE = 10; #endif -static const auto DEFAULT_POSITION_GETTER = []{ return Vectors::ZERO; }; +static const auto DEFAULT_POSITION_GETTER = [] { return Vectors::ZERO; }; static const auto DEFAULT_ORIENTATION_GETTER = [] { return Quaternions::IDENTITY; }; static const int DEFAULT_BUFFER_FRAMES = 1; @@ -78,12 +79,11 @@ static const int OUTPUT_CHANNEL_COUNT = 2; static const bool DEFAULT_STARVE_DETECTION_ENABLED = true; static const int STARVE_DETECTION_THRESHOLD = 3; -static const int STARVE_DETECTION_PERIOD = 10 * 1000; // 10 Seconds +static const int STARVE_DETECTION_PERIOD = 10 * 1000; // 10 Seconds Setting::Handle dynamicJitterBufferEnabled("dynamicJitterBuffersEnabled", - InboundAudioStream::DEFAULT_DYNAMIC_JITTER_BUFFER_ENABLED); -Setting::Handle staticJitterBufferFrames("staticJitterBufferFrames", - InboundAudioStream::DEFAULT_STATIC_JITTER_FRAMES); + InboundAudioStream::DEFAULT_DYNAMIC_JITTER_BUFFER_ENABLED); +Setting::Handle staticJitterBufferFrames("staticJitterBufferFrames", InboundAudioStream::DEFAULT_STATIC_JITTER_FRAMES); // protect the Qt internal device list using Mutex = std::mutex; @@ -127,7 +127,7 @@ QAudioDeviceInfo AudioClient::getActiveAudioDevice(QAudio::Mode mode) const { if (mode == QAudio::AudioInput) { return _inputDeviceInfo; - } else { // if (mode == QAudio::AudioOutput) + } else { // if (mode == QAudio::AudioOutput) return _outputDeviceInfo; } } @@ -137,14 +137,13 @@ QList AudioClient::getAudioDevices(QAudio::Mode mode) const { if (mode == QAudio::AudioInput) { return _inputDevices; - } else { // if (mode == QAudio::AudioOutput) + } else { // if (mode == QAudio::AudioOutput) return _outputDevices; } } static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) { - for (int i = 0; i < numSamples/2; i++) { - + for (int i = 0; i < numSamples / 2; i++) { // read 2 samples int16_t left = *source++; int16_t right = *source++; @@ -159,8 +158,7 @@ static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int num } static void channelDownmix(int16_t* source, int16_t* dest, int numSamples) { - for (int i = 0; i < numSamples/2; i++) { - + for (int i = 0; i < numSamples / 2; i++) { // read 2 samples int16_t left = *source++; int16_t right = *source++; @@ -175,48 +173,22 @@ static inline float convertToFloat(int16_t sample) { } AudioClient::AudioClient() : - AbstractAudioInterface(), - _gate(this), - _audioInput(NULL), - _dummyAudioInput(NULL), - _desiredInputFormat(), - _inputFormat(), - _numInputCallbackBytes(0), - _audioOutput(NULL), - _desiredOutputFormat(), - _outputFormat(), - _outputFrameSize(0), - _numOutputCallbackBytes(0), - _loopbackAudioOutput(NULL), - _loopbackOutputDevice(NULL), - _inputRingBuffer(0), - _localInjectorsStream(0, 1), - _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES), - _isStereoInput(false), - _outputStarveDetectionStartTimeMsec(0), - _outputStarveDetectionCount(0), + AbstractAudioInterface(), _gate(this), _audioInput(NULL), _dummyAudioInput(NULL), _desiredInputFormat(), _inputFormat(), + _numInputCallbackBytes(0), _audioOutput(NULL), _desiredOutputFormat(), _outputFormat(), _outputFrameSize(0), + _numOutputCallbackBytes(0), _loopbackAudioOutput(NULL), _loopbackOutputDevice(NULL), _inputRingBuffer(0), + _localInjectorsStream(0, 1), _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES), _isStereoInput(false), + _outputStarveDetectionStartTimeMsec(0), _outputStarveDetectionCount(0), _outputBufferSizeFrames("audioOutputBufferFrames", DEFAULT_BUFFER_FRAMES), _sessionOutputBufferSizeFrames(_outputBufferSizeFrames.get()), _outputStarveDetectionEnabled("audioOutputStarveDetectionEnabled", DEFAULT_STARVE_DETECTION_ENABLED), - _lastInputLoudness(0.0f), - _timeSinceLastClip(-1.0f), - _muted(false), - _shouldEchoLocally(false), - _shouldEchoToServer(false), - _isNoiseGateEnabled(true), - _reverb(false), - _reverbOptions(&_scriptReverbOptions), - _inputToNetworkResampler(NULL), - _networkToOutputResampler(NULL), - _localToOutputResampler(NULL), - _audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), - _outgoingAvatarAudioSequenceNumber(0), - _audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this), - _stats(&_receivedAudioStream), + _lastInputLoudness(0.0f), _timeSinceLastClip(-1.0f), _muted(false), _shouldEchoLocally(false), _shouldEchoToServer(false), + _isNoiseGateEnabled(true), _reverb(false), _reverbOptions(&_scriptReverbOptions), _inputToNetworkResampler(NULL), + _networkToOutputResampler(NULL), _localToOutputResampler(NULL), + _audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), _outgoingAvatarAudioSequenceNumber(0), + _audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this), _stats(&_receivedAudioStream), _positionGetter(DEFAULT_POSITION_GETTER), #if defined(Q_OS_ANDROID) - _checkInputTimer(this), - _isHeadsetPluggedIn(false), + _checkInputTimer(this), _isHeadsetPluggedIn(false), #endif _orientationGetter(DEFAULT_ORIENTATION_GETTER) { // avoid putting a lock in the device callback @@ -226,16 +198,20 @@ AudioClient::AudioClient() : { Setting::Handle::Deprecated("maxFramesOverDesired", InboundAudioStream::MAX_FRAMES_OVER_DESIRED); Setting::Handle::Deprecated("windowStarveThreshold", InboundAudioStream::WINDOW_STARVE_THRESHOLD); - Setting::Handle::Deprecated("windowSecondsForDesiredCalcOnTooManyStarves", InboundAudioStream::WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES); - Setting::Handle::Deprecated("windowSecondsForDesiredReduction", InboundAudioStream::WINDOW_SECONDS_FOR_DESIRED_REDUCTION); + Setting::Handle::Deprecated("windowSecondsForDesiredCalcOnTooManyStarves", + InboundAudioStream::WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES); + Setting::Handle::Deprecated("windowSecondsForDesiredReduction", + InboundAudioStream::WINDOW_SECONDS_FOR_DESIRED_REDUCTION); Setting::Handle::Deprecated("useStDevForJitterCalc", InboundAudioStream::USE_STDEV_FOR_JITTER); Setting::Handle::Deprecated("repetitionWithFade", InboundAudioStream::REPETITION_WITH_FADE); } - connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples, - this, &AudioClient::processReceivedSamples, Qt::DirectConnection); + connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples, this, &AudioClient::processReceivedSamples, + Qt::DirectConnection); connect(this, &AudioClient::changeDevice, this, [=](const QAudioDeviceInfo& outputDeviceInfo) { - qCDebug(audioclient) << "got AudioClient::changeDevice signal, about to call switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; + qCDebug(audioclient) + << "got AudioClient::changeDevice signal, about to call switchOutputToAudioDevice() outputDeviceInfo: [" + << outputDeviceInfo.deviceName() << "]"; switchOutputToAudioDevice(outputDeviceInfo); }); @@ -244,20 +220,18 @@ AudioClient::AudioClient() : // initialize wasapi; if getAvailableDevices is called from the CheckDevicesThread before this, it will crash getAvailableDevices(QAudio::AudioInput); getAvailableDevices(QAudio::AudioOutput); - + // start a thread to detect any device changes _checkDevicesTimer = new QTimer(this); - connect(_checkDevicesTimer, &QTimer::timeout, this, [this] { - QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkDevices(); }); - }); + connect(_checkDevicesTimer, &QTimer::timeout, this, + [this] { QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkDevices(); }); }); const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000; _checkDevicesTimer->start(DEVICE_CHECK_INTERVAL_MSECS); // start a thread to detect peak value changes _checkPeakValuesTimer = new QTimer(this); - connect(_checkPeakValuesTimer, &QTimer::timeout, this, [this] { - QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkPeakValues(); }); - }); + connect(_checkPeakValuesTimer, &QTimer::timeout, this, + [this] { QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkPeakValues(); }); }); const unsigned long PEAK_VALUES_CHECK_INTERVAL_MSECS = 50; _checkPeakValuesTimer->start(PEAK_VALUES_CHECK_INTERVAL_MSECS); @@ -289,11 +263,11 @@ void AudioClient::customDeleter() { } void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) { - qCDebug(audioclient) << __FUNCTION__ << "sendingNode:" << *node << "currentCodec:" << currentCodec << "recievedCodec:" << recievedCodec; + qCDebug(audioclient) << __FUNCTION__ << "sendingNode:" << *node << "currentCodec:" << currentCodec + << "recievedCodec:" << recievedCodec; selectAudioFormat(recievedCodec); } - void AudioClient::reset() { _receivedAudioStream.reset(); _stats.reset(); @@ -321,7 +295,7 @@ void AudioClient::setAudioPaused(bool pause) { QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) { QAudioDeviceInfo result; - foreach(QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { + foreach (QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) { result = audioDevice; break; @@ -356,7 +330,8 @@ QString AudioClient::getWinDeviceName(wchar_t* guid) { HRESULT hr = S_OK; CoInitialize(nullptr); IMMDeviceEnumerator* pMMDeviceEnumerator = nullptr; - CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); + CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), + (void**)&pMMDeviceEnumerator); IMMDevice* pEndpoint; hr = pMMDeviceEnumerator->GetDevice(guid, &pEndpoint); if (hr == E_NOTFOUND) { @@ -380,34 +355,26 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { if (getAvailableDevices(mode).size() > 1) { AudioDeviceID defaultDeviceID = 0; uint32_t propertySize = sizeof(AudioDeviceID); - AudioObjectPropertyAddress propertyAddress = { - kAudioHardwarePropertyDefaultInputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; + AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDefaultInputDevice, + kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; if (mode == QAudio::AudioOutput) { propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; } - - OSStatus getPropertyError = AudioObjectGetPropertyData(kAudioObjectSystemObject, - &propertyAddress, - 0, - NULL, - &propertySize, - &defaultDeviceID); + OSStatus getPropertyError = + AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &defaultDeviceID); if (!getPropertyError && propertySize) { CFStringRef deviceName = NULL; propertySize = sizeof(deviceName); propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; - getPropertyError = AudioObjectGetPropertyData(defaultDeviceID, &propertyAddress, 0, - NULL, &propertySize, &deviceName); + getPropertyError = + AudioObjectGetPropertyData(defaultDeviceID, &propertyAddress, 0, NULL, &propertySize, &deviceName); if (!getPropertyError && propertySize) { // find a device in the list that matches the name we have and return it - foreach(QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { + foreach (QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { if (audioDevice.deviceName() == CFStringGetCStringPtr(deviceName, kCFStringEncodingMacRoman)) { return audioDevice; } @@ -419,7 +386,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { #ifdef WIN32 QString deviceName; //Check for Windows Vista or higher, IMMDeviceEnumerator doesn't work below that. - if (!IsWindowsVistaOrGreater()) { // lower then vista + if (!IsWindowsVistaOrGreater()) { // lower then vista if (mode == QAudio::AudioInput) { WAVEINCAPS wic; // first use WAVE_MAPPER to get the default devices manufacturer ID @@ -441,9 +408,11 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { HRESULT hr = S_OK; CoInitialize(NULL); IMMDeviceEnumerator* pMMDeviceEnumerator = NULL; - CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); + CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), + (void**)&pMMDeviceEnumerator); IMMDevice* pEndpoint; - hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(mode == QAudio::AudioOutput ? eRender : eCapture, eMultimedia, &pEndpoint); + hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(mode == QAudio::AudioOutput ? eRender : eCapture, eMultimedia, + &pEndpoint); if (hr == E_NOTFOUND) { printf("Audio Error: device not found\n"); deviceName = QString("NONE"); @@ -457,22 +426,22 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { CoUninitialize(); } - qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input") - << " [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]"; + qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input") << " [" + << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]"; return getNamedAudioDeviceForMode(mode, deviceName); #endif -#if defined (Q_OS_ANDROID) +#if defined(Q_OS_ANDROID) if (mode == QAudio::AudioInput) { Setting::Handle enableAEC(SETTING_AEC_KEY, false); bool aecEnabled = enableAEC.get(); auto audioClient = DependencyManager::get(); - bool headsetOn = audioClient? audioClient->isHeadsetPluggedIn() : false; + bool headsetOn = audioClient ? audioClient->isHeadsetPluggedIn() : false; auto inputDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (auto inputDevice : inputDevices) { if (((headsetOn || !aecEnabled) && inputDevice.deviceName() == VOICE_RECOGNITION) || - ((!headsetOn && aecEnabled) && inputDevice.deviceName() == VOICE_COMMUNICATION)) { + ((!headsetOn && aecEnabled) && inputDevice.deviceName() == VOICE_COMMUNICATION)) { return inputDevice; } } @@ -486,11 +455,8 @@ bool AudioClient::getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QStr return (getNamedAudioDeviceForMode(mode, deviceName).deviceName() == deviceName); } - // attempt to use the native sample rate and channel count -bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, - QAudioFormat& audioFormat) { - +bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, QAudioFormat& audioFormat) { audioFormat = audioDevice.preferredFormat(); audioFormat.setCodec("audio/pcm"); @@ -513,7 +479,6 @@ bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, const QAudioFormat& desiredAudioFormat, QAudioFormat& adjustedAudioFormat) { - qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat; #if defined(Q_OS_ANDROID) || defined(Q_OS_OSX) @@ -539,12 +504,11 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, // Attempt the device sample rate and channel count in decreasing order of preference. // 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 + 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); @@ -554,11 +518,14 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, } } - return false; // a supported format could not be found + return false; // a supported format could not be found } -bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationSamples, unsigned int numSourceSamples, - const int sourceChannelCount, const int destinationChannelCount) { +bool sampleChannelConversion(const int16_t* sourceSamples, + int16_t* destinationSamples, + unsigned int numSourceSamples, + const int sourceChannelCount, + const int destinationChannelCount) { if (sourceChannelCount == 2 && destinationChannelCount == 1) { // loop through the stereo input audio samples and average every two samples for (uint i = 0; i < numSourceSamples; i += 2) { @@ -567,7 +534,6 @@ bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationS return true; } else if (sourceChannelCount == 1 && destinationChannelCount == 2) { - // loop through the mono input audio and repeat each sample twice for (uint i = 0; i < numSourceSamples; ++i) { destinationSamples[i * 2] = destinationSamples[(i * 2) + 1] = sourceSamples[i]; @@ -580,32 +546,31 @@ bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationS } void possibleResampling(AudioSRC* resampler, - const int16_t* sourceSamples, int16_t* destinationSamples, - unsigned int numSourceSamples, unsigned int numDestinationSamples, - const int sourceChannelCount, const int destinationChannelCount) { - + const int16_t* sourceSamples, + int16_t* destinationSamples, + unsigned int numSourceSamples, + unsigned int numDestinationSamples, + const int sourceChannelCount, + const int destinationChannelCount) { if (numSourceSamples > 0) { if (!resampler) { - if (!sampleChannelConversion(sourceSamples, destinationSamples, numSourceSamples, - sourceChannelCount, destinationChannelCount)) { + if (!sampleChannelConversion(sourceSamples, destinationSamples, numSourceSamples, sourceChannelCount, + destinationChannelCount)) { // no conversion, we can copy the samples directly across memcpy(destinationSamples, sourceSamples, numSourceSamples * AudioConstants::SAMPLE_SIZE); } } else { - if (sourceChannelCount != destinationChannelCount) { - int numChannelCoversionSamples = (numSourceSamples * destinationChannelCount) / sourceChannelCount; int16_t* channelConversionSamples = new int16_t[numChannelCoversionSamples]; - sampleChannelConversion(sourceSamples, channelConversionSamples, numSourceSamples, - sourceChannelCount, destinationChannelCount); + sampleChannelConversion(sourceSamples, channelConversionSamples, numSourceSamples, sourceChannelCount, + destinationChannelCount); resampler->render(channelConversionSamples, destinationSamples, numChannelCoversionSamples); delete[] channelConversionSamples; } else { - unsigned int numAdjustedSourceSamples = numSourceSamples; unsigned int numAdjustedDestinationSamples = numDestinationSamples; @@ -621,7 +586,6 @@ void possibleResampling(AudioSRC* resampler, } void AudioClient::start() { - // set up the desired audio format _desiredInputFormat.setSampleRate(AudioConstants::SAMPLE_RATE); _desiredInputFormat.setSampleSize(16); @@ -710,7 +674,6 @@ void AudioClient::handleAudioDataPacket(QSharedPointer message) nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket); if (_audioOutput) { - if (!_hasReceivedFirstPacket) { _hasReceivedFirstPacket = true; @@ -727,8 +690,8 @@ void AudioClient::handleAudioDataPacket(QSharedPointer message) } } -AudioClient::Gate::Gate(AudioClient* audioClient) : - _audioClient(audioClient) {} +AudioClient::Gate::Gate(AudioClient* audioClient) : _audioClient(audioClient) { +} void AudioClient::Gate::setIsSimulatingJitter(bool enable) { std::lock_guard lock(_mutex); @@ -781,7 +744,6 @@ void AudioClient::Gate::flush() { _index = 0; } - void AudioClient::handleNoisyMutePacket(QSharedPointer message) { if (!_muted) { setMuted(true); @@ -827,7 +789,6 @@ void AudioClient::handleSelectedAudioFormat(QSharedPointer mess } void AudioClient::selectAudioFormat(const QString& selectedCodecName) { - _selectedCodecName = selectedCodecName; qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput; @@ -845,12 +806,12 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) { if (_selectedCodecName == plugin->getName()) { _codec = plugin; _receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO); - _encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); + _encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, + _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get(); break; } } - } bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo) { @@ -862,7 +823,7 @@ bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& d if (mode == QAudio::AudioInput) { return switchInputToAudioDevice(device); - } else { // if (mode == QAudio::AudioOutput) + } else { // if (mode == QAudio::AudioOutput) return switchOutputToAudioDevice(device); } } @@ -904,8 +865,8 @@ void AudioClient::configureReverb() { p.sampleRate = _outputFormat.sampleRate(); p.wetDryMix = 100.0f; p.preDelay = 0.0f; - p.earlyGain = -96.0f; // disable ER - p.lateGain += _reverbOptions->getWetDryMix() * (24.0f/100.0f) - 24.0f; // -0dB to -24dB, based on wetDryMix + p.earlyGain = -96.0f; // disable ER + p.lateGain += _reverbOptions->getWetDryMix() * (24.0f / 100.0f) - 24.0f; // -0dB to -24dB, based on wetDryMix p.lateMixLeft = 0.0f; p.lateMixRight = 0.0f; @@ -915,7 +876,6 @@ void AudioClient::configureReverb() { void AudioClient::updateReverbOptions() { bool reverbChanged = false; if (_receivedAudioStream.hasReverb()) { - if (_zoneReverbOptions.getReverbTime() != _receivedAudioStream.getRevebTime()) { _zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime()); reverbChanged = true; @@ -1020,7 +980,8 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { int16_t* loopbackSamples = reinterpret_cast(loopBackByteArray.data()); // upmix mono to stereo - if (!sampleChannelConversion(inputSamples, loopbackSamples, numInputSamples, _inputFormat.channelCount(), OUTPUT_CHANNEL_COUNT)) { + if (!sampleChannelConversion(inputSamples, loopbackSamples, numInputSamples, _inputFormat.channelCount(), + OUTPUT_CHANNEL_COUNT)) { // no conversion, just copy the samples memcpy(loopbackSamples, inputSamples, numInputSamples * AudioConstants::SAMPLE_SIZE); } @@ -1028,17 +989,15 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { // apply stereo reverb at the source, to the loopback audio if (!_shouldEchoLocally && hasReverb) { updateReverbOptions(); - _sourceReverb.render(loopbackSamples, loopbackSamples, numLoopbackSamples/2); + _sourceReverb.render(loopbackSamples, loopbackSamples, numLoopbackSamples / 2); } // if required, upmix or downmix to deviceChannelCount int deviceChannelCount = _outputFormat.channelCount(); if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { - _loopbackOutputDevice->write(loopBackByteArray); } else { - static QByteArray deviceByteArray; int numDeviceSamples = (numLoopbackSamples * deviceChannelCount) / OUTPUT_CHANNEL_COUNT; @@ -1074,7 +1033,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { } int32_t loudness = 0; - assert(numSamples < 65536); // int32_t loudness cannot overflow + assert(numSamples < 65536); // int32_t loudness cannot overflow bool didClip = false; for (int i = 0; i < numSamples; ++i) { const int32_t CLIPPING_THRESHOLD = (int32_t)(AudioConstants::MAX_SAMPLE_VALUE * 0.9f); @@ -1129,13 +1088,14 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { } emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, _isStereoInput, - audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, - packetType, _selectedCodecName); + audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, packetType, _selectedCodecName); _stats.sentPacket(); } } -void AudioClient::processAudioAndAddToRingBuffer(QByteArray& inputByteArray, const uchar& channelCount, const qint32& bytesForDuration) { +void AudioClient::processAudioAndAddToRingBuffer(QByteArray& inputByteArray, + const uchar& channelCount, + const qint32& bytesForDuration) { // input samples required to produce exactly NETWORK_FRAME_SAMPLES of output const int inputSamplesRequired = (_inputToNetworkResampler ? _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) @@ -1189,11 +1149,10 @@ void AudioClient::handleMicAudioInput() { } void AudioClient::handleDummyAudioInput() { - const int numNetworkBytes = _isStereoInput - ? AudioConstants::NETWORK_FRAME_BYTES_STEREO - : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; + const int numNetworkBytes = + _isStereoInput ? AudioConstants::NETWORK_FRAME_BYTES_STEREO : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; - QByteArray audioBuffer(numNetworkBytes, 0); // silent + QByteArray audioBuffer(numNetworkBytes, 0); // silent handleAudioInput(audioBuffer); } @@ -1202,13 +1161,59 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { handleAudioInput(audioBuffer); } + int rawToWav(const char* rawData, const int& rawLength, const char* wavfn, long frequency) { + long chunksize = 0x10; + + struct { + unsigned short wFormatTag; + unsigned short wChannels; + unsigned long dwSamplesPerSec; + unsigned long dwAvgBytesPerSec; + unsigned short wBlockAlign; + unsigned short wBitsPerSample; + } fmt; + + long samplecount = rawLength / 2; + long riffsize = samplecount * 2 + 0x24; + long datasize = samplecount * 2; + + FILE* wav = fopen(wavfn, "wb"); + if (!wav) { + return -3; + } + + fwrite("RIFF", 1, 4, wav); + fwrite(&riffsize, 4, 1, wav); + fwrite("WAVEfmt ", 1, 8, wav); + fwrite(&chunksize, 4, 1, wav); + + fmt.wFormatTag = 1; // PCM + fmt.wChannels = 1; // MONO + fmt.dwSamplesPerSec = frequency * 1; + fmt.dwAvgBytesPerSec = frequency * 1 * 2; // 16 bit + fmt.wBlockAlign = 2; + fmt.wBitsPerSample = 16; + + fwrite(&fmt, sizeof(fmt), 1, wav); + fwrite("data", 1, 4, wav); + fwrite(&datasize, 4, 1, wav); + fwrite(rawData, 1, rawLength, wav); + fclose(wav); +} + void AudioClient::handleTTSAudioInput(const QByteArray& audio) { QByteArray audioBuffer(audio); + QVector audioBufferReal; + + QString filename = QString::number(usecTimestampNow()); + QString path = PathUtils::getAppDataPath() + "Audio/" + filename + ".wav"; + rawToWav(audioBuffer.data(), audioBuffer.size(), path.toLocal8Bit(), 24000); + while (audioBuffer.size() > 0) { QByteArray part; part.append(audioBuffer.data(), AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); audioBuffer.remove(0, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); - processAudioAndAddToRingBuffer(part, 1, 48); + processAudioAndAddToRingBuffer(part, 1, 48); } } @@ -1234,9 +1239,8 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr localAudioLoc int bufferCapacity = _localInjectorsStream.getSampleCapacity(); int maxOutputSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * AudioConstants::STEREO; if (_localToOutputResampler) { - maxOutputSamples = - _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * - AudioConstants::STEREO; + maxOutputSamples = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * + AudioConstants::STEREO; } samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed); @@ -1259,7 +1263,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr localAudioLoc if (_localToOutputResampler) { // resample to output sample rate int frames = _localToOutputResampler->render(_localMixBuffer, _localOutputMixBuffer, - AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); // write to local injectors' ring buffer samples = frames * AudioConstants::STEREO; @@ -1268,8 +1272,7 @@ void AudioClient::prepareLocalAudioInjectors(std::unique_ptr localAudioLoc } else { // write to local injectors' ring buffer samples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; - _localInjectorsStream.writeSamples(_localMixBuffer, - AudioConstants::NETWORK_FRAME_SAMPLES_STEREO); + _localInjectorsStream.writeSamples(_localMixBuffer, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO); } _localSamplesAvailable.fetch_add(samples, std::memory_order_release); @@ -1294,18 +1297,16 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { // the lock guarantees that injectorBuffer, if found, is invariant AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer(); if (injectorBuffer) { - static const int HRTF_DATASET_INDEX = 1; - int numChannels = injector->isAmbisonic() ? AudioConstants::AMBISONIC : (injector->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); + int numChannels = injector->isAmbisonic() ? AudioConstants::AMBISONIC + : (injector->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); size_t bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; // get one frame from the injector memset(_localScratchBuffer, 0, bytesToRead); if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) { - if (injector->isAmbisonic()) { - // no distance attenuation float gain = injector->getVolume(); @@ -1322,11 +1323,10 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { float qz = relativeOrientation.y; // Ambisonic gets spatialized into mixBuffer - injector->getLocalFOA().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, - qw, qx, qy, qz, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + injector->getLocalFOA().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, qw, qx, qy, qz, gain, + AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); } else if (injector->isStereo()) { - // stereo gets directly mixed into mixBuffer float gain = injector->getVolume(); for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) { @@ -1334,7 +1334,6 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { } } else { - // calculate distance, gain and azimuth for hrtf glm::vec3 relativePosition = injector->getPosition() - _positionGetter(); float distance = glm::max(glm::length(relativePosition), EPSILON); @@ -1342,19 +1341,17 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { float azimuth = azimuthForSource(relativePosition); // mono gets spatialized into mixBuffer - injector->getLocalHRTF().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, - azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + injector->getLocalHRTF().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, azimuth, distance, gain, + AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); } } else { - qCDebug(audioclient) << "injector has no more data, marking finished for removal"; injector->finishLocalInjection(); injectorsToRemove.append(injector); } } else { - qCDebug(audioclient) << "injector has no local buffer, marking as finished for removal"; injector->finishLocalInjection(); injectorsToRemove.append(injector); @@ -1373,7 +1370,6 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { } void AudioClient::processReceivedSamples(const QByteArray& decodedBuffer, QByteArray& outputBuffer) { - const int16_t* decodedSamples = reinterpret_cast(decodedBuffer.data()); assert(decodedBuffer.size() == AudioConstants::NETWORK_FRAME_BYTES_STEREO); @@ -1442,7 +1438,6 @@ void AudioClient::setNoiseReduction(bool enable, bool emitSignal) { } } - bool AudioClient::setIsStereoInput(bool isStereoInput) { bool stereoInputChanged = false; if (isStereoInput != _isStereoInput && _inputDeviceInfo.supportedChannelCounts().contains(2)) { @@ -1460,7 +1455,8 @@ bool AudioClient::setIsStereoInput(bool isStereoInput) { if (_encoder) { _codec->releaseEncoder(_encoder); } - _encoder = _codec->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); + _encoder = _codec->createEncoder(AudioConstants::SAMPLE_RATE, + _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); } qCDebug(audioclient) << "Reset Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput; @@ -1500,7 +1496,7 @@ bool AudioClient::outputLocalInjector(const AudioInjectorPointer& injector) { void AudioClient::outputFormatChanged() { _outputFrameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * OUTPUT_CHANNEL_COUNT * _outputFormat.sampleRate()) / - _desiredOutputFormat.sampleRate(); + _desiredOutputFormat.sampleRate(); _receivedAudioStream.outputFormatChanged(_outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); } @@ -1514,7 +1510,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInf Lock lock(_deviceMutex); #if defined(Q_OS_ANDROID) - _shouldRestartInputSetup = false; // avoid a double call to _audioInput->start() from audioInputStateChanged + _shouldRestartInputSetup = false; // avoid a double call to _audioInput->start() from audioInputStateChanged #endif // cleanup any previously initialized device @@ -1565,15 +1561,15 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInf // we've got the best we can get for input // if required, setup a resampler for this input to our desired network format - if (_inputFormat != _desiredInputFormat - && _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) { + if (_inputFormat != _desiredInputFormat && _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) { qCDebug(audioclient) << "Attemping to create a resampler for input format to network format."; assert(_inputFormat.sampleSize() == 16); assert(_desiredInputFormat.sampleSize() == 16); int channelCount = (_inputFormat.channelCount() == 2 && _desiredInputFormat.channelCount() == 2) ? 2 : 1; - _inputToNetworkResampler = new AudioSRC(_inputFormat.sampleRate(), _desiredInputFormat.sampleRate(), channelCount); + _inputToNetworkResampler = + new AudioSRC(_inputFormat.sampleRate(), _desiredInputFormat.sampleRate(), channelCount); } else { qCDebug(audioclient) << "No resampling required for audio input to match desired network format."; @@ -1607,7 +1603,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInf connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); supportedFormat = true; } else { - qCDebug(audioclient) << "Error starting audio input -" << _audioInput->error(); + qCDebug(audioclient) << "Error starting audio input -" << _audioInput->error(); _audioInput->deleteLater(); _audioInput = NULL; } @@ -1677,7 +1673,7 @@ void AudioClient::checkInputTimeout() { void AudioClient::setHeadsetPluggedIn(bool pluggedIn) { #if defined(Q_OS_ANDROID) if (pluggedIn == !_isHeadsetPluggedIn && !_inputDeviceInfo.isNull()) { - QAndroidJniObject brand = QAndroidJniObject::getStaticObjectField("android/os/Build", "BRAND"); + QAndroidJniObject brand = QAndroidJniObject::getStaticObjectField("android/os/Build", "BRAND"); // some samsung phones needs more time to shutdown the previous input device if (brand.toString().contains("samsung", Qt::CaseInsensitive)) { switchInputToAudioDevice(QAudioDeviceInfo(), true); @@ -1715,8 +1711,8 @@ void AudioClient::outputNotify() { int newOutputBufferSizeFrames = setOutputBufferSize(oldOutputBufferSizeFrames + 1, false); if (newOutputBufferSizeFrames > oldOutputBufferSizeFrames) { - qCDebug(audioclient, - "Starve threshold surpassed (%d starves in %d ms)", _outputStarveDetectionCount, dt); + qCDebug(audioclient, "Starve threshold surpassed (%d starves in %d ms)", _outputStarveDetectionCount, + dt); } _outputStarveDetectionStartTimeMsec = now; @@ -1730,7 +1726,8 @@ void AudioClient::outputNotify() { bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) { Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); - qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; + qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() + << "]"; bool supportedFormat = false; // NOTE: device start() uses the Qt internal device list @@ -1789,15 +1786,16 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceI // we've got the best we can get for input // if required, setup a resampler for this input to our desired network format - if (_desiredOutputFormat != _outputFormat - && _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) { + if (_desiredOutputFormat != _outputFormat && _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) { qCDebug(audioclient) << "Attemping to create a resampler for network format to output format."; assert(_desiredOutputFormat.sampleSize() == 16); assert(_outputFormat.sampleSize() == 16); - _networkToOutputResampler = new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); - _localToOutputResampler = new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); + _networkToOutputResampler = + new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); + _localToOutputResampler = + new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); } else { qCDebug(audioclient) << "No resampling required for network output to match actual output format."; @@ -1809,7 +1807,9 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceI _audioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); int deviceChannelCount = _outputFormat.channelCount(); - int frameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate(); + int frameSize = + (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / + _desiredOutputFormat.sampleRate(); int requestedSize = _sessionOutputBufferSizeFrames * frameSize * AudioConstants::SAMPLE_SIZE; _audioOutput->setBufferSize(requestedSize); @@ -1825,7 +1825,10 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceI _outputScratchBuffer = new int16_t[_outputPeriod]; // size local output mix buffer based on resampled network frame size - int networkPeriod = _localToOutputResampler ? _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO) : AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; + int networkPeriod = + _localToOutputResampler + ? _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO) + : AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; _localOutputMixBuffer = new float[networkPeriod]; // local period should be at least twice the output period, @@ -1875,7 +1878,8 @@ int AudioClient::setOutputBufferSize(int numFrames, bool persist) { qCDebug(audioclient) << __FUNCTION__ << "numFrames:" << numFrames << "persist:" << persist; numFrames = std::min(std::max(numFrames, MIN_BUFFER_FRAMES), MAX_BUFFER_FRAMES); - qCDebug(audioclient) << __FUNCTION__ << "clamped numFrames:" << numFrames << "_sessionOutputBufferSizeFrames:" << _sessionOutputBufferSizeFrames; + qCDebug(audioclient) << __FUNCTION__ << "clamped numFrames:" << numFrames + << "_sessionOutputBufferSizeFrames:" << _sessionOutputBufferSizeFrames; if (numFrames != _sessionOutputBufferSizeFrames) { qCInfo(audioclient, "Audio output buffer set to %d frames", numFrames); @@ -1906,10 +1910,10 @@ const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 2.0f; #endif int AudioClient::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const { - int numInputCallbackBytes = (int)(((AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL - * format.channelCount() - * ((float) format.sampleRate() / AudioConstants::SAMPLE_RATE)) - / CALLBACK_ACCELERATOR_RATIO) + 0.5f); + int numInputCallbackBytes = (int)(((AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * format.channelCount() * + ((float)format.sampleRate() / AudioConstants::SAMPLE_RATE)) / + CALLBACK_ACCELERATOR_RATIO) + + 0.5f); return numInputCallbackBytes; } @@ -1931,10 +1935,9 @@ float AudioClient::azimuthForSource(const glm::vec3& relativePosition) { float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition); if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) { - // produce an oriented angle about the y-axis glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2)); - float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" + float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" return (direction.x < 0.0f) ? -angle : angle; } else { @@ -1944,7 +1947,6 @@ float AudioClient::azimuthForSource(const glm::vec3& relativePosition) { } float AudioClient::gainForSource(float distance, float volume) { - // attenuation = -6dB * log2(distance) // reference attenuation of 0dB at distance = 1.0m float gain = volume / std::max(distance, HRTF_NEARFIELD_MIN); @@ -1952,8 +1954,7 @@ float AudioClient::gainForSource(float distance, float volume) { return gain; } -qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { - +qint64 AudioClient::AudioOutputIODevice::readData(char* data, qint64 maxSize) { // samples requested from OUTPUT_CHANNEL_COUNT int deviceChannelCount = _audio->_outputFormat.channelCount(); int samplesRequested = (int)(maxSize / AudioConstants::SAMPLE_SIZE) * OUTPUT_CHANNEL_COUNT / deviceChannelCount; @@ -1965,7 +1966,8 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { int networkSamplesPopped; if ((networkSamplesPopped = _receivedAudioStream.popSamples(samplesRequested, false)) > 0) { - qCDebug(audiostream, "Read %d samples from buffer (%d available, %d requested)", networkSamplesPopped, _receivedAudioStream.getSamplesAvailable(), samplesRequested); + qCDebug(audiostream, "Read %d samples from buffer (%d available, %d requested)", networkSamplesPopped, + _receivedAudioStream.getSamplesAvailable(), samplesRequested); AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput(); lastPopOutput.readSamples(scratchBuffer, networkSamplesPopped); for (int i = 0; i < networkSamplesPopped; i++) { @@ -1997,14 +1999,13 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { samplesRequested = std::min(samplesRequested, samplesAvailable); if ((injectorSamplesPopped = _localInjectorsStream.appendSamples(mixBuffer, samplesRequested, append)) > 0) { _audio->_localSamplesAvailable.fetch_sub(injectorSamplesPopped, std::memory_order_release); - qCDebug(audiostream, "Read %d samples from injectors (%d available, %d requested)", injectorSamplesPopped, _localInjectorsStream.samplesAvailable(), samplesRequested); + qCDebug(audiostream, "Read %d samples from injectors (%d available, %d requested)", injectorSamplesPopped, + _localInjectorsStream.samplesAvailable(), samplesRequested); } } // prepare injectors for the next callback - QtConcurrent::run(QThreadPool::globalInstance(), [this] { - _audio->prepareLocalAudioInjectors(); - }); + QtConcurrent::run(QThreadPool::globalInstance(), [this] { _audio->prepareLocalAudioInjectors(); }); int samplesPopped = std::max(networkSamplesPopped, injectorSamplesPopped); int framesPopped = samplesPopped / AudioConstants::STEREO; @@ -2038,7 +2039,6 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { _audio->_audioFileWav.addRawAudioChunk(reinterpret_cast(scratchBuffer), bytesWritten); } - int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree(); float msecsAudioOutputUnplayed = bytesAudioOutputUnplayed / (float)_audio->_outputFormat.bytesForDuration(USECS_PER_MSEC); _audio->_stats.updateOutputMsUnplayed(msecsAudioOutputUnplayed); @@ -2075,7 +2075,6 @@ void AudioClient::loadSettings() { for (auto& plugin : codecPlugins) { qCDebug(audioclient) << "Codec available:" << plugin->getName(); } - } void AudioClient::saveSettings() { @@ -2088,7 +2087,6 @@ void AudioClient::setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 sca avatarBoundingBoxScale = scale; } - void AudioClient::startThread() { moveToNewNamedThread(this, "Audio Thread", [this] { start(); }, QThread::TimeCriticalPriority); } From d9873d363322e5b15cb0e2c8e976e8595f3505ea Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 15 Oct 2018 11:33:42 -0700 Subject: [PATCH 009/125] Adding some debug stuff... --- libraries/audio-client/src/AudioClient.cpp | 48 ++++++++++++++-------- libraries/audio-client/src/AudioClient.h | 5 ++- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 858f6e738c..c2b066b716 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1095,7 +1095,8 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { void AudioClient::processAudioAndAddToRingBuffer(QByteArray& inputByteArray, const uchar& channelCount, - const qint32& bytesForDuration) { + const qint32& bytesForDuration, + QByteArray& rollingBuffer) { // input samples required to produce exactly NETWORK_FRAME_SAMPLES of output const int inputSamplesRequired = (_inputToNetworkResampler ? _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) @@ -1131,6 +1132,7 @@ void AudioClient::processAudioAndAddToRingBuffer(QByteArray& inputByteArray, _stats.updateInputMsUnplayed(msecsInInputRingBuffer); QByteArray audioBuffer(reinterpret_cast(networkAudioSamples), numNetworkBytes); + rollingBuffer.append(audioBuffer); handleAudioInput(audioBuffer); } } @@ -1144,8 +1146,10 @@ void AudioClient::handleMicAudioInput() { _inputReadsSinceLastCheck++; #endif + QByteArray temp; + processAudioAndAddToRingBuffer(_inputDevice->readAll(), _inputFormat.channelCount(), - _inputFormat.bytesForDuration(USECS_PER_MSEC)); + _inputFormat.bytesForDuration(USECS_PER_MSEC), temp); } void AudioClient::handleDummyAudioInput() { @@ -1161,9 +1165,7 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { handleAudioInput(audioBuffer); } - int rawToWav(const char* rawData, const int& rawLength, const char* wavfn, long frequency) { - long chunksize = 0x10; - +int rawToWav(const char* rawData, const int& rawLength, const char* wavfn, long frequency, unsigned short channels) { struct { unsigned short wFormatTag; unsigned short wChannels; @@ -1174,47 +1176,59 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { } fmt; long samplecount = rawLength / 2; - long riffsize = samplecount * 2 + 0x24; - long datasize = samplecount * 2; FILE* wav = fopen(wavfn, "wb"); if (!wav) { - return -3; + return -1; } fwrite("RIFF", 1, 4, wav); + + long riffsize = samplecount * 2 + 0x24; fwrite(&riffsize, 4, 1, wav); + fwrite("WAVEfmt ", 1, 8, wav); + + long chunksize = 0x10; fwrite(&chunksize, 4, 1, wav); - fmt.wFormatTag = 1; // PCM - fmt.wChannels = 1; // MONO + fmt.wFormatTag = 1; // WAVE_FORMAT_PCM + fmt.wChannels = channels; fmt.dwSamplesPerSec = frequency * 1; - fmt.dwAvgBytesPerSec = frequency * 1 * 2; // 16 bit - fmt.wBlockAlign = 2; fmt.wBitsPerSample = 16; - + fmt.wBlockAlign = fmt.wChannels * fmt.wBitsPerSample / 8; + fmt.dwAvgBytesPerSec = fmt.dwSamplesPerSec * fmt.wBlockAlign; fwrite(&fmt, sizeof(fmt), 1, wav); + fwrite("data", 1, 4, wav); + long datasize = samplecount * 2; fwrite(&datasize, 4, 1, wav); fwrite(rawData, 1, rawLength, wav); + fclose(wav); + + return 0; } void AudioClient::handleTTSAudioInput(const QByteArray& audio) { QByteArray audioBuffer(audio); - QVector audioBufferReal; QString filename = QString::number(usecTimestampNow()); - QString path = PathUtils::getAppDataPath() + "Audio/" + filename + ".wav"; - rawToWav(audioBuffer.data(), audioBuffer.size(), path.toLocal8Bit(), 24000); + QString path = PathUtils::getAppDataPath() + "Audio/" + filename + "-before.wav"; + rawToWav(audioBuffer.data(), audioBuffer.size(), path.toLocal8Bit(), 24000, 1); + + QByteArray temp; while (audioBuffer.size() > 0) { QByteArray part; part.append(audioBuffer.data(), AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); audioBuffer.remove(0, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); - processAudioAndAddToRingBuffer(part, 1, 48); + processAudioAndAddToRingBuffer(part, 1, 48, temp); } + + filename = QString::number(usecTimestampNow()); + path = PathUtils::getAppDataPath() + "Audio/" + filename + "-after.wav"; + rawToWav(temp.data(), temp.size(), path.toLocal8Bit(), 12000, 1); } void AudioClient::prepareLocalAudioInjectors(std::unique_ptr localAudioLock) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 170a355abe..1ca7cac6ca 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -290,7 +290,10 @@ private: float azimuthForSource(const glm::vec3& relativePosition); float gainForSource(float distance, float volume); - void processAudioAndAddToRingBuffer(QByteArray& inputByteArray, const uchar& channelCount, const qint32& bytesForDuration); + void processAudioAndAddToRingBuffer(QByteArray& inputByteArray, + const uchar& channelCount, + const qint32& bytesForDuration, + QByteArray& rollingBuffer); #ifdef Q_OS_ANDROID QTimer _checkInputTimer; From ae3d5c148ae3882d74628994cbf3cd68c945f52c Mon Sep 17 00:00:00 2001 From: sam gateau Date: Mon, 15 Oct 2018 17:48:16 -0700 Subject: [PATCH 010/125] Debugging the tablet disappearing in hmd --- interface/src/ui/overlays/ModelOverlay.cpp | 20 ++++++++++++++++++-- libraries/render-utils/src/Model.cpp | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 2379685252..5040842b3b 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -571,8 +571,24 @@ void ModelOverlay::locationChanged(bool tellPhysics) { // FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform() if (_model && _model->isActive()) { - _model->setRotation(getWorldOrientation()); - _model->setTranslation(getWorldPosition()); + if (!_isLODEnabled) { + auto rot = _model->getRotation(); + auto tra = _model->getTranslation(); + + auto nrot = getWorldOrientation(); + auto ntra = getWorldPosition(); + if (glm::any(glm::notEqual(rot, nrot))) { + rot = nrot; + _model->setRotation(rot); + } + if (glm::any(glm::notEqual(tra, ntra))) { + tra = ntra; + _model->setTranslation(tra); + } + } else { + _model->setRotation(getWorldOrientation()); + _model->setTranslation(getWorldPosition()); + } } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8b50f2e420..2bc9d25aff 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -228,6 +228,10 @@ void Model::updateRenderItems() { bool isWireframe = self->isWireframe(); auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags(); + if (renderItemKeyGlobalFlags.isLODDisabled()) { + modelTransform.setScale(glm::vec3(1.0f)); + } + render::Transaction transaction; for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) { @@ -247,6 +251,8 @@ void Model::updateRenderItems() { data.updateClusterBuffer(meshState.clusterMatrices); } + auto bound = data.getBound(); + Transform renderTransform = modelTransform; if (useDualQuaternionSkinning) { @@ -264,6 +270,15 @@ void Model::updateRenderItems() { } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); + if (renderItemKeyGlobalFlags.isLODDisabled()) { + auto newBound = data.getBound(); + if (bound != newBound) { + data.updateTransformForSkinnedMesh(renderTransform, modelTransform); + } else { + data.updateTransformForSkinnedMesh(renderTransform, modelTransform); + } + } + data.updateKey(renderItemKeyGlobalFlags); data.setShapeKey(invalidatePayloadShapeKey, isWireframe, useDualQuaternionSkinning); }); @@ -893,7 +908,12 @@ void Model::updateRenderItemsKey(const render::ScenePointer& scene) { _needsFixupInScene = true; return; } + auto prevVal = _needsFixupInScene; auto renderItemsKey = _renderItemKeyGlobalFlags; + if (renderItemsKey.isLODDisabled()) { + _needsFixupInScene = true; + _needsFixupInScene = prevVal; + } render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { transaction.updateItem(item, [renderItemsKey](ModelMeshPartPayload& data) { From 4837fd86de6e72a59436280364d241fc4babe4f9 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 16 Oct 2018 17:29:46 -0700 Subject: [PATCH 011/125] Desperataly trying to be efficient in js --- .../utilities/render/deferredLighting.qml | 26 +++- scripts/developer/utilities/render/luci.js | 128 +++++++++++++++--- 2 files changed, 129 insertions(+), 25 deletions(-) diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index a9479b2935..135c3fac97 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -279,11 +279,27 @@ Rectangle { } } Separator {} - HifiControls.Button { - text: "Engine" - // activeFocusOnPress: false - onClicked: { - sendToScript({method: "openEngineView"}); + Row { + HifiControls.Button { + text: "Engine" + // activeFocusOnPress: false + onClicked: { + sendToScript({method: "openEngineView"}); + } + } + HifiControls.Button { + text: "LOD" + // activeFocusOnPress: false + onClicked: { + sendToScript({method: "openEngineLODView"}); + } + } + HifiControls.Button { + text: "Cull" + // activeFocusOnPress: false + onClicked: { + sendToScript({method: "openCullInspectorView"}); + } } } } diff --git a/scripts/developer/utilities/render/luci.js b/scripts/developer/utilities/render/luci.js index cb5b01f9b2..40fb6e01d7 100644 --- a/scripts/developer/utilities/render/luci.js +++ b/scripts/developer/utilities/render/luci.js @@ -88,35 +88,123 @@ } - function fromQml(message) { - switch (message.method) { - case "openEngineView": - openEngineTaskView(); - break; - } - } + + var Page = function(title, qmlurl, width, height) { + + this.title = title; + this.qml = Script.resolvePath(qmlurl); + this.width = width; + this.height = height; + this.window = null; + }; + + Page.prototype.createView = function() { + if (this.window == null) { + var window = Desktop.createWindow(this.qml, { + title: this.title, + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: this.width, y: this.height} + }); + this.window = window + this.window.closed.connect(this.killView); + } + }; + + Page.prototype.killView = function() { + if (this.window !== undefined) { + this.window.closed.disconnect(this.killView); + this.window.close() + this.window = undefined + } + }; + + var pages = [] + pages.push_back(new Page('Render Engine', 'engineInspector.qml', 300, 400)) + + var engineInspectorView = null - function openEngineTaskView() { - if (engineInspectorView == null) { + function openEngineInspectorView() { + + /* if (engineInspectorView == null) { var qml = Script.resolvePath('engineInspector.qml'); - var window = new OverlayWindow({ + var window = Desktop.createWindow(qml, { title: 'Render Engine', - source: qml, - width: 300, - height: 400 + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: 300, y: 400} }); - window.setPosition(200, 50); engineInspectorView = window - window.closed.connect(function() { engineInspectorView = null; }); - } else { - engineInspectorView.setPosition(200, 50); + window.closed.connect(killEngineInspectorView); } } + function killEngineInspectorView() { + if (engineInspectorView !== undefined) { + engineInspectorView.closed.disconnect(killEngineInspectorView); + engineInspectorView.close() + engineInspectorView = undefined + } + } +*/ + var cullInspectorView = null + function openCullInspectorView() { + if (cullInspectorView == null) { + var qml = Script.resolvePath('culling.qml'); + var window = Desktop.createWindow(qml, { + title: 'Cull Inspector', + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: 400, y: 600} + }); + cullInspectorView = window + window.closed.connect(killCullInspectorView); + } + } + function killCullInspectorView() { + if (cullInspectorView !== undefined) { + cullInspectorView.closed.disconnect(killCullInspectorView); + cullInspectorView.close() + cullInspectorView = undefined + } + } + + var engineLODView = null + function openEngineLODView() { + if (engineLODView == null) { + engineLODView = Desktop.createWindow(Script.resolvePath('lod.qml'), { + title: 'Render LOD', + flags: Desktop.ALWAYS_ON_TOP, + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: 300, y: 500}, + }); + engineLODView.closed.connect(killEngineLODView); + } + } + function killEngineLODView() { + if (engineLODView !== undefined) { + engineLODView.closed.disconnect(killEngineLODView); + engineLODView.close() + engineLODView = undefined + } + } + + + function fromQml(message) { + switch (message.method) { + case "openEngineView": + openEngineInspectorView(); + break; + case "openCullInspectorView": + openCullInspectorView(); + break; + case "openEngineLODView": + openEngineLODView(); + break; + } + } + Script.scriptEnding.connect(function () { if (onLuciScreen) { tablet.gotoHomeScreen(); @@ -125,8 +213,8 @@ tablet.screenChanged.disconnect(onScreenChanged); tablet.removeButton(button); - if (engineInspectorView !== null) { - engineInspectorView.close() - } + killEngineInspectorView(); + killCullInspectorView(); + killEngineLODWindow(); }); }()); \ No newline at end of file From 26e388b139bb040ae2260042e7c8ff327ca18e1f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 16 Oct 2018 17:34:48 -0700 Subject: [PATCH 012/125] Some experimentation yields promising results... --- interface/src/Application.cpp | 1 + .../src/scripting/TTSScriptingInterface.cpp | 17 +- .../src/scripting/TTSScriptingInterface.h | 11 +- libraries/audio-client/src/AudioClient.cpp | 1607 +++++++++-------- libraries/audio-client/src/AudioClient.h | 19 +- 5 files changed, 844 insertions(+), 811 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 728fea8c10..2991fab5f7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1182,6 +1182,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto TTS = DependencyManager::get().data(); connect(TTS, &TTSScriptingInterface::ttsSampleCreated, audioIO, &AudioClient::handleTTSAudioInput); + connect(TTS, &TTSScriptingInterface::clearTTSBuffer, audioIO, &AudioClient::clearTTSBuffer); connect(audioIO, &AudioClient::inputReceived, [](const QByteArray& audio) { static auto recorder = DependencyManager::get(); diff --git a/interface/src/scripting/TTSScriptingInterface.cpp b/interface/src/scripting/TTSScriptingInterface.cpp index fdbb37e586..5fb47a73c3 100644 --- a/interface/src/scripting/TTSScriptingInterface.cpp +++ b/interface/src/scripting/TTSScriptingInterface.cpp @@ -65,7 +65,7 @@ void TTSScriptingInterface::testTone(const bool& alsoInject) { int16_t temp = (glm::sin(glm::radians((float)a))) * 32768; samples[a] = temp; } - emit ttsSampleCreated(_lastSoundByteArray); + emit ttsSampleCreated(_lastSoundByteArray, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * 50, 96); if (alsoInject) { AudioInjectorOptions options; @@ -75,11 +75,16 @@ void TTSScriptingInterface::testTone(const bool& alsoInject) { } } -void TTSScriptingInterface::speakText(const QString& textToSpeak, const bool& alsoInject) { +void TTSScriptingInterface::speakText(const QString& textToSpeak, + const int& newChunkSize, + const int& timerInterval, + const int& sampleRate, + const int& bitsPerSample, + const bool& alsoInject) { WAVEFORMATEX fmt; fmt.wFormatTag = WAVE_FORMAT_PCM; - fmt.nSamplesPerSec = 24000; - fmt.wBitsPerSample = 16; + fmt.nSamplesPerSec = sampleRate; + fmt.wBitsPerSample = bitsPerSample; fmt.nChannels = 1; fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; @@ -146,7 +151,7 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak, const bool& al _lastSoundByteArray.resize(0); _lastSoundByteArray.append(buf1, dwSize); - emit ttsSampleCreated(_lastSoundByteArray); + emit ttsSampleCreated(_lastSoundByteArray, newChunkSize, timerInterval); if (alsoInject) { AudioInjectorOptions options; @@ -160,4 +165,6 @@ void TTSScriptingInterface::stopLastSpeech() { if (_lastSoundAudioInjector) { _lastSoundAudioInjector->stop(); } + + emit clearTTSBuffer(); } diff --git a/interface/src/scripting/TTSScriptingInterface.h b/interface/src/scripting/TTSScriptingInterface.h index c1fffe67d1..f6eca081ab 100644 --- a/interface/src/scripting/TTSScriptingInterface.h +++ b/interface/src/scripting/TTSScriptingInterface.h @@ -19,6 +19,7 @@ #include // SAPI #include // SAPI Helper #include +#include class TTSScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -28,11 +29,17 @@ public: ~TTSScriptingInterface(); Q_INVOKABLE void testTone(const bool& alsoInject = false); - Q_INVOKABLE void speakText(const QString& textToSpeak, const bool& alsoInject = false); + Q_INVOKABLE void speakText(const QString& textToSpeak, + const int& newChunkSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * 50), + const int& timerInterval = 96, + const int& sampleRate = 24000, + const int& bitsPerSample = 16, + const bool& alsoInject = false); Q_INVOKABLE void stopLastSpeech(); signals: - void ttsSampleCreated(QByteArray outputArray); + void ttsSampleCreated(QByteArray outputArray, const int& newChunkSize, const int& timerInterval); + void clearTTSBuffer(); private: class CComAutoInit { diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index c2b066b716..606763e4ab 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -186,7 +186,7 @@ AudioClient::AudioClient() : _networkToOutputResampler(NULL), _localToOutputResampler(NULL), _audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), _outgoingAvatarAudioSequenceNumber(0), _audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this), _stats(&_receivedAudioStream), - _positionGetter(DEFAULT_POSITION_GETTER), + _positionGetter(DEFAULT_POSITION_GETTER), _TTSTimer(this), #if defined(Q_OS_ANDROID) _checkInputTimer(this), _isHeadsetPluggedIn(false), #endif @@ -245,6 +245,8 @@ AudioClient::AudioClient() : packetReceiver.registerListener(PacketType::NoisyMute, this, "handleNoisyMutePacket"); packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); + + connect(&_TTSTimer, &QTimer::timeout, this, &AudioClient::processTTSBuffer); } AudioClient::~AudioClient() { @@ -939,7 +941,7 @@ void AudioClient::setReverbOptions(const AudioEffectOptions* options) { } } -void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { +void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray, const int& sampleRate, const int& channelCount) { // If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here. bool hasReverb = _reverb || _receivedAudioStream.hasReverb(); if (_muted || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) { @@ -949,7 +951,7 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { // NOTE: we assume the inputFormat and the outputFormat are the same, since on any modern // multimedia OS they should be. If there is a device that this is not true for, we can // add back support to do resampling. - if (_inputFormat.sampleRate() != _outputFormat.sampleRate()) { + if (sampleRate != _outputFormat.sampleRate()) { return; } @@ -972,7 +974,7 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { static QByteArray loopBackByteArray; int numInputSamples = inputByteArray.size() / AudioConstants::SAMPLE_SIZE; - int numLoopbackSamples = (numInputSamples * OUTPUT_CHANNEL_COUNT) / _inputFormat.channelCount(); + int numLoopbackSamples = (numInputSamples * OUTPUT_CHANNEL_COUNT) / channelCount; loopBackByteArray.resize(numLoopbackSamples * AudioConstants::SAMPLE_SIZE); @@ -980,7 +982,7 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { int16_t* loopbackSamples = reinterpret_cast(loopBackByteArray.data()); // upmix mono to stereo - if (!sampleChannelConversion(inputSamples, loopbackSamples, numInputSamples, _inputFormat.channelCount(), + if (!sampleChannelConversion(inputSamples, loopbackSamples, numInputSamples, channelCount, OUTPUT_CHANNEL_COUNT)) { // no conversion, just copy the samples memcpy(loopbackSamples, inputSamples, numInputSamples * AudioConstants::SAMPLE_SIZE); @@ -1093,23 +1095,29 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { } } -void AudioClient::processAudioAndAddToRingBuffer(QByteArray& inputByteArray, - const uchar& channelCount, - const qint32& bytesForDuration, - QByteArray& rollingBuffer) { +void AudioClient::handleMicAudioInput() { + if (!_inputDevice || _isPlayingBackRecording) { + return; + } + +#if defined(Q_OS_ANDROID) + _inputReadsSinceLastCheck++; +#endif + // input samples required to produce exactly NETWORK_FRAME_SAMPLES of output const int inputSamplesRequired = (_inputToNetworkResampler ? _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * - channelCount; + _inputFormat.channelCount(); const auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); + QByteArray inputByteArray = _inputDevice->readAll(); - handleLocalEchoAndReverb(inputByteArray); + handleLocalEchoAndReverb(inputByteArray, _inputFormat.sampleRate(), _inputFormat.channelCount()); _inputRingBuffer.writeData(inputByteArray.data(), inputByteArray.size()); - float audioInputMsecsRead = inputByteArray.size() / (float)(bytesForDuration); + float audioInputMsecsRead = inputByteArray.size() / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); _stats.updateInputMsRead(audioInputMsecsRead); const int numNetworkBytes = @@ -1125,33 +1133,17 @@ void AudioClient::processAudioAndAddToRingBuffer(QByteArray& inputByteArray, } else { _inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired); possibleResampling(_inputToNetworkResampler, inputAudioSamples.get(), networkAudioSamples, inputSamplesRequired, - numNetworkSamples, channelCount, _desiredInputFormat.channelCount()); + numNetworkSamples, _inputFormat.channelCount(), _desiredInputFormat.channelCount()); } int bytesInInputRingBuffer = _inputRingBuffer.samplesAvailable() * AudioConstants::SAMPLE_SIZE; - float msecsInInputRingBuffer = bytesInInputRingBuffer / (float)(bytesForDuration); + float msecsInInputRingBuffer = bytesInInputRingBuffer / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); _stats.updateInputMsUnplayed(msecsInInputRingBuffer); QByteArray audioBuffer(reinterpret_cast(networkAudioSamples), numNetworkBytes); - rollingBuffer.append(audioBuffer); handleAudioInput(audioBuffer); } } -void AudioClient::handleMicAudioInput() { - if (!_inputDevice || _isPlayingBackRecording) { - return; - } - -#if defined(Q_OS_ANDROID) - _inputReadsSinceLastCheck++; -#endif - - QByteArray temp; - - processAudioAndAddToRingBuffer(_inputDevice->readAll(), _inputFormat.channelCount(), - _inputFormat.bytesForDuration(USECS_PER_MSEC), temp); -} - void AudioClient::handleDummyAudioInput() { const int numNetworkBytes = _isStereoInput ? AudioConstants::NETWORK_FRAME_BYTES_STEREO : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; @@ -1192,7 +1184,7 @@ int rawToWav(const char* rawData, const int& rawLength, const char* wavfn, long long chunksize = 0x10; fwrite(&chunksize, 4, 1, wav); - fmt.wFormatTag = 1; // WAVE_FORMAT_PCM + fmt.wFormatTag = 1; // WAVE_FORMAT_PCM fmt.wChannels = channels; fmt.dwSamplesPerSec = frequency * 1; fmt.wBitsPerSample = 16; @@ -1210,906 +1202,927 @@ int rawToWav(const char* rawData, const int& rawLength, const char* wavfn, long return 0; } -void AudioClient::handleTTSAudioInput(const QByteArray& audio) { - QByteArray audioBuffer(audio); - - QString filename = QString::number(usecTimestampNow()); - QString path = PathUtils::getAppDataPath() + "Audio/" + filename + "-before.wav"; - rawToWav(audioBuffer.data(), audioBuffer.size(), path.toLocal8Bit(), 24000, 1); - - QByteArray temp; - - while (audioBuffer.size() > 0) { +void AudioClient::processTTSBuffer() { + Lock lock(_TTSMutex); + if (_TTSAudioBuffer.size() > 0) { QByteArray part; - part.append(audioBuffer.data(), AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); - audioBuffer.remove(0, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); - processAudioAndAddToRingBuffer(part, 1, 48, temp); + part.append(_TTSAudioBuffer.data(), _TTSChunkSize); + _TTSAudioBuffer.remove(0, _TTSChunkSize); + handleAudioInput(part); + } else { + _isProcessingTTS = false; + _TTSTimer.stop(); } +} - filename = QString::number(usecTimestampNow()); - path = PathUtils::getAppDataPath() + "Audio/" + filename + "-after.wav"; - rawToWav(temp.data(), temp.size(), path.toLocal8Bit(), 12000, 1); +void AudioClient::handleTTSAudioInput(const QByteArray& audio, const int& newChunkSize, const int& timerInterval) { + _TTSChunkSize = newChunkSize; + _TTSAudioBuffer.append(audio); + + handleLocalEchoAndReverb(_TTSAudioBuffer, 48000, 1); + + //QString filename = QString::number(usecTimestampNow()); + //QString path = PathUtils::getAppDataPath() + "Audio/" + filename + "-before.wav"; + //rawToWav(_TTSAudioBuffer.data(), _TTSAudioBuffer.size(), path.toLocal8Bit(), 24000, 1); + + //QByteArray temp; + + _isProcessingTTS = true; + _TTSTimer.start(timerInterval); + + //filename = QString::number(usecTimestampNow()); + //path = PathUtils::getAppDataPath() + "Audio/" + filename + "-after.wav"; + //rawToWav(temp.data(), temp.size(), path.toLocal8Bit(), 12000, 1); +} + +void AudioClient::clearTTSBuffer() { + _TTSAudioBuffer.resize(0); + _isProcessingTTS = false; + _TTSTimer.stop(); } void AudioClient::prepareLocalAudioInjectors(std::unique_ptr localAudioLock) { - bool doSynchronously = localAudioLock.operator bool(); - if (!localAudioLock) { - localAudioLock.reset(new Lock(_localAudioMutex)); + bool doSynchronously = localAudioLock.operator bool(); + if (!localAudioLock) { + localAudioLock.reset(new Lock(_localAudioMutex)); + } + + int samplesNeeded = std::numeric_limits::max(); + while (samplesNeeded > 0) { + if (!doSynchronously) { + // unlock between every write to allow device switching + localAudioLock->unlock(); + localAudioLock->lock(); + } + + // in case of a device switch, consider bufferCapacity volatile across iterations + if (_outputPeriod == 0) { + return; + } + + int bufferCapacity = _localInjectorsStream.getSampleCapacity(); + int maxOutputSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * AudioConstants::STEREO; + if (_localToOutputResampler) { + maxOutputSamples = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * + AudioConstants::STEREO; + } + + samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed); + if (samplesNeeded < maxOutputSamples) { + // avoid overwriting the buffer to prevent losing frames + break; + } + + // get a network frame of local injectors' audio + if (!mixLocalAudioInjectors(_localMixBuffer)) { + break; + } + + // reverb + if (_reverb) { + _localReverb.render(_localMixBuffer, _localMixBuffer, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + } + + int samples; + if (_localToOutputResampler) { + // resample to output sample rate + int frames = _localToOutputResampler->render(_localMixBuffer, _localOutputMixBuffer, + AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + + // write to local injectors' ring buffer + samples = frames * AudioConstants::STEREO; + _localInjectorsStream.writeSamples(_localOutputMixBuffer, samples); + + } else { + // write to local injectors' ring buffer + samples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; + _localInjectorsStream.writeSamples(_localMixBuffer, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO); + } + + _localSamplesAvailable.fetch_add(samples, std::memory_order_release); + samplesNeeded -= samples; + } } - int samplesNeeded = std::numeric_limits::max(); - while (samplesNeeded > 0) { - if (!doSynchronously) { - // unlock between every write to allow device switching - localAudioLock->unlock(); - localAudioLock->lock(); + bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { + // check the flag for injectors before attempting to lock + if (!_localInjectorsAvailable.load(std::memory_order_acquire)) { + return false; } - // in case of a device switch, consider bufferCapacity volatile across iterations - if (_outputPeriod == 0) { - return; - } + // lock the injectors + Lock lock(_injectorsMutex); - int bufferCapacity = _localInjectorsStream.getSampleCapacity(); - int maxOutputSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * AudioConstants::STEREO; - if (_localToOutputResampler) { - maxOutputSamples = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * - AudioConstants::STEREO; - } + QVector injectorsToRemove; - samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed); - if (samplesNeeded < maxOutputSamples) { - // avoid overwriting the buffer to prevent losing frames - break; - } + memset(mixBuffer, 0, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO * sizeof(float)); - // get a network frame of local injectors' audio - if (!mixLocalAudioInjectors(_localMixBuffer)) { - break; - } + for (const AudioInjectorPointer& injector : _activeLocalAudioInjectors) { + // the lock guarantees that injectorBuffer, if found, is invariant + AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer(); + if (injectorBuffer) { + static const int HRTF_DATASET_INDEX = 1; - // reverb - if (_reverb) { - _localReverb.render(_localMixBuffer, _localMixBuffer, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); - } + int numChannels = injector->isAmbisonic() + ? AudioConstants::AMBISONIC + : (injector->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); + size_t bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; - int samples; - if (_localToOutputResampler) { - // resample to output sample rate - int frames = _localToOutputResampler->render(_localMixBuffer, _localOutputMixBuffer, - AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + // get one frame from the injector + memset(_localScratchBuffer, 0, bytesToRead); + if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) { + if (injector->isAmbisonic()) { + // no distance attenuation + float gain = injector->getVolume(); - // write to local injectors' ring buffer - samples = frames * AudioConstants::STEREO; - _localInjectorsStream.writeSamples(_localOutputMixBuffer, samples); + // + // Calculate the soundfield orientation relative to the listener. + // Injector orientation can be used to align a recording to our world coordinates. + // + glm::quat relativeOrientation = injector->getOrientation() * glm::inverse(_orientationGetter()); - } else { - // write to local injectors' ring buffer - samples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; - _localInjectorsStream.writeSamples(_localMixBuffer, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO); - } + // convert from Y-up (OpenGL) to Z-up (Ambisonic) coordinate system + float qw = relativeOrientation.w; + float qx = -relativeOrientation.z; + float qy = -relativeOrientation.x; + float qz = relativeOrientation.y; - _localSamplesAvailable.fetch_add(samples, std::memory_order_release); - samplesNeeded -= samples; - } -} + // Ambisonic gets spatialized into mixBuffer + injector->getLocalFOA().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, qw, qx, qy, qz, gain, + AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); -bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { - // check the flag for injectors before attempting to lock - if (!_localInjectorsAvailable.load(std::memory_order_acquire)) { - return false; - } + } else if (injector->isStereo()) { + // stereo gets directly mixed into mixBuffer + float gain = injector->getVolume(); + for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) { + mixBuffer[i] += convertToFloat(_localScratchBuffer[i]) * gain; + } - // lock the injectors - Lock lock(_injectorsMutex); + } else { + // calculate distance, gain and azimuth for hrtf + glm::vec3 relativePosition = injector->getPosition() - _positionGetter(); + float distance = glm::max(glm::length(relativePosition), EPSILON); + float gain = gainForSource(distance, injector->getVolume()); + float azimuth = azimuthForSource(relativePosition); - QVector injectorsToRemove; - - memset(mixBuffer, 0, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO * sizeof(float)); - - for (const AudioInjectorPointer& injector : _activeLocalAudioInjectors) { - // the lock guarantees that injectorBuffer, if found, is invariant - AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer(); - if (injectorBuffer) { - static const int HRTF_DATASET_INDEX = 1; - - int numChannels = injector->isAmbisonic() ? AudioConstants::AMBISONIC - : (injector->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); - size_t bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; - - // get one frame from the injector - memset(_localScratchBuffer, 0, bytesToRead); - if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) { - if (injector->isAmbisonic()) { - // no distance attenuation - float gain = injector->getVolume(); - - // - // Calculate the soundfield orientation relative to the listener. - // Injector orientation can be used to align a recording to our world coordinates. - // - glm::quat relativeOrientation = injector->getOrientation() * glm::inverse(_orientationGetter()); - - // convert from Y-up (OpenGL) to Z-up (Ambisonic) coordinate system - float qw = relativeOrientation.w; - float qx = -relativeOrientation.z; - float qy = -relativeOrientation.x; - float qz = relativeOrientation.y; - - // Ambisonic gets spatialized into mixBuffer - injector->getLocalFOA().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, qw, qx, qy, qz, gain, - AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); - - } else if (injector->isStereo()) { - // stereo gets directly mixed into mixBuffer - float gain = injector->getVolume(); - for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) { - mixBuffer[i] += convertToFloat(_localScratchBuffer[i]) * gain; + // mono gets spatialized into mixBuffer + injector->getLocalHRTF().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, azimuth, distance, + gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); } } else { - // calculate distance, gain and azimuth for hrtf - glm::vec3 relativePosition = injector->getPosition() - _positionGetter(); - float distance = glm::max(glm::length(relativePosition), EPSILON); - float gain = gainForSource(distance, injector->getVolume()); - float azimuth = azimuthForSource(relativePosition); - - // mono gets spatialized into mixBuffer - injector->getLocalHRTF().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, azimuth, distance, gain, - AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + qCDebug(audioclient) << "injector has no more data, marking finished for removal"; + injector->finishLocalInjection(); + injectorsToRemove.append(injector); } } else { - qCDebug(audioclient) << "injector has no more data, marking finished for removal"; + qCDebug(audioclient) << "injector has no local buffer, marking as finished for removal"; injector->finishLocalInjection(); injectorsToRemove.append(injector); } - - } else { - qCDebug(audioclient) << "injector has no local buffer, marking as finished for removal"; - injector->finishLocalInjection(); - injectorsToRemove.append(injector); - } - } - - for (const AudioInjectorPointer& injector : injectorsToRemove) { - qCDebug(audioclient) << "removing injector"; - _activeLocalAudioInjectors.removeOne(injector); - } - - // update the flag - _localInjectorsAvailable.exchange(!_activeLocalAudioInjectors.empty(), std::memory_order_release); - - return true; -} - -void AudioClient::processReceivedSamples(const QByteArray& decodedBuffer, QByteArray& outputBuffer) { - const int16_t* decodedSamples = reinterpret_cast(decodedBuffer.data()); - assert(decodedBuffer.size() == AudioConstants::NETWORK_FRAME_BYTES_STEREO); - - outputBuffer.resize(_outputFrameSize * AudioConstants::SAMPLE_SIZE); - int16_t* outputSamples = reinterpret_cast(outputBuffer.data()); - - bool hasReverb = _reverb || _receivedAudioStream.hasReverb(); - - // apply stereo reverb - if (hasReverb) { - updateReverbOptions(); - int16_t* reverbSamples = _networkToOutputResampler ? _networkScratchBuffer : outputSamples; - _listenerReverb.render(decodedSamples, reverbSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); - } - - // resample to output sample rate - if (_networkToOutputResampler) { - const int16_t* inputSamples = hasReverb ? _networkScratchBuffer : decodedSamples; - _networkToOutputResampler->render(inputSamples, outputSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); - } - - // if no transformations were applied, we still need to copy the buffer - if (!hasReverb && !_networkToOutputResampler) { - memcpy(outputSamples, decodedSamples, decodedBuffer.size()); - } -} - -void AudioClient::sendMuteEnvironmentPacket() { - auto nodeList = DependencyManager::get(); - - int dataSize = sizeof(glm::vec3) + sizeof(float); - - auto mutePacket = NLPacket::create(PacketType::MuteEnvironment, dataSize); - - const float MUTE_RADIUS = 50; - - glm::vec3 currentSourcePosition = _positionGetter(); - - mutePacket->writePrimitive(currentSourcePosition); - mutePacket->writePrimitive(MUTE_RADIUS); - - // grab our audio mixer from the NodeList, if it exists - SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); - - if (audioMixer) { - // send off this mute packet - nodeList->sendPacket(std::move(mutePacket), *audioMixer); - } -} - -void AudioClient::setMuted(bool muted, bool emitSignal) { - if (_muted != muted) { - _muted = muted; - if (emitSignal) { - emit muteToggled(_muted); - } - } -} - -void AudioClient::setNoiseReduction(bool enable, bool emitSignal) { - if (_isNoiseGateEnabled != enable) { - _isNoiseGateEnabled = enable; - if (emitSignal) { - emit noiseReductionChanged(_isNoiseGateEnabled); - } - } -} - -bool AudioClient::setIsStereoInput(bool isStereoInput) { - bool stereoInputChanged = false; - if (isStereoInput != _isStereoInput && _inputDeviceInfo.supportedChannelCounts().contains(2)) { - _isStereoInput = isStereoInput; - stereoInputChanged = true; - - if (_isStereoInput) { - _desiredInputFormat.setChannelCount(2); - } else { - _desiredInputFormat.setChannelCount(1); } - // restart the codec - if (_codec) { - if (_encoder) { - _codec->releaseEncoder(_encoder); - } - _encoder = _codec->createEncoder(AudioConstants::SAMPLE_RATE, - _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); + for (const AudioInjectorPointer& injector : injectorsToRemove) { + qCDebug(audioclient) << "removing injector"; + _activeLocalAudioInjectors.removeOne(injector); } - qCDebug(audioclient) << "Reset Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput; - // restart the input device - switchInputToAudioDevice(_inputDeviceInfo); - - emit isStereoInputChanged(_isStereoInput); - } - - return stereoInputChanged; -} - -bool AudioClient::outputLocalInjector(const AudioInjectorPointer& injector) { - AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer(); - if (injectorBuffer) { - // local injectors are on the AudioInjectorsThread, so we must guard access - Lock lock(_injectorsMutex); - if (!_activeLocalAudioInjectors.contains(injector)) { - qCDebug(audioclient) << "adding new injector"; - _activeLocalAudioInjectors.append(injector); - // move local buffer to the LocalAudioThread to avoid dataraces with AudioInjector (like stop()) - injectorBuffer->setParent(nullptr); - - // update the flag - _localInjectorsAvailable.exchange(true, std::memory_order_release); - } else { - qCDebug(audioclient) << "injector exists in active list already"; - } + // update the flag + _localInjectorsAvailable.exchange(!_activeLocalAudioInjectors.empty(), std::memory_order_release); return true; - - } else { - // no local buffer - return false; } -} -void AudioClient::outputFormatChanged() { - _outputFrameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * OUTPUT_CHANNEL_COUNT * _outputFormat.sampleRate()) / - _desiredOutputFormat.sampleRate(); - _receivedAudioStream.outputFormatChanged(_outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); -} + void AudioClient::processReceivedSamples(const QByteArray& decodedBuffer, QByteArray& outputBuffer) { + const int16_t* decodedSamples = reinterpret_cast(decodedBuffer.data()); + assert(decodedBuffer.size() == AudioConstants::NETWORK_FRAME_BYTES_STEREO); -bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) { - Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); + outputBuffer.resize(_outputFrameSize * AudioConstants::SAMPLE_SIZE); + int16_t* outputSamples = reinterpret_cast(outputBuffer.data()); - qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]"; - bool supportedFormat = false; + bool hasReverb = _reverb || _receivedAudioStream.hasReverb(); - // NOTE: device start() uses the Qt internal device list - Lock lock(_deviceMutex); + // apply stereo reverb + if (hasReverb) { + updateReverbOptions(); + int16_t* reverbSamples = _networkToOutputResampler ? _networkScratchBuffer : outputSamples; + _listenerReverb.render(decodedSamples, reverbSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + } + + // resample to output sample rate + if (_networkToOutputResampler) { + const int16_t* inputSamples = hasReverb ? _networkScratchBuffer : decodedSamples; + _networkToOutputResampler->render(inputSamples, outputSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + } + + // if no transformations were applied, we still need to copy the buffer + if (!hasReverb && !_networkToOutputResampler) { + memcpy(outputSamples, decodedSamples, decodedBuffer.size()); + } + } + + void AudioClient::sendMuteEnvironmentPacket() { + auto nodeList = DependencyManager::get(); + + int dataSize = sizeof(glm::vec3) + sizeof(float); + + auto mutePacket = NLPacket::create(PacketType::MuteEnvironment, dataSize); + + const float MUTE_RADIUS = 50; + + glm::vec3 currentSourcePosition = _positionGetter(); + + mutePacket->writePrimitive(currentSourcePosition); + mutePacket->writePrimitive(MUTE_RADIUS); + + // grab our audio mixer from the NodeList, if it exists + SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); + + if (audioMixer) { + // send off this mute packet + nodeList->sendPacket(std::move(mutePacket), *audioMixer); + } + } + + void AudioClient::setMuted(bool muted, bool emitSignal) { + if (_muted != muted) { + _muted = muted; + if (emitSignal) { + emit muteToggled(_muted); + } + } + } + + void AudioClient::setNoiseReduction(bool enable, bool emitSignal) { + if (_isNoiseGateEnabled != enable) { + _isNoiseGateEnabled = enable; + if (emitSignal) { + emit noiseReductionChanged(_isNoiseGateEnabled); + } + } + } + + bool AudioClient::setIsStereoInput(bool isStereoInput) { + bool stereoInputChanged = false; + if (isStereoInput != _isStereoInput && _inputDeviceInfo.supportedChannelCounts().contains(2)) { + _isStereoInput = isStereoInput; + stereoInputChanged = true; + + if (_isStereoInput) { + _desiredInputFormat.setChannelCount(2); + } else { + _desiredInputFormat.setChannelCount(1); + } + + // restart the codec + if (_codec) { + if (_encoder) { + _codec->releaseEncoder(_encoder); + } + _encoder = _codec->createEncoder(AudioConstants::SAMPLE_RATE, + _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); + } + qCDebug(audioclient) << "Reset Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput; + + // restart the input device + switchInputToAudioDevice(_inputDeviceInfo); + + emit isStereoInputChanged(_isStereoInput); + } + + return stereoInputChanged; + } + + bool AudioClient::outputLocalInjector(const AudioInjectorPointer& injector) { + AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer(); + if (injectorBuffer) { + // local injectors are on the AudioInjectorsThread, so we must guard access + Lock lock(_injectorsMutex); + if (!_activeLocalAudioInjectors.contains(injector)) { + qCDebug(audioclient) << "adding new injector"; + _activeLocalAudioInjectors.append(injector); + // move local buffer to the LocalAudioThread to avoid dataraces with AudioInjector (like stop()) + injectorBuffer->setParent(nullptr); + + // update the flag + _localInjectorsAvailable.exchange(true, std::memory_order_release); + } else { + qCDebug(audioclient) << "injector exists in active list already"; + } + + return true; + + } else { + // no local buffer + return false; + } + } + + void AudioClient::outputFormatChanged() { + _outputFrameSize = + (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * OUTPUT_CHANNEL_COUNT * _outputFormat.sampleRate()) / + _desiredOutputFormat.sampleRate(); + _receivedAudioStream.outputFormatChanged(_outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); + } + + bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) { + Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); + + qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]"; + bool supportedFormat = false; + + // NOTE: device start() uses the Qt internal device list + Lock lock(_deviceMutex); #if defined(Q_OS_ANDROID) - _shouldRestartInputSetup = false; // avoid a double call to _audioInput->start() from audioInputStateChanged + _shouldRestartInputSetup = false; // avoid a double call to _audioInput->start() from audioInputStateChanged #endif - // cleanup any previously initialized device - if (_audioInput) { - // The call to stop() causes _inputDevice to be destructed. - // That in turn causes it to be disconnected (see for example - // http://stackoverflow.com/questions/9264750/qt-signals-and-slots-object-disconnect). - _audioInput->stop(); - _inputDevice = NULL; + // cleanup any previously initialized device + if (_audioInput) { + // The call to stop() causes _inputDevice to be destructed. + // That in turn causes it to be disconnected (see for example + // http://stackoverflow.com/questions/9264750/qt-signals-and-slots-object-disconnect). + _audioInput->stop(); + _inputDevice = NULL; - _audioInput->deleteLater(); - _audioInput = NULL; - _numInputCallbackBytes = 0; + _audioInput->deleteLater(); + _audioInput = NULL; + _numInputCallbackBytes = 0; - _inputDeviceInfo = QAudioDeviceInfo(); - } + _inputDeviceInfo = QAudioDeviceInfo(); + } - if (_dummyAudioInput) { - _dummyAudioInput->stop(); + if (_dummyAudioInput) { + _dummyAudioInput->stop(); - _dummyAudioInput->deleteLater(); - _dummyAudioInput = NULL; - } + _dummyAudioInput->deleteLater(); + _dummyAudioInput = NULL; + } - if (_inputToNetworkResampler) { - // if we were using an input to network resampler, delete it here - delete _inputToNetworkResampler; - _inputToNetworkResampler = NULL; - } + if (_inputToNetworkResampler) { + // if we were using an input to network resampler, delete it here + delete _inputToNetworkResampler; + _inputToNetworkResampler = NULL; + } - if (_audioGate) { - delete _audioGate; - _audioGate = nullptr; - } + if (_audioGate) { + delete _audioGate; + _audioGate = nullptr; + } - if (isShutdownRequest) { - qCDebug(audioclient) << "The audio input device has shut down."; - return true; - } + if (isShutdownRequest) { + qCDebug(audioclient) << "The audio input device has shut down."; + return true; + } - if (!inputDeviceInfo.isNull()) { - qCDebug(audioclient) << "The audio input device " << inputDeviceInfo.deviceName() << "is available."; - _inputDeviceInfo = inputDeviceInfo; - emit deviceChanged(QAudio::AudioInput, inputDeviceInfo); + if (!inputDeviceInfo.isNull()) { + qCDebug(audioclient) << "The audio input device " << inputDeviceInfo.deviceName() << "is available."; + _inputDeviceInfo = inputDeviceInfo; + emit deviceChanged(QAudio::AudioInput, inputDeviceInfo); - if (adjustedFormatForAudioDevice(inputDeviceInfo, _desiredInputFormat, _inputFormat)) { - qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat; + if (adjustedFormatForAudioDevice(inputDeviceInfo, _desiredInputFormat, _inputFormat)) { + qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat; - // we've got the best we can get for input - // if required, setup a resampler for this input to our desired network format - if (_inputFormat != _desiredInputFormat && _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) { - qCDebug(audioclient) << "Attemping to create a resampler for input format to network format."; + // we've got the best we can get for input + // if required, setup a resampler for this input to our desired network format + if (_inputFormat != _desiredInputFormat && _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) { + qCDebug(audioclient) << "Attemping to create a resampler for input format to network format."; - assert(_inputFormat.sampleSize() == 16); - assert(_desiredInputFormat.sampleSize() == 16); - int channelCount = (_inputFormat.channelCount() == 2 && _desiredInputFormat.channelCount() == 2) ? 2 : 1; + assert(_inputFormat.sampleSize() == 16); + assert(_desiredInputFormat.sampleSize() == 16); + int channelCount = (_inputFormat.channelCount() == 2 && _desiredInputFormat.channelCount() == 2) ? 2 : 1; - _inputToNetworkResampler = - new AudioSRC(_inputFormat.sampleRate(), _desiredInputFormat.sampleRate(), channelCount); + _inputToNetworkResampler = + new AudioSRC(_inputFormat.sampleRate(), _desiredInputFormat.sampleRate(), channelCount); - } else { - qCDebug(audioclient) << "No resampling required for audio input to match desired network format."; + } else { + qCDebug(audioclient) << "No resampling required for audio input to match desired network format."; + } + + // the audio gate runs after the resampler + _audioGate = new AudioGate(_desiredInputFormat.sampleRate(), _desiredInputFormat.channelCount()); + qCDebug(audioclient) << "Noise gate created with" << _desiredInputFormat.channelCount() << "channels."; + + // if the user wants stereo but this device can't provide then bail + if (!_isStereoInput || _inputFormat.channelCount() == 2) { + _audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this); + _numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat); + _audioInput->setBufferSize(_numInputCallbackBytes); + // different audio input devices may have different volumes + emit inputVolumeChanged(_audioInput->volume()); + + // how do we want to handle input working, but output not working? + int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes); + _inputRingBuffer.resizeForFrameSize(numFrameSamples); + +#if defined(Q_OS_ANDROID) + if (_audioInput) { + _shouldRestartInputSetup = true; + connect(_audioInput, &QAudioInput::stateChanged, this, &AudioClient::audioInputStateChanged); + } +#endif + _inputDevice = _audioInput->start(); + + if (_inputDevice) { + connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); + supportedFormat = true; + } else { + qCDebug(audioclient) << "Error starting audio input -" << _audioInput->error(); + _audioInput->deleteLater(); + _audioInput = NULL; + } + } } + } + + // If there is no working input device, use the dummy input device. + // It generates audio callbacks on a timer to simulate a mic stream of silent packets. + // This enables clients without a mic to still receive an audio stream from the mixer. + if (!_audioInput) { + qCDebug(audioclient) << "Audio input device is not available, using dummy input."; + _inputDeviceInfo = QAudioDeviceInfo(); + emit deviceChanged(QAudio::AudioInput, _inputDeviceInfo); + + _inputFormat = _desiredInputFormat; + qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat; + qCDebug(audioclient) << "No resampling required for audio input to match desired network format."; - // the audio gate runs after the resampler _audioGate = new AudioGate(_desiredInputFormat.sampleRate(), _desiredInputFormat.channelCount()); qCDebug(audioclient) << "Noise gate created with" << _desiredInputFormat.channelCount() << "channels."; - // if the user wants stereo but this device can't provide then bail - if (!_isStereoInput || _inputFormat.channelCount() == 2) { - _audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this); - _numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat); - _audioInput->setBufferSize(_numInputCallbackBytes); - // different audio input devices may have different volumes - emit inputVolumeChanged(_audioInput->volume()); - - // how do we want to handle input working, but output not working? - int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes); - _inputRingBuffer.resizeForFrameSize(numFrameSamples); - -#if defined(Q_OS_ANDROID) - if (_audioInput) { - _shouldRestartInputSetup = true; - connect(_audioInput, &QAudioInput::stateChanged, this, &AudioClient::audioInputStateChanged); - } -#endif - _inputDevice = _audioInput->start(); - - if (_inputDevice) { - connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); - supportedFormat = true; - } else { - qCDebug(audioclient) << "Error starting audio input -" << _audioInput->error(); - _audioInput->deleteLater(); - _audioInput = NULL; - } - } - } - } - - // If there is no working input device, use the dummy input device. - // It generates audio callbacks on a timer to simulate a mic stream of silent packets. - // This enables clients without a mic to still receive an audio stream from the mixer. - if (!_audioInput) { - qCDebug(audioclient) << "Audio input device is not available, using dummy input."; - _inputDeviceInfo = QAudioDeviceInfo(); - emit deviceChanged(QAudio::AudioInput, _inputDeviceInfo); - - _inputFormat = _desiredInputFormat; - qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat; - qCDebug(audioclient) << "No resampling required for audio input to match desired network format."; - - _audioGate = new AudioGate(_desiredInputFormat.sampleRate(), _desiredInputFormat.channelCount()); - qCDebug(audioclient) << "Noise gate created with" << _desiredInputFormat.channelCount() << "channels."; - - // generate audio callbacks at the network sample rate - _dummyAudioInput = new QTimer(this); - connect(_dummyAudioInput, SIGNAL(timeout()), this, SLOT(handleDummyAudioInput())); - _dummyAudioInput->start((int)(AudioConstants::NETWORK_FRAME_MSECS + 0.5f)); - } - - return supportedFormat; -} - -void AudioClient::audioInputStateChanged(QAudio::State state) { -#if defined(Q_OS_ANDROID) - switch (state) { - case QAudio::StoppedState: - if (!_audioInput) { - break; - } - // Stopped on purpose - if (_shouldRestartInputSetup) { - Lock lock(_deviceMutex); - _inputDevice = _audioInput->start(); - lock.unlock(); - if (_inputDevice) { - connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); - } - } - break; - case QAudio::ActiveState: - break; - default: - break; - } -#endif -} - -void AudioClient::checkInputTimeout() { -#if defined(Q_OS_ANDROID) - if (_audioInput && _inputReadsSinceLastCheck < MIN_READS_TO_CONSIDER_INPUT_ALIVE) { - _audioInput->stop(); - } else { - _inputReadsSinceLastCheck = 0; - } -#endif -} - -void AudioClient::setHeadsetPluggedIn(bool pluggedIn) { -#if defined(Q_OS_ANDROID) - if (pluggedIn == !_isHeadsetPluggedIn && !_inputDeviceInfo.isNull()) { - QAndroidJniObject brand = QAndroidJniObject::getStaticObjectField("android/os/Build", "BRAND"); - // some samsung phones needs more time to shutdown the previous input device - if (brand.toString().contains("samsung", Qt::CaseInsensitive)) { - switchInputToAudioDevice(QAudioDeviceInfo(), true); - QThread::msleep(200); + // generate audio callbacks at the network sample rate + _dummyAudioInput = new QTimer(this); + connect(_dummyAudioInput, SIGNAL(timeout()), this, SLOT(handleDummyAudioInput())); + _dummyAudioInput->start((int)(AudioConstants::NETWORK_FRAME_MSECS + 0.5f)); } - Setting::Handle enableAEC(SETTING_AEC_KEY, false); - bool aecEnabled = enableAEC.get(); - - if ((pluggedIn || !aecEnabled) && _inputDeviceInfo.deviceName() != VOICE_RECOGNITION) { - switchAudioDevice(QAudio::AudioInput, VOICE_RECOGNITION); - } else if (!pluggedIn && aecEnabled && _inputDeviceInfo.deviceName() != VOICE_COMMUNICATION) { - switchAudioDevice(QAudio::AudioInput, VOICE_COMMUNICATION); - } + return supportedFormat; } - _isHeadsetPluggedIn = pluggedIn; -#endif -} -void AudioClient::outputNotify() { - int recentUnfulfilled = _audioOutputIODevice.getRecentUnfulfilledReads(); - if (recentUnfulfilled > 0) { - qCDebug(audioclient, "Starve detected, %d new unfulfilled reads", recentUnfulfilled); - - if (_outputStarveDetectionEnabled.get()) { - quint64 now = usecTimestampNow() / 1000; - int dt = (int)(now - _outputStarveDetectionStartTimeMsec); - if (dt > STARVE_DETECTION_PERIOD) { - _outputStarveDetectionStartTimeMsec = now; - _outputStarveDetectionCount = 0; - } else { - _outputStarveDetectionCount += recentUnfulfilled; - if (_outputStarveDetectionCount > STARVE_DETECTION_THRESHOLD) { - int oldOutputBufferSizeFrames = _sessionOutputBufferSizeFrames; - int newOutputBufferSizeFrames = setOutputBufferSize(oldOutputBufferSizeFrames + 1, false); - - if (newOutputBufferSizeFrames > oldOutputBufferSizeFrames) { - qCDebug(audioclient, "Starve threshold surpassed (%d starves in %d ms)", _outputStarveDetectionCount, - dt); + void AudioClient::audioInputStateChanged(QAudio::State state) { +#if defined(Q_OS_ANDROID) + switch (state) { + case QAudio::StoppedState: + if (!_audioInput) { + break; + } + // Stopped on purpose + if (_shouldRestartInputSetup) { + Lock lock(_deviceMutex); + _inputDevice = _audioInput->start(); + lock.unlock(); + if (_inputDevice) { + connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); } + } + break; + case QAudio::ActiveState: + break; + default: + break; + } +#endif + } + void AudioClient::checkInputTimeout() { +#if defined(Q_OS_ANDROID) + if (_audioInput && _inputReadsSinceLastCheck < MIN_READS_TO_CONSIDER_INPUT_ALIVE) { + _audioInput->stop(); + } else { + _inputReadsSinceLastCheck = 0; + } +#endif + } + + void AudioClient::setHeadsetPluggedIn(bool pluggedIn) { +#if defined(Q_OS_ANDROID) + if (pluggedIn == !_isHeadsetPluggedIn && !_inputDeviceInfo.isNull()) { + QAndroidJniObject brand = QAndroidJniObject::getStaticObjectField("android/os/Build", "BRAND"); + // some samsung phones needs more time to shutdown the previous input device + if (brand.toString().contains("samsung", Qt::CaseInsensitive)) { + switchInputToAudioDevice(QAudioDeviceInfo(), true); + QThread::msleep(200); + } + + Setting::Handle enableAEC(SETTING_AEC_KEY, false); + bool aecEnabled = enableAEC.get(); + + if ((pluggedIn || !aecEnabled) && _inputDeviceInfo.deviceName() != VOICE_RECOGNITION) { + switchAudioDevice(QAudio::AudioInput, VOICE_RECOGNITION); + } else if (!pluggedIn && aecEnabled && _inputDeviceInfo.deviceName() != VOICE_COMMUNICATION) { + switchAudioDevice(QAudio::AudioInput, VOICE_COMMUNICATION); + } + } + _isHeadsetPluggedIn = pluggedIn; +#endif + } + + void AudioClient::outputNotify() { + int recentUnfulfilled = _audioOutputIODevice.getRecentUnfulfilledReads(); + if (recentUnfulfilled > 0) { + qCDebug(audioclient, "Starve detected, %d new unfulfilled reads", recentUnfulfilled); + + if (_outputStarveDetectionEnabled.get()) { + quint64 now = usecTimestampNow() / 1000; + int dt = (int)(now - _outputStarveDetectionStartTimeMsec); + if (dt > STARVE_DETECTION_PERIOD) { _outputStarveDetectionStartTimeMsec = now; _outputStarveDetectionCount = 0; + } else { + _outputStarveDetectionCount += recentUnfulfilled; + if (_outputStarveDetectionCount > STARVE_DETECTION_THRESHOLD) { + int oldOutputBufferSizeFrames = _sessionOutputBufferSizeFrames; + int newOutputBufferSizeFrames = setOutputBufferSize(oldOutputBufferSizeFrames + 1, false); + + if (newOutputBufferSizeFrames > oldOutputBufferSizeFrames) { + qCDebug(audioclient, "Starve threshold surpassed (%d starves in %d ms)", + _outputStarveDetectionCount, dt); + } + + _outputStarveDetectionStartTimeMsec = now; + _outputStarveDetectionCount = 0; + } } } } } -} -bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) { - Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); + bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) { + Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); - qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() - << "]"; - bool supportedFormat = false; + qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() + << "]"; + bool supportedFormat = false; - // NOTE: device start() uses the Qt internal device list - Lock lock(_deviceMutex); + // NOTE: device start() uses the Qt internal device list + Lock lock(_deviceMutex); - Lock localAudioLock(_localAudioMutex); - _localSamplesAvailable.exchange(0, std::memory_order_release); + Lock localAudioLock(_localAudioMutex); + _localSamplesAvailable.exchange(0, std::memory_order_release); - // cleanup any previously initialized device - if (_audioOutput) { - _audioOutputIODevice.close(); - _audioOutput->stop(); + // cleanup any previously initialized device + if (_audioOutput) { + _audioOutputIODevice.close(); + _audioOutput->stop(); - //must be deleted in next eventloop cycle when its called from notify() - _audioOutput->deleteLater(); - _audioOutput = NULL; + //must be deleted in next eventloop cycle when its called from notify() + _audioOutput->deleteLater(); + _audioOutput = NULL; - _loopbackOutputDevice = NULL; - //must be deleted in next eventloop cycle when its called from notify() - _loopbackAudioOutput->deleteLater(); - _loopbackAudioOutput = NULL; + _loopbackOutputDevice = NULL; + //must be deleted in next eventloop cycle when its called from notify() + _loopbackAudioOutput->deleteLater(); + _loopbackAudioOutput = NULL; - delete[] _outputMixBuffer; - _outputMixBuffer = NULL; + delete[] _outputMixBuffer; + _outputMixBuffer = NULL; - delete[] _outputScratchBuffer; - _outputScratchBuffer = NULL; + delete[] _outputScratchBuffer; + _outputScratchBuffer = NULL; - delete[] _localOutputMixBuffer; - _localOutputMixBuffer = NULL; + delete[] _localOutputMixBuffer; + _localOutputMixBuffer = NULL; - _outputDeviceInfo = QAudioDeviceInfo(); - } + _outputDeviceInfo = QAudioDeviceInfo(); + } - if (_networkToOutputResampler) { - // if we were using an input to network resampler, delete it here - delete _networkToOutputResampler; - _networkToOutputResampler = NULL; + if (_networkToOutputResampler) { + // if we were using an input to network resampler, delete it here + delete _networkToOutputResampler; + _networkToOutputResampler = NULL; - delete _localToOutputResampler; - _localToOutputResampler = NULL; - } + delete _localToOutputResampler; + _localToOutputResampler = NULL; + } - if (isShutdownRequest) { - qCDebug(audioclient) << "The audio output device has shut down."; - return true; - } + if (isShutdownRequest) { + qCDebug(audioclient) << "The audio output device has shut down."; + return true; + } - if (!outputDeviceInfo.isNull()) { - qCDebug(audioclient) << "The audio output device " << outputDeviceInfo.deviceName() << "is available."; - _outputDeviceInfo = outputDeviceInfo; - emit deviceChanged(QAudio::AudioOutput, outputDeviceInfo); + if (!outputDeviceInfo.isNull()) { + qCDebug(audioclient) << "The audio output device " << outputDeviceInfo.deviceName() << "is available."; + _outputDeviceInfo = outputDeviceInfo; + emit deviceChanged(QAudio::AudioOutput, outputDeviceInfo); - if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) { - qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat; + if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) { + qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat; - // we've got the best we can get for input - // if required, setup a resampler for this input to our desired network format - if (_desiredOutputFormat != _outputFormat && _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) { - qCDebug(audioclient) << "Attemping to create a resampler for network format to output format."; + // we've got the best we can get for input + // if required, setup a resampler for this input to our desired network format + if (_desiredOutputFormat != _outputFormat && _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) { + qCDebug(audioclient) << "Attemping to create a resampler for network format to output format."; - assert(_desiredOutputFormat.sampleSize() == 16); - assert(_outputFormat.sampleSize() == 16); + assert(_desiredOutputFormat.sampleSize() == 16); + assert(_outputFormat.sampleSize() == 16); - _networkToOutputResampler = - new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); - _localToOutputResampler = - new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); + _networkToOutputResampler = + new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); + _localToOutputResampler = + new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); - } else { - qCDebug(audioclient) << "No resampling required for network output to match actual output format."; - } - - outputFormatChanged(); - - // setup our general output device for audio-mixer audio - _audioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); - - int deviceChannelCount = _outputFormat.channelCount(); - int frameSize = - (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / - _desiredOutputFormat.sampleRate(); - int requestedSize = _sessionOutputBufferSizeFrames * frameSize * AudioConstants::SAMPLE_SIZE; - _audioOutput->setBufferSize(requestedSize); - - // initialize mix buffers on the _audioOutput thread to avoid races - connect(_audioOutput, &QAudioOutput::stateChanged, [&, frameSize, requestedSize](QAudio::State state) { - if (state == QAudio::ActiveState) { - // restrict device callback to _outputPeriod samples - _outputPeriod = _audioOutput->periodSize() / AudioConstants::SAMPLE_SIZE; - // device callback may exceed reported period, so double it to avoid stutter - _outputPeriod *= 2; - - _outputMixBuffer = new float[_outputPeriod]; - _outputScratchBuffer = new int16_t[_outputPeriod]; - - // size local output mix buffer based on resampled network frame size - int networkPeriod = - _localToOutputResampler - ? _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO) - : AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; - _localOutputMixBuffer = new float[networkPeriod]; - - // local period should be at least twice the output period, - // in case two device reads happen before more data can be read (worst case) - int localPeriod = _outputPeriod * 2; - // round up to an exact multiple of networkPeriod - localPeriod = ((localPeriod + networkPeriod - 1) / networkPeriod) * networkPeriod; - // this ensures lowest latency without stutter from underrun - _localInjectorsStream.resizeForFrameSize(localPeriod); - - int bufferSize = _audioOutput->bufferSize(); - int bufferSamples = bufferSize / AudioConstants::SAMPLE_SIZE; - int bufferFrames = bufferSamples / (float)frameSize; - qCDebug(audioclient) << "frame (samples):" << frameSize; - qCDebug(audioclient) << "buffer (frames):" << bufferFrames; - qCDebug(audioclient) << "buffer (samples):" << bufferSamples; - qCDebug(audioclient) << "buffer (bytes):" << bufferSize; - qCDebug(audioclient) << "requested (bytes):" << requestedSize; - qCDebug(audioclient) << "period (samples):" << _outputPeriod; - qCDebug(audioclient) << "local buffer (samples):" << localPeriod; - - disconnect(_audioOutput, &QAudioOutput::stateChanged, 0, 0); - - // unlock to avoid a deadlock with the device callback (which always succeeds this initialization) - localAudioLock.unlock(); + } else { + qCDebug(audioclient) << "No resampling required for network output to match actual output format."; } - }); - connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify); - _audioOutputIODevice.start(); + outputFormatChanged(); - _audioOutput->start(&_audioOutputIODevice); + // setup our general output device for audio-mixer audio + _audioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); - // setup a loopback audio output device - _loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); + int deviceChannelCount = _outputFormat.channelCount(); + int frameSize = + (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / + _desiredOutputFormat.sampleRate(); + int requestedSize = _sessionOutputBufferSizeFrames * frameSize * AudioConstants::SAMPLE_SIZE; + _audioOutput->setBufferSize(requestedSize); - _timeSinceLastReceived.start(); + // initialize mix buffers on the _audioOutput thread to avoid races + connect(_audioOutput, &QAudioOutput::stateChanged, [&, frameSize, requestedSize](QAudio::State state) { + if (state == QAudio::ActiveState) { + // restrict device callback to _outputPeriod samples + _outputPeriod = _audioOutput->periodSize() / AudioConstants::SAMPLE_SIZE; + // device callback may exceed reported period, so double it to avoid stutter + _outputPeriod *= 2; - supportedFormat = true; + _outputMixBuffer = new float[_outputPeriod]; + _outputScratchBuffer = new int16_t[_outputPeriod]; + + // size local output mix buffer based on resampled network frame size + int networkPeriod = + _localToOutputResampler + ? _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO) + : AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; + _localOutputMixBuffer = new float[networkPeriod]; + + // local period should be at least twice the output period, + // in case two device reads happen before more data can be read (worst case) + int localPeriod = _outputPeriod * 2; + // round up to an exact multiple of networkPeriod + localPeriod = ((localPeriod + networkPeriod - 1) / networkPeriod) * networkPeriod; + // this ensures lowest latency without stutter from underrun + _localInjectorsStream.resizeForFrameSize(localPeriod); + + int bufferSize = _audioOutput->bufferSize(); + int bufferSamples = bufferSize / AudioConstants::SAMPLE_SIZE; + int bufferFrames = bufferSamples / (float)frameSize; + qCDebug(audioclient) << "frame (samples):" << frameSize; + qCDebug(audioclient) << "buffer (frames):" << bufferFrames; + qCDebug(audioclient) << "buffer (samples):" << bufferSamples; + qCDebug(audioclient) << "buffer (bytes):" << bufferSize; + qCDebug(audioclient) << "requested (bytes):" << requestedSize; + qCDebug(audioclient) << "period (samples):" << _outputPeriod; + qCDebug(audioclient) << "local buffer (samples):" << localPeriod; + + disconnect(_audioOutput, &QAudioOutput::stateChanged, 0, 0); + + // unlock to avoid a deadlock with the device callback (which always succeeds this initialization) + localAudioLock.unlock(); + } + }); + connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify); + + _audioOutputIODevice.start(); + + _audioOutput->start(&_audioOutputIODevice); + + // setup a loopback audio output device + _loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); + + _timeSinceLastReceived.start(); + + supportedFormat = true; + } } + + return supportedFormat; } - return supportedFormat; -} + int AudioClient::setOutputBufferSize(int numFrames, bool persist) { + qCDebug(audioclient) << __FUNCTION__ << "numFrames:" << numFrames << "persist:" << persist; -int AudioClient::setOutputBufferSize(int numFrames, bool persist) { - qCDebug(audioclient) << __FUNCTION__ << "numFrames:" << numFrames << "persist:" << persist; + numFrames = std::min(std::max(numFrames, MIN_BUFFER_FRAMES), MAX_BUFFER_FRAMES); + qCDebug(audioclient) << __FUNCTION__ << "clamped numFrames:" << numFrames + << "_sessionOutputBufferSizeFrames:" << _sessionOutputBufferSizeFrames; - numFrames = std::min(std::max(numFrames, MIN_BUFFER_FRAMES), MAX_BUFFER_FRAMES); - qCDebug(audioclient) << __FUNCTION__ << "clamped numFrames:" << numFrames - << "_sessionOutputBufferSizeFrames:" << _sessionOutputBufferSizeFrames; - - if (numFrames != _sessionOutputBufferSizeFrames) { - qCInfo(audioclient, "Audio output buffer set to %d frames", numFrames); - _sessionOutputBufferSizeFrames = numFrames; - if (persist) { - _outputBufferSizeFrames.set(numFrames); + if (numFrames != _sessionOutputBufferSizeFrames) { + qCInfo(audioclient, "Audio output buffer set to %d frames", numFrames); + _sessionOutputBufferSizeFrames = numFrames; + if (persist) { + _outputBufferSizeFrames.set(numFrames); + } } + return numFrames; } - return numFrames; -} -// The following constant is operating system dependent due to differences in -// the way input audio is handled. The audio input buffer size is inversely -// proportional to the accelerator ratio. + // The following constant is operating system dependent due to differences in + // the way input audio is handled. The audio input buffer size is inversely + // proportional to the accelerator ratio. #ifdef Q_OS_WIN -const float AudioClient::CALLBACK_ACCELERATOR_RATIO = IsWindows8OrGreater() ? 1.0f : 0.25f; + const float AudioClient::CALLBACK_ACCELERATOR_RATIO = IsWindows8OrGreater() ? 1.0f : 0.25f; #endif #ifdef Q_OS_MAC -const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 2.0f; + const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 2.0f; #endif #ifdef Q_OS_ANDROID -const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 0.5f; + const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 0.5f; #elif defined(Q_OS_LINUX) -const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 2.0f; + const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 2.0f; #endif -int AudioClient::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const { - int numInputCallbackBytes = (int)(((AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * format.channelCount() * - ((float)format.sampleRate() / AudioConstants::SAMPLE_RATE)) / - CALLBACK_ACCELERATOR_RATIO) + - 0.5f); + int AudioClient::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const { + int numInputCallbackBytes = (int)(((AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * format.channelCount() * + ((float)format.sampleRate() / AudioConstants::SAMPLE_RATE)) / + CALLBACK_ACCELERATOR_RATIO) + + 0.5f); - return numInputCallbackBytes; -} - -int AudioClient::calculateNumberOfFrameSamples(int numBytes) const { - int frameSamples = (int)(numBytes * CALLBACK_ACCELERATOR_RATIO + 0.5f) / AudioConstants::SAMPLE_SIZE; - return frameSamples; -} - -float AudioClient::azimuthForSource(const glm::vec3& relativePosition) { - glm::quat inverseOrientation = glm::inverse(_orientationGetter()); - - glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition; - - // project the rotated source position vector onto the XZ plane - rotatedSourcePosition.y = 0.0f; - - static const float SOURCE_DISTANCE_THRESHOLD = 1e-30f; - - float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition); - if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) { - // produce an oriented angle about the y-axis - glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2)); - float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" - return (direction.x < 0.0f) ? -angle : angle; - - } else { - // no azimuth if they are in same spot - return 0.0f; - } -} - -float AudioClient::gainForSource(float distance, float volume) { - // attenuation = -6dB * log2(distance) - // reference attenuation of 0dB at distance = 1.0m - float gain = volume / std::max(distance, HRTF_NEARFIELD_MIN); - - return gain; -} - -qint64 AudioClient::AudioOutputIODevice::readData(char* data, qint64 maxSize) { - // samples requested from OUTPUT_CHANNEL_COUNT - int deviceChannelCount = _audio->_outputFormat.channelCount(); - int samplesRequested = (int)(maxSize / AudioConstants::SAMPLE_SIZE) * OUTPUT_CHANNEL_COUNT / deviceChannelCount; - // restrict samplesRequested to the size of our mix/scratch buffers - samplesRequested = std::min(samplesRequested, _audio->_outputPeriod); - - int16_t* scratchBuffer = _audio->_outputScratchBuffer; - float* mixBuffer = _audio->_outputMixBuffer; - - int networkSamplesPopped; - if ((networkSamplesPopped = _receivedAudioStream.popSamples(samplesRequested, false)) > 0) { - qCDebug(audiostream, "Read %d samples from buffer (%d available, %d requested)", networkSamplesPopped, - _receivedAudioStream.getSamplesAvailable(), samplesRequested); - AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput(); - lastPopOutput.readSamples(scratchBuffer, networkSamplesPopped); - for (int i = 0; i < networkSamplesPopped; i++) { - mixBuffer[i] = convertToFloat(scratchBuffer[i]); - } - samplesRequested = networkSamplesPopped; + return numInputCallbackBytes; } - int injectorSamplesPopped = 0; - { - bool append = networkSamplesPopped > 0; - // check the samples we have available locklessly; this is possible because only two functions add to the count: - // - prepareLocalAudioInjectors will only increase samples count - // - switchOutputToAudioDevice will zero samples count, - // stop the device - so that readData will exhaust the existing buffer or see a zeroed samples count, - // and start the device - which can then only see a zeroed samples count - int samplesAvailable = _audio->_localSamplesAvailable.load(std::memory_order_acquire); - - // if we do not have enough samples buffered despite having injectors, buffer them synchronously - if (samplesAvailable < samplesRequested && _audio->_localInjectorsAvailable.load(std::memory_order_acquire)) { - // try_to_lock, in case the device is being shut down already - std::unique_ptr localAudioLock(new Lock(_audio->_localAudioMutex, std::try_to_lock)); - if (localAudioLock->owns_lock()) { - _audio->prepareLocalAudioInjectors(std::move(localAudioLock)); - samplesAvailable = _audio->_localSamplesAvailable.load(std::memory_order_acquire); - } - } - - samplesRequested = std::min(samplesRequested, samplesAvailable); - if ((injectorSamplesPopped = _localInjectorsStream.appendSamples(mixBuffer, samplesRequested, append)) > 0) { - _audio->_localSamplesAvailable.fetch_sub(injectorSamplesPopped, std::memory_order_release); - qCDebug(audiostream, "Read %d samples from injectors (%d available, %d requested)", injectorSamplesPopped, - _localInjectorsStream.samplesAvailable(), samplesRequested); - } + int AudioClient::calculateNumberOfFrameSamples(int numBytes) const { + int frameSamples = (int)(numBytes * CALLBACK_ACCELERATOR_RATIO + 0.5f) / AudioConstants::SAMPLE_SIZE; + return frameSamples; } - // prepare injectors for the next callback - QtConcurrent::run(QThreadPool::globalInstance(), [this] { _audio->prepareLocalAudioInjectors(); }); + float AudioClient::azimuthForSource(const glm::vec3& relativePosition) { + glm::quat inverseOrientation = glm::inverse(_orientationGetter()); + + glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition; + + // project the rotated source position vector onto the XZ plane + rotatedSourcePosition.y = 0.0f; + + static const float SOURCE_DISTANCE_THRESHOLD = 1e-30f; + + float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition); + if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) { + // produce an oriented angle about the y-axis + glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2)); + float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" + return (direction.x < 0.0f) ? -angle : angle; - int samplesPopped = std::max(networkSamplesPopped, injectorSamplesPopped); - int framesPopped = samplesPopped / AudioConstants::STEREO; - int bytesWritten; - if (samplesPopped > 0) { - if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { - // limit the audio - _audio->_audioLimiter.render(mixBuffer, (int16_t*)data, framesPopped); } else { - _audio->_audioLimiter.render(mixBuffer, scratchBuffer, framesPopped); + // no azimuth if they are in same spot + return 0.0f; + } + } - // upmix or downmix to deviceChannelCount - if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) { - int extraChannels = deviceChannelCount - OUTPUT_CHANNEL_COUNT; - channelUpmix(scratchBuffer, (int16_t*)data, samplesPopped, extraChannels); - } else { - channelDownmix(scratchBuffer, (int16_t*)data, samplesPopped); + float AudioClient::gainForSource(float distance, float volume) { + // attenuation = -6dB * log2(distance) + // reference attenuation of 0dB at distance = 1.0m + float gain = volume / std::max(distance, HRTF_NEARFIELD_MIN); + + return gain; + } + + qint64 AudioClient::AudioOutputIODevice::readData(char* data, qint64 maxSize) { + // samples requested from OUTPUT_CHANNEL_COUNT + int deviceChannelCount = _audio->_outputFormat.channelCount(); + int samplesRequested = (int)(maxSize / AudioConstants::SAMPLE_SIZE) * OUTPUT_CHANNEL_COUNT / deviceChannelCount; + // restrict samplesRequested to the size of our mix/scratch buffers + samplesRequested = std::min(samplesRequested, _audio->_outputPeriod); + + int16_t* scratchBuffer = _audio->_outputScratchBuffer; + float* mixBuffer = _audio->_outputMixBuffer; + + int networkSamplesPopped; + if ((networkSamplesPopped = _receivedAudioStream.popSamples(samplesRequested, false)) > 0) { + qCDebug(audiostream, "Read %d samples from buffer (%d available, %d requested)", networkSamplesPopped, + _receivedAudioStream.getSamplesAvailable(), samplesRequested); + AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput(); + lastPopOutput.readSamples(scratchBuffer, networkSamplesPopped); + for (int i = 0; i < networkSamplesPopped; i++) { + mixBuffer[i] = convertToFloat(scratchBuffer[i]); + } + samplesRequested = networkSamplesPopped; + } + + int injectorSamplesPopped = 0; + { + bool append = networkSamplesPopped > 0; + // check the samples we have available locklessly; this is possible because only two functions add to the count: + // - prepareLocalAudioInjectors will only increase samples count + // - switchOutputToAudioDevice will zero samples count, + // stop the device - so that readData will exhaust the existing buffer or see a zeroed samples count, + // and start the device - which can then only see a zeroed samples count + int samplesAvailable = _audio->_localSamplesAvailable.load(std::memory_order_acquire); + + // if we do not have enough samples buffered despite having injectors, buffer them synchronously + if (samplesAvailable < samplesRequested && _audio->_localInjectorsAvailable.load(std::memory_order_acquire)) { + // try_to_lock, in case the device is being shut down already + std::unique_ptr localAudioLock(new Lock(_audio->_localAudioMutex, std::try_to_lock)); + if (localAudioLock->owns_lock()) { + _audio->prepareLocalAudioInjectors(std::move(localAudioLock)); + samplesAvailable = _audio->_localSamplesAvailable.load(std::memory_order_acquire); + } + } + + samplesRequested = std::min(samplesRequested, samplesAvailable); + if ((injectorSamplesPopped = _localInjectorsStream.appendSamples(mixBuffer, samplesRequested, append)) > 0) { + _audio->_localSamplesAvailable.fetch_sub(injectorSamplesPopped, std::memory_order_release); + qCDebug(audiostream, "Read %d samples from injectors (%d available, %d requested)", injectorSamplesPopped, + _localInjectorsStream.samplesAvailable(), samplesRequested); } } - bytesWritten = framesPopped * AudioConstants::SAMPLE_SIZE * deviceChannelCount; - } else { - // nothing on network, don't grab anything from injectors, and just return 0s - memset(data, 0, maxSize); - bytesWritten = maxSize; + // prepare injectors for the next callback + QtConcurrent::run(QThreadPool::globalInstance(), [this] { _audio->prepareLocalAudioInjectors(); }); + + int samplesPopped = std::max(networkSamplesPopped, injectorSamplesPopped); + int framesPopped = samplesPopped / AudioConstants::STEREO; + int bytesWritten; + if (samplesPopped > 0) { + if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) { + // limit the audio + _audio->_audioLimiter.render(mixBuffer, (int16_t*)data, framesPopped); + } else { + _audio->_audioLimiter.render(mixBuffer, scratchBuffer, framesPopped); + + // upmix or downmix to deviceChannelCount + if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) { + int extraChannels = deviceChannelCount - OUTPUT_CHANNEL_COUNT; + channelUpmix(scratchBuffer, (int16_t*)data, samplesPopped, extraChannels); + } else { + channelDownmix(scratchBuffer, (int16_t*)data, samplesPopped); + } + } + + bytesWritten = framesPopped * AudioConstants::SAMPLE_SIZE * deviceChannelCount; + } else { + // nothing on network, don't grab anything from injectors, and just return 0s + memset(data, 0, maxSize); + bytesWritten = maxSize; + } + + // send output buffer for recording + if (_audio->_isRecording) { + Lock lock(_recordMutex); + _audio->_audioFileWav.addRawAudioChunk(reinterpret_cast(scratchBuffer), bytesWritten); + } + + int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree(); + float msecsAudioOutputUnplayed = + bytesAudioOutputUnplayed / (float)_audio->_outputFormat.bytesForDuration(USECS_PER_MSEC); + _audio->_stats.updateOutputMsUnplayed(msecsAudioOutputUnplayed); + + if (bytesAudioOutputUnplayed == 0) { + _unfulfilledReads++; + } + + return bytesWritten; } - // send output buffer for recording - if (_audio->_isRecording) { - Lock lock(_recordMutex); - _audio->_audioFileWav.addRawAudioChunk(reinterpret_cast(scratchBuffer), bytesWritten); + bool AudioClient::startRecording(const QString& filepath) { + if (!_audioFileWav.create(_outputFormat, filepath)) { + qDebug() << "Error creating audio file: " + filepath; + return false; + } + _isRecording = true; + return true; } - int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree(); - float msecsAudioOutputUnplayed = bytesAudioOutputUnplayed / (float)_audio->_outputFormat.bytesForDuration(USECS_PER_MSEC); - _audio->_stats.updateOutputMsUnplayed(msecsAudioOutputUnplayed); - - if (bytesAudioOutputUnplayed == 0) { - _unfulfilledReads++; - } - - return bytesWritten; -} - -bool AudioClient::startRecording(const QString& filepath) { - if (!_audioFileWav.create(_outputFormat, filepath)) { - qDebug() << "Error creating audio file: " + filepath; - return false; - } - _isRecording = true; - return true; -} - -void AudioClient::stopRecording() { - if (_isRecording) { - _isRecording = false; - _audioFileWav.close(); - } -} - -void AudioClient::loadSettings() { - _receivedAudioStream.setDynamicJitterBufferEnabled(dynamicJitterBufferEnabled.get()); - _receivedAudioStream.setStaticJitterBufferFrames(staticJitterBufferFrames.get()); - - qCDebug(audioclient) << "---- Initializing Audio Client ----"; - auto codecPlugins = PluginManager::getInstance()->getCodecPlugins(); - for (auto& plugin : codecPlugins) { - qCDebug(audioclient) << "Codec available:" << plugin->getName(); - } -} - -void AudioClient::saveSettings() { - dynamicJitterBufferEnabled.set(_receivedAudioStream.dynamicJitterBufferEnabled()); - staticJitterBufferFrames.set(_receivedAudioStream.getStaticJitterBufferFrames()); -} - -void AudioClient::setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale) { - avatarBoundingBoxCorner = corner; - avatarBoundingBoxScale = scale; -} - -void AudioClient::startThread() { - moveToNewNamedThread(this, "Audio Thread", [this] { start(); }, QThread::TimeCriticalPriority); -} - -void AudioClient::setInputVolume(float volume, bool emitSignal) { - if (_audioInput && volume != (float)_audioInput->volume()) { - _audioInput->setVolume(volume); - if (emitSignal) { - emit inputVolumeChanged(_audioInput->volume()); + void AudioClient::stopRecording() { + if (_isRecording) { + _isRecording = false; + _audioFileWav.close(); + } + } + + void AudioClient::loadSettings() { + _receivedAudioStream.setDynamicJitterBufferEnabled(dynamicJitterBufferEnabled.get()); + _receivedAudioStream.setStaticJitterBufferFrames(staticJitterBufferFrames.get()); + + qCDebug(audioclient) << "---- Initializing Audio Client ----"; + auto codecPlugins = PluginManager::getInstance()->getCodecPlugins(); + for (auto& plugin : codecPlugins) { + qCDebug(audioclient) << "Codec available:" << plugin->getName(); + } + } + + void AudioClient::saveSettings() { + dynamicJitterBufferEnabled.set(_receivedAudioStream.dynamicJitterBufferEnabled()); + staticJitterBufferFrames.set(_receivedAudioStream.getStaticJitterBufferFrames()); + } + + void AudioClient::setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale) { + avatarBoundingBoxCorner = corner; + avatarBoundingBoxScale = scale; + } + + void AudioClient::startThread() { + moveToNewNamedThread(this, "Audio Thread", [this] { start(); }, QThread::TimeCriticalPriority); + } + + void AudioClient::setInputVolume(float volume, bool emitSignal) { + if (_audioInput && volume != (float)_audioInput->volume()) { + _audioInput->setVolume(volume); + if (emitSignal) { + emit inputVolumeChanged(_audioInput->volume()); + } } } -} diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 1ca7cac6ca..2e5ef65473 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -197,7 +197,11 @@ public slots: void checkInputTimeout(); void handleDummyAudioInput(); void handleRecordedAudioInput(const QByteArray& audio); - void handleTTSAudioInput(const QByteArray& audio); + void handleTTSAudioInput(const QByteArray& audio, + const int& newChunkSize, + const int& timerInterval); + void clearTTSBuffer(); + void processTTSBuffer(); void reset(); void audioMixerKilled(); @@ -289,11 +293,12 @@ private: bool mixLocalAudioInjectors(float* mixBuffer); float azimuthForSource(const glm::vec3& relativePosition); float gainForSource(float distance, float volume); - - void processAudioAndAddToRingBuffer(QByteArray& inputByteArray, - const uchar& channelCount, - const qint32& bytesForDuration, - QByteArray& rollingBuffer); + + Mutex _TTSMutex; + QTimer _TTSTimer; + bool _isProcessingTTS {false}; + QByteArray _TTSAudioBuffer; + int _TTSChunkSize = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * 50; #ifdef Q_OS_ANDROID QTimer _checkInputTimer; @@ -401,7 +406,7 @@ private: void configureReverb(); void updateReverbOptions(); - void handleLocalEchoAndReverb(QByteArray& inputByteArray); + void handleLocalEchoAndReverb(QByteArray& inputByteArray, const int& sampleRate, const int& channelCount); bool switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false); bool switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest = false); From 5f229280066eb3b6a081a14c08fc492039d7be17 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Wed, 17 Oct 2018 12:12:13 -0700 Subject: [PATCH 013/125] consolidating the tools under luci --- scripts/developer/utilities/render/luci.js | 259 +++++++-------------- 1 file changed, 90 insertions(+), 169 deletions(-) diff --git a/scripts/developer/utilities/render/luci.js b/scripts/developer/utilities/render/luci.js index 40fb6e01d7..cffeb615c9 100644 --- a/scripts/developer/utilities/render/luci.js +++ b/scripts/developer/utilities/render/luci.js @@ -11,210 +11,131 @@ // (function() { - var TABLET_BUTTON_NAME = "LUCI"; - var QMLAPP_URL = Script.resolvePath("./deferredLighting.qml"); - var ICON_URL = Script.resolvePath("../../../system/assets/images/luci-i.svg"); - var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/luci-a.svg"); - - - var onLuciScreen = false; - - function onClicked() { - if (onLuciScreen) { - tablet.gotoHomeScreen(); - } else { - tablet.loadQMLSource(QMLAPP_URL); - } - } - - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - var button = tablet.addButton({ - text: TABLET_BUTTON_NAME, - icon: ICON_URL, - activeIcon: ACTIVE_ICON_URL - }); - - var hasEventBridge = false; - - function wireEventBridge(on) { - if (!tablet) { - print("Warning in wireEventBridge(): 'tablet' undefined!"); - return; - } - if (on) { - if (!hasEventBridge) { - tablet.fromQml.connect(fromQml); - hasEventBridge = true; - } - } else { - if (hasEventBridge) { - tablet.fromQml.disconnect(fromQml); - hasEventBridge = false; - } - } - } - - function onScreenChanged(type, url) { - if (url === QMLAPP_URL) { - onLuciScreen = true; - } else { - onLuciScreen = false; - } - - button.editProperties({isActive: onLuciScreen}); - wireEventBridge(onLuciScreen); - } - - button.clicked.connect(onClicked); - tablet.screenChanged.connect(onScreenChanged); + var AppUi = Script.require('appUi'); var moveDebugCursor = false; - Controller.mousePressEvent.connect(function (e) { + var onMousePressEvent = function (e) { if (e.isMiddleButton) { moveDebugCursor = true; setDebugCursor(e.x, e.y); } - }); - Controller.mouseReleaseEvent.connect(function() { moveDebugCursor = false; }); - Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); }); + }; + Controller.mousePressEvent.connect(onMousePressEvent); + var onMouseReleaseEvent = function () { + moveDebugCursor = false; + }; + Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); + var onMouseMoveEvent = function (e) { + if (moveDebugCursor) { + setDebugCursor(e.x, e.y); + } + }; + Controller.mouseMoveEvent.connect(onMouseMoveEvent); function setDebugCursor(x, y) { - nx = 2.0 * (x / Window.innerWidth) - 1.0; - ny = 1.0 - 2.0 * ((y) / (Window.innerHeight)); + var nx = 2.0 * (x / Window.innerWidth) - 1.0; + var ny = 1.0 - 2.0 * ((y) / (Window.innerHeight)); - Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 }; + Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 }; } - - - var Page = function(title, qmlurl, width, height) { - + function Page(title, qmlurl, width, height) { this.title = title; - this.qml = Script.resolvePath(qmlurl); + this.qml = qmlurl; this.width = width; this.height = height; - this.window = null; + this.window; + + print("Page: New Page:" + JSON.stringify(this)); + } + + Page.prototype.killView = function () { + print("Page: Kill window for page:" + JSON.stringify(this)); + if (this.window) { + print("Page: Kill window for page:" + this.title); + //this.window.closed.disconnect(function () { + // this.killView(); + //}); + this.window.close(); + this.window = false; + } }; - Page.prototype.createView = function() { - if (this.window == null) { - var window = Desktop.createWindow(this.qml, { + Page.prototype.createView = function () { + var that = this; + if (!this.window) { + print("Page: New window for page:" + this.title); + this.window = Desktop.createWindow(Script.resolvePath(this.qml), { title: this.title, presentationMode: Desktop.PresentationMode.NATIVE, size: {x: this.width, y: this.height} }); - this.window = window - this.window.closed.connect(this.killView); + this.window.closed.connect(function () { + that.killView(); + }); } }; - Page.prototype.killView = function() { - if (this.window !== undefined) { - this.window.closed.disconnect(this.killView); - this.window.close() - this.window = undefined - } + + var Pages = function () { + this._pages = {}; }; - var pages = [] - pages.push_back(new Page('Render Engine', 'engineInspector.qml', 300, 400)) + Pages.prototype.addPage = function (command, title, qmlurl, width, height) { + this._pages[command] = new Page(title, qmlurl, width, height); + }; - + Pages.prototype.open = function (command) { + print("Pages: command = " + command); + if (!this._pages[command]) { + print("Pages: unknown command = " + command); + return; + } + this._pages[command].createView(); + }; - var engineInspectorView = null - function openEngineInspectorView() { - - /* if (engineInspectorView == null) { - var qml = Script.resolvePath('engineInspector.qml'); - var window = Desktop.createWindow(qml, { - title: 'Render Engine', - presentationMode: Desktop.PresentationMode.NATIVE, - size: {x: 300, y: 400} - }); - engineInspectorView = window - window.closed.connect(killEngineInspectorView); + Pages.prototype.clear = function () { + for (var p in this._pages) { + print("Pages: kill page: " + p); + this._pages[p].killView(); + delete this._pages[p]; } - } + this._pages = {}; + }; + var pages = new Pages(); - function killEngineInspectorView() { - if (engineInspectorView !== undefined) { - engineInspectorView.closed.disconnect(killEngineInspectorView); - engineInspectorView.close() - engineInspectorView = undefined - } - } -*/ - var cullInspectorView = null - function openCullInspectorView() { - if (cullInspectorView == null) { - var qml = Script.resolvePath('culling.qml'); - var window = Desktop.createWindow(qml, { - title: 'Cull Inspector', - presentationMode: Desktop.PresentationMode.NATIVE, - size: {x: 400, y: 600} - }); - cullInspectorView = window - window.closed.connect(killCullInspectorView); - } - } - - function killCullInspectorView() { - if (cullInspectorView !== undefined) { - cullInspectorView.closed.disconnect(killCullInspectorView); - cullInspectorView.close() - cullInspectorView = undefined - } - } - - var engineLODView = null - function openEngineLODView() { - if (engineLODView == null) { - engineLODView = Desktop.createWindow(Script.resolvePath('lod.qml'), { - title: 'Render LOD', - flags: Desktop.ALWAYS_ON_TOP, - presentationMode: Desktop.PresentationMode.NATIVE, - size: {x: 300, y: 500}, - }); - engineLODView.closed.connect(killEngineLODView); - } - } - function killEngineLODView() { - if (engineLODView !== undefined) { - engineLODView.closed.disconnect(killEngineLODView); - engineLODView.close() - engineLODView = undefined - } - } - - + pages.addPage('openEngineView', 'Render Engine', 'engineInspector.qml', 300, 400); + pages.addPage('openEngineLODView', 'Render LOD', 'lod.qml', 300, 400); + pages.addPage('openCullInspectorView', 'Cull Inspector', 'culling.qml', 300, 400); function fromQml(message) { - switch (message.method) { - case "openEngineView": - openEngineInspectorView(); - break; - case "openCullInspectorView": - openCullInspectorView(); - break; - case "openEngineLODView": - openEngineLODView(); - break; - } + if (pages.open(message.method)) { + return; + } } + var ui; + function startup() { + ui = new AppUi({ + buttonName: "LUCI", + home: Script.resolvePath("deferredLighting.qml"), + additionalAppScreens: Script.resolvePath("engineInspector.qml"), + onMessage: fromQml, + normalButton: Script.resolvePath("../../../system/assets/images/luci-i.svg"), + activeButton: Script.resolvePath("../../../system/assets/images/luci-a.svg") + }); + } + startup(); Script.scriptEnding.connect(function () { - if (onLuciScreen) { - tablet.gotoHomeScreen(); - } - button.clicked.disconnect(onClicked); - tablet.screenChanged.disconnect(onScreenChanged); - tablet.removeButton(button); - - killEngineInspectorView(); - killCullInspectorView(); - killEngineLODWindow(); + Controller.mousePressEvent.disconnect(onMousePressEvent); + Controller.mouseReleaseEvent.disconnect(onMouseReleaseEvent); + Controller.mouseMoveEvent.disconnect(onMouseMoveEvent); + pages.clear(); + // killEngineInspectorView(); + // killCullInspectorView(); + // killEngineLODWindow(); }); -}()); \ No newline at end of file +}()); From 0ab8a0ba5ff6831e5949758d36a7248d367e775c Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Thu, 18 Oct 2018 01:23:10 +0300 Subject: [PATCH 014/125] FB19396 - QML warning qrc:///qml/hifi/tablet/WindowRoot.qml:132: TypeError: Cannot read property of null --- .../resources/qml/hifi/tablet/WindowRoot.qml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml index 9c027308b8..d55ec363f0 100644 --- a/interface/resources/qml/hifi/tablet/WindowRoot.qml +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -129,8 +129,21 @@ Windows.ScrollingWindow { height: pane.scrollHeight width: pane.contentWidth - anchors.left: parent.left - anchors.top: parent.top + + // this might be looking not clear from the first look + // but loader.parent is not tabletRoot and it can be null! + // unfortunately we can't use conditional bindings here due to https://bugreports.qt.io/browse/QTBUG-22005 + + onParentChanged: { + if (parent) { + anchors.left = Qt.binding(function() { return parent.left }) + anchors.top = Qt.binding(function() { return parent.top }) + } else { + anchors.left = undefined + anchors.top = undefined + } + } + signal loaded; onWidthChanged: { From 12d092609b39ad125c57f7f038501ee1d4c77215 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 18 Oct 2018 17:20:26 -0700 Subject: [PATCH 015/125] Do not show login dialog if requested not to on the command line. --- interface/src/Application.cpp | 40 ++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1bd6af2eba..ba51ff9cec 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2330,23 +2330,29 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground); AndroidHelper::instance().notifyLoadComplete(); #else - static int CHECK_LOGIN_TIMER = 3000; - QTimer* checkLoginTimer = new QTimer(this); - checkLoginTimer->setInterval(CHECK_LOGIN_TIMER); - checkLoginTimer->setSingleShot(true); - connect(checkLoginTimer, &QTimer::timeout, this, []() { - auto accountManager = DependencyManager::get(); - auto dialogsManager = DependencyManager::get(); - if (!accountManager->isLoggedIn()) { - Setting::Handle{"loginDialogPoppedUp", false}.set(true); - dialogsManager->showLoginDialog(); - QJsonObject loginData = {}; - loginData["action"] = "login dialog shown"; - UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); - } - }); - Setting::Handle{"loginDialogPoppedUp", false}.set(false); - checkLoginTimer->start(); + // Do not show login dialog if requested not to on the command line + const QString HIFI_NO_LOGIN_COMMAND_LINE_KEY = "--no-login"; + int index = arguments().indexOf(HIFI_NO_LOGIN_COMMAND_LINE_KEY); + if (index == -1) { + // request not found + static int CHECK_LOGIN_TIMER = 3000; + QTimer* checkLoginTimer = new QTimer(this); + checkLoginTimer->setInterval(CHECK_LOGIN_TIMER); + checkLoginTimer->setSingleShot(true); + connect(checkLoginTimer, &QTimer::timeout, this, []() { + auto accountManager = DependencyManager::get(); + auto dialogsManager = DependencyManager::get(); + if (!accountManager->isLoggedIn()) { + Setting::Handle{ "loginDialogPoppedUp", false }.set(true); + dialogsManager->showLoginDialog(); + QJsonObject loginData = {}; + loginData["action"] = "login dialog shown"; + UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); + } + }); + Setting::Handle{ "loginDialogPoppedUp", false }.set(false); + checkLoginTimer->start(); + } #endif } From 092f88e5586bf79b0ef8afb511dd0d6793c87ea7 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Mon, 22 Oct 2018 10:55:50 -0700 Subject: [PATCH 016/125] Change parameter name from `--no-login` to `--no-login-suggestion` --- interface/src/Application.cpp | 2 +- tools/auto-tester/src/TestRunner.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ba51ff9cec..f07983fddf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2331,7 +2331,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo AndroidHelper::instance().notifyLoadComplete(); #else // Do not show login dialog if requested not to on the command line - const QString HIFI_NO_LOGIN_COMMAND_LINE_KEY = "--no-login"; + const QString HIFI_NO_LOGIN_COMMAND_LINE_KEY = "--no-login-suggestion"; int index = arguments().indexOf(HIFI_NO_LOGIN_COMMAND_LINE_KEY); if (index == -1) { // request not found diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp index 674cf6f8e8..01ec04f254 100644 --- a/tools/auto-tester/src/TestRunner.cpp +++ b/tools/auto-tester/src/TestRunner.cpp @@ -340,7 +340,7 @@ void TestRunner::runInterfaceWithTestScript() { QString testScript = QString("https://raw.githubusercontent.com/") + _user + "/hifi_tests/" + _branch + "/tests/testRecursive.js"; - QString commandLine = exeFile + " --url " + url + " --no-updater --no-login" + " --testScript " + testScript + + QString commandLine = exeFile + " --url " + url + " --no-updater --no-login-suggestion" + " --testScript " + testScript + " quitWhenFinished --testResultsLocation " + snapshotFolder; interfaceWorker->setCommandLine(commandLine); From 1d8994993c0e640ecaeadd8557fe09ea9a6f5fe2 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 22 Oct 2018 17:05:31 -0700 Subject: [PATCH 017/125] Whitelist TTS scripting interface to TTS app, which is now a default script --- interface/resources/qml/hifi/tts/TTS.qml | 304 ++++++++++++++++++ interface/src/Application.cpp | 12 +- .../src/scripting/TTSScriptingInterface.cpp | 4 +- scripts/defaultScripts.js | 3 +- scripts/system/tts/TTS.js | 28 ++ scripts/system/tts/tts-a.svg | 9 + scripts/system/tts/tts-i.svg | 9 + 7 files changed, 363 insertions(+), 6 deletions(-) create mode 100644 interface/resources/qml/hifi/tts/TTS.qml create mode 100644 scripts/system/tts/TTS.js create mode 100644 scripts/system/tts/tts-a.svg create mode 100644 scripts/system/tts/tts-i.svg diff --git a/interface/resources/qml/hifi/tts/TTS.qml b/interface/resources/qml/hifi/tts/TTS.qml new file mode 100644 index 0000000000..114efd0cca --- /dev/null +++ b/interface/resources/qml/hifi/tts/TTS.qml @@ -0,0 +1,304 @@ +// +// TTS.qml +// +// TTS App +// +// Created by Zach Fox on 2018-10-10 +// Copyright 2018 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 +// + +import Hifi 1.0 as Hifi +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import "qrc:////qml//styles-uit" as HifiStylesUit +import "qrc:////qml//controls-uit" as HifiControlsUit +import "qrc:////qml//controls" as HifiControls + +Rectangle { + HifiStylesUit.HifiConstants { id: hifi; } + + id: root; + // Style + color: hifi.colors.darkGray; + property bool keyboardRaised: false; + + // + // TITLE BAR START + // + Item { + id: titleBarContainer; + // Size + width: root.width; + height: 50; + // Anchors + anchors.left: parent.left; + anchors.top: parent.top; + + // Title bar text + HifiStylesUit.RalewaySemiBold { + id: titleBarText; + text: "Text-to-Speech"; + // Text size + size: hifi.fontSizes.overlayTitle; + // Anchors + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.leftMargin: 16; + width: paintedWidth; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignHLeft; + verticalAlignment: Text.AlignVCenter; + } + + // Separator + HifiControlsUit.Separator { + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + } + } + // + // TITLE BAR END + // + + + Item { + id: tagButtonContainer; + anchors.top: titleBarContainer.bottom; + anchors.topMargin: 2; + anchors.left: parent.left; + anchors.right: parent.right; + height: 70; + + HifiStylesUit.RalewaySemiBold { + id: tagButtonTitle; + text: "Insert Tag:"; + // Text size + size: 18; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + height: 35; + // Style + color: hifi.colors.lightGrayText; + // Alignment + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + + HifiControlsUit.Button { + id: pitch10Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: parent.left; + anchors.leftMargin: 3; + width: parent.width/6 - 6; + height: 30; + text: "Pitch 10"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + + HifiControlsUit.Button { + id: pitch0Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: pitch10Button.right; + anchors.leftMargin: 6; + width: parent.width/6 - anchors.leftMargin; + height: 30; + text: "Pitch 0"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + + HifiControlsUit.Button { + id: pitchNeg10Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: pitch0Button.right; + anchors.leftMargin: 6; + width: parent.width/6 - anchors.leftMargin; + height: 30; + text: "Pitch -10"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + + HifiControlsUit.Button { + id: speed5Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: pitchNeg10Button.right; + anchors.leftMargin: 6; + width: parent.width/6 - anchors.leftMargin; + height: 30; + text: "Speed 5"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + + HifiControlsUit.Button { + id: speed0Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: speed5Button.right; + anchors.leftMargin: 6; + width: parent.width/6 - anchors.leftMargin; + height: 30; + text: "Speed 0"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + + HifiControlsUit.Button { + id: speedNeg10Button; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.top: tagButtonTitle.bottom; + anchors.left: speed0Button.right; + anchors.leftMargin: 6; + width: parent.width/6 - anchors.leftMargin; + height: 30; + text: "Speed -10"; + onClicked: { + messageToSpeak.insert(messageToSpeak.cursorPosition, ""); + } + } + } + + Item { + anchors.top: tagButtonContainer.bottom; + anchors.topMargin: 8; + anchors.bottom: keyboardContainer.top; + anchors.bottomMargin: 16; + anchors.left: parent.left; + anchors.leftMargin: 16; + anchors.right: parent.right; + anchors.rightMargin: 16; + + TextArea { + id: messageToSpeak; + placeholderText: "Message to Speak"; + font.family: "Fira Sans SemiBold"; + font.pixelSize: 20; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: speakButton.top; + anchors.bottomMargin: 8; + // Style + background: Rectangle { + anchors.fill: parent; + color: parent.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow; + border.width: parent.activeFocus ? 1 : 0; + border.color: parent.activeFocus ? hifi.colors.primaryHighlight : hifi.colors.textFieldLightBackground; + } + color: hifi.colors.white; + textFormat: TextEdit.PlainText; + wrapMode: TextEdit.Wrap; + activeFocusOnPress: true; + activeFocusOnTab: true; + Keys.onPressed: { + if (event.key == Qt.Key_Return || event.key == Qt.Key_Enter) { + TextToSpeech.speakText(messageToSpeak.text, 480, 10, 24000, 16, true); + event.accepted = true; + } + } + } + + HifiControlsUit.Button { + id: speakButton; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + width: 215; + height: 40; + text: "Speak"; + onClicked: { + TextToSpeech.speakText(messageToSpeak.text, 480, 10, 24000, 16, true); + } + } + + HifiControlsUit.Button { + id: clearButton; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.white; + colorScheme: hifi.colorSchemes.dark; + anchors.right: speakButton.left; + anchors.rightMargin: 16; + anchors.bottom: parent.bottom; + width: 100; + height: 40; + text: "Clear"; + onClicked: { + messageToSpeak.text = ""; + } + } + + HifiControlsUit.Button { + id: stopButton; + focusPolicy: Qt.NoFocus; + color: hifi.buttons.red; + colorScheme: hifi.colorSchemes.dark; + anchors.right: clearButton.left; + anchors.rightMargin: 16; + anchors.bottom: parent.bottom; + width: 100; + height: 40; + text: "Stop Last"; + onClicked: { + TextToSpeech.stopLastSpeech(); + } + } + } + + Item { + id: keyboardContainer; + z: 998; + visible: keyboard.raised; + property bool punctuationMode: false; + anchors { + bottom: parent.bottom; + left: parent.left; + right: parent.right; + } + + HifiControlsUit.Keyboard { + id: keyboard; + raised: HMD.mounted && root.keyboardRaised; + numeric: parent.punctuationMode; + anchors { + bottom: parent.bottom; + left: parent.left; + right: parent.right; + } + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 836e12e60a..9cfdc8a9bb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2906,7 +2906,7 @@ void Application::initializeUi() { LoginDialog::registerType(); Tooltip::registerType(); UpdateDialog::registerType(); - QmlContextCallback callback = [](QQmlContext* context) { + QmlContextCallback commerceCallback = [](QQmlContext* context) { context->setContextProperty("Commerce", new QmlCommerce()); }; OffscreenQmlSurface::addWhitelistContextHandler({ @@ -2932,7 +2932,13 @@ void Application::initializeUi() { QUrl{ "hifi/commerce/wallet/Wallet.qml" }, QUrl{ "hifi/commerce/wallet/WalletHome.qml" }, QUrl{ "hifi/commerce/wallet/WalletSetup.qml" }, - }, callback); + }, commerceCallback); + QmlContextCallback ttsCallback = [](QQmlContext* context) { + context->setContextProperty("TextToSpeech", DependencyManager::get().data()); + }; + OffscreenQmlSurface::addWhitelistContextHandler({ + QUrl{ "hifi/tts/TTS.qml" } + }, ttsCallback); qmlRegisterType("Hifi", 1, 0, "ResourceImageItem"); qmlRegisterType("Hifi", 1, 0, "Preference"); qmlRegisterType("HifiWeb", 1, 0, "WebBrowserSuggestionsEngine"); @@ -3135,7 +3141,6 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get().data()); surfaceContext->setContextProperty("Wallet", DependencyManager::get().data()); surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); - surfaceContext->setContextProperty("TextToSpeech", DependencyManager::get().data()); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); @@ -6818,7 +6823,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("Wallet", DependencyManager::get().data()); scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get().data()); scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); - scriptEngine->registerGlobalObject("TextToSpeech", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue); diff --git a/interface/src/scripting/TTSScriptingInterface.cpp b/interface/src/scripting/TTSScriptingInterface.cpp index 5fb47a73c3..0cdb24e15d 100644 --- a/interface/src/scripting/TTSScriptingInterface.cpp +++ b/interface/src/scripting/TTSScriptingInterface.cpp @@ -151,7 +151,9 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak, _lastSoundByteArray.resize(0); _lastSoundByteArray.append(buf1, dwSize); - emit ttsSampleCreated(_lastSoundByteArray, newChunkSize, timerInterval); + // Commented out because this doesn't work completely :) + // Obviously, commenting this out isn't fit for production, but it's fine for a test PR + //emit ttsSampleCreated(_lastSoundByteArray, newChunkSize, timerInterval); if (alsoInject) { AudioInjectorOptions options; diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 9efb040624..2398973dfd 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -32,7 +32,8 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/firstPersonHMD.js", "system/tablet-ui/tabletUI.js", "system/emote.js", - "system/miniTablet.js" + "system/miniTablet.js", + "system/tts/TTS.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", diff --git a/scripts/system/tts/TTS.js b/scripts/system/tts/TTS.js new file mode 100644 index 0000000000..36259cfda0 --- /dev/null +++ b/scripts/system/tts/TTS.js @@ -0,0 +1,28 @@ +"use strict"; +/*jslint vars:true, plusplus:true, forin:true*/ +/*global Tablet, Script, */ +/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ +// +// TTS.js +// +// Created by Zach Fox on 2018-10-10 +// Copyright 2018 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 +// + +(function () { // BEGIN LOCAL_SCOPE +var AppUi = Script.require('appUi'); + +var ui; +function startup() { + ui = new AppUi({ + buttonName: "TTS", + //home: Script.resolvePath("TTS.qml") + home: "hifi/tts/TTS.qml", + graphicsDirectory: Script.resolvePath("./") // speech by Danil Polshin from the Noun Project + }); +} +startup(); +}()); // END LOCAL_SCOPE diff --git a/scripts/system/tts/tts-a.svg b/scripts/system/tts/tts-a.svg new file mode 100644 index 0000000000..9dac3a2d53 --- /dev/null +++ b/scripts/system/tts/tts-a.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/scripts/system/tts/tts-i.svg b/scripts/system/tts/tts-i.svg new file mode 100644 index 0000000000..1c52ec3193 --- /dev/null +++ b/scripts/system/tts/tts-i.svg @@ -0,0 +1,9 @@ + + + + + + + + + From 794767144fe0a8837c361502d25b12cee5f53a3d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 23 Oct 2018 15:30:33 +1300 Subject: [PATCH 018/125] Fix Interface crash importing JSON with duplicate entity IDs in file --- libraries/entities/src/EntityTree.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 0b3b8abba2..1c201e7b46 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2752,9 +2752,11 @@ bool EntityTree::readFromMap(QVariantMap& map) { success = false; } - const QUuid& cloneOriginID = entity->getCloneOriginID(); - if (!cloneOriginID.isNull()) { - cloneIDs[cloneOriginID].push_back(entity->getEntityItemID()); + if (entity) { + const QUuid& cloneOriginID = entity->getCloneOriginID(); + if (!cloneOriginID.isNull()) { + cloneIDs[cloneOriginID].push_back(entity->getEntityItemID()); + } } } From 60447bc52176993e408dc189755632e19dc4c21a Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 22 Oct 2018 22:27:16 -0700 Subject: [PATCH 019/125] Adding the missing MetaType on the overlay... --- interface/src/ui/overlays/ModelOverlay.cpp | 1 + interface/src/ui/overlays/Overlay.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 5040842b3b..80fbe0fb95 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -803,5 +803,6 @@ render::ItemKey ModelOverlay::getKey() { if (!_isLODEnabled) { builder.withLODDisabled(); } + return builder.build(); } \ No newline at end of file diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 22cf924727..1bf94adfa0 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -247,7 +247,7 @@ void Overlay::removeMaterial(graphics::MaterialPointer material, const std::stri } render::ItemKey Overlay::getKey() { - auto builder = render::ItemKey::Builder().withTypeShape(); + auto builder = render::ItemKey::Builder().withTypeShape().withTypeMeta(); builder.withViewSpace(); builder.withLayer(render::hifi::LAYER_2D); From 702f6229b87869b118ed2ff8308abebfbc1420a0 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 23 Oct 2018 08:50:10 -0700 Subject: [PATCH 020/125] Removing the pseudo fix for lod culling that is not ready in this pr --- interface/src/ui/overlays/ModelOverlay.cpp | 43 +--------------------- interface/src/ui/overlays/ModelOverlay.h | 3 -- libraries/render-utils/src/Model.cpp | 31 ---------------- libraries/render-utils/src/Model.h | 3 -- libraries/render/src/render/CullTask.cpp | 4 +- libraries/render/src/render/Item.h | 9 ----- 6 files changed, 4 insertions(+), 89 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 80fbe0fb95..eee8222051 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -127,11 +127,6 @@ void ModelOverlay::update(float deltatime) { _model->setGroupCulled(_isGroupCulled, scene); metaDirty = true; } - if (_lodEnabledDirty) { - _lodEnabledDirty = false; - _model->setLODEnabled(_isLODEnabled, scene); - metaDirty = true; - } if (metaDirty) { transaction.updateItem(getRenderItemID(), [](Overlay& data) {}); } @@ -196,14 +191,6 @@ void ModelOverlay::setGroupCulled(bool groupCulled) { } } -void ModelOverlay::setLODEnabled(bool lodEnabled) { - if (lodEnabled != _isLODEnabled) { - _isLODEnabled = lodEnabled; - _lodEnabledDirty = true; - } -} - - void ModelOverlay::setProperties(const QVariantMap& properties) { auto origPosition = getWorldPosition(); auto origRotation = getWorldOrientation(); @@ -261,12 +248,6 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { setGroupCulled(groupCulledValue.toBool()); } - auto lodEnabledValue = properties["isLODEnabled"]; - if (lodEnabledValue.isValid() && lodEnabledValue.canConvert(QVariant::Bool)) { - setLODEnabled(lodEnabledValue.toBool()); - } - - // jointNames is read-only. // jointPositions is read-only. // jointOrientations is read-only. @@ -571,24 +552,8 @@ void ModelOverlay::locationChanged(bool tellPhysics) { // FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform() if (_model && _model->isActive()) { - if (!_isLODEnabled) { - auto rot = _model->getRotation(); - auto tra = _model->getTranslation(); - - auto nrot = getWorldOrientation(); - auto ntra = getWorldPosition(); - if (glm::any(glm::notEqual(rot, nrot))) { - rot = nrot; - _model->setRotation(rot); - } - if (glm::any(glm::notEqual(tra, ntra))) { - tra = ntra; - _model->setTranslation(tra); - } - } else { - _model->setRotation(getWorldOrientation()); - _model->setTranslation(getWorldPosition()); - } + _model->setRotation(getWorldOrientation()); + _model->setTranslation(getWorldPosition()); } } @@ -800,9 +765,5 @@ render::ItemKey ModelOverlay::getKey() { if (_isGroupCulled) { builder.withMetaCullGroup(); } - if (!_isLODEnabled) { - builder.withLODDisabled(); - } - return builder.build(); } \ No newline at end of file diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index dc127afe48..bd922e258a 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -69,7 +69,6 @@ public: void setDrawInFront(bool drawInFront) override; void setDrawHUDLayer(bool drawHUDLayer) override; void setGroupCulled(bool groupCulled); - void setLODEnabled(bool lodEnabled); void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override; void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override; @@ -131,8 +130,6 @@ private: bool _drawInHUDDirty { false }; bool _isGroupCulled { false }; bool _groupCulledDirty { false }; - bool _isLODEnabled{ true }; - bool _lodEnabledDirty{ true }; void processMaterials(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index da50fd6ff0..53009e8bfa 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -226,10 +226,6 @@ void Model::updateRenderItems() { bool isWireframe = self->isWireframe(); auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags(); - if (renderItemKeyGlobalFlags.isLODDisabled()) { - modelTransform.setScale(glm::vec3(1.0f)); - } - render::Transaction transaction; for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) { @@ -249,8 +245,6 @@ void Model::updateRenderItems() { data.updateClusterBuffer(meshState.clusterMatrices); } - auto bound = data.getBound(); - Transform renderTransform = modelTransform; if (useDualQuaternionSkinning) { @@ -268,15 +262,6 @@ void Model::updateRenderItems() { } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); - if (renderItemKeyGlobalFlags.isLODDisabled()) { - auto newBound = data.getBound(); - if (bound != newBound) { - data.updateTransformForSkinnedMesh(renderTransform, modelTransform); - } else { - data.updateTransformForSkinnedMesh(renderTransform, modelTransform); - } - } - data.updateKey(renderItemKeyGlobalFlags); data.setShapeKey(invalidatePayloadShapeKey, isWireframe, useDualQuaternionSkinning); }); @@ -906,12 +891,7 @@ void Model::updateRenderItemsKey(const render::ScenePointer& scene) { _needsFixupInScene = true; return; } - auto prevVal = _needsFixupInScene; auto renderItemsKey = _renderItemKeyGlobalFlags; - if (renderItemsKey.isLODDisabled()) { - _needsFixupInScene = true; - _needsFixupInScene = prevVal; - } render::Transaction transaction; foreach(auto item, _modelMeshRenderItemsMap.keys()) { transaction.updateItem(item, [renderItemsKey](ModelMeshPartPayload& data) { @@ -991,17 +971,6 @@ bool Model::isGroupCulled() const { return _renderItemKeyGlobalFlags.isSubMetaCulled(); } -void Model::setLODEnabled(bool isLODEnabled, const render::ScenePointer& scene) { - if (Model::isLODEnabled() != isLODEnabled) { - auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags); - _renderItemKeyGlobalFlags = (isLODEnabled ? keyBuilder.withLODEnabled() : keyBuilder.withLODDisabled()); - updateRenderItemsKey(scene); - } -} -bool Model::isLODEnabled() const { - return _renderItemKeyGlobalFlags.isLODEnabled(); -} - const render::ItemKey Model::getRenderItemKeyGlobalFlags() const { return _renderItemKeyGlobalFlags; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 6988e870a5..71809821eb 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -120,9 +120,6 @@ public: bool isGroupCulled() const; void setGroupCulled(bool isGroupCulled, const render::ScenePointer& scene = nullptr); - bool isLODEnabled() const; - void setLODEnabled(bool isLODEnabled, const render::ScenePointer& scene = nullptr); - bool canCastShadow() const; void setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene = nullptr); diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index ad9f6b7076..5e5c6b4c6e 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -294,7 +294,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, auto& item = scene->getItem(id); if (filter.test(item.getKey())) { ItemBound itemBound(id, item.getBound()); - if (item.getKey().isLODDisabled() || test.solidAngleTest(itemBound.bound)) { + if (test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { item.fetchMetaSubItemBounds(outItems, (*scene)); @@ -329,7 +329,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, if (filter.test(item.getKey())) { ItemBound itemBound(id, item.getBound()); if (test.frustumTest(itemBound.bound)) { - if (item.getKey().isLODDisabled() || test.solidAngleTest(itemBound.bound)) { + if (test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); if (item.getKey().isMetaCullGroup()) { item.fetchMetaSubItemBounds(outItems, (*scene)); diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 9d2c0928e0..a87e2031f9 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -101,7 +101,6 @@ public: SHADOW_CASTER, // Item cast shadows META_CULL_GROUP, // As a meta item, the culling of my sub items is based solely on my bounding box and my visibility in the view SUB_META_CULLED, // As a sub item of a meta render item set as cull group, need to be set to my culling to the meta render it - LOD_DISABLED, // Item LOD behavior is disabled, it won't be LODed because of lod behavior in the view FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against LAST_TAG_BIT = FIRST_TAG_BIT + NUM_TAGS, @@ -161,8 +160,6 @@ public: Builder& withoutMetaCullGroup() { _flags.reset(META_CULL_GROUP); return (*this); } Builder& withSubMetaCulled() { _flags.set(SUB_META_CULLED); return (*this); } Builder& withoutSubMetaCulled() { _flags.reset(SUB_META_CULLED); return (*this); } - Builder& withLODDisabled() { _flags.set(LOD_DISABLED); return (*this); } - Builder& withLODEnabled() { _flags.reset(LOD_DISABLED); return (*this); } Builder& withTag(Tag tag) { _flags.set(FIRST_TAG_BIT + tag); return (*this); } // Set ALL the tags in one call using the Tag bits @@ -206,9 +203,6 @@ public: bool isSubMetaCulled() const { return _flags[SUB_META_CULLED]; } void setSubMetaCulled(bool metaCulled) { (metaCulled ? _flags.set(SUB_META_CULLED) : _flags.reset(SUB_META_CULLED)); } - bool isLODEnabled() const { return !_flags[LOD_DISABLED]; } - bool isLODDisabled() const { return _flags[LOD_DISABLED]; } - bool isTag(Tag tag) const { return _flags[FIRST_TAG_BIT + tag]; } uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); } @@ -280,9 +274,6 @@ public: Builder& withoutSubMetaCulled() { _value.reset(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); } Builder& withSubMetaCulled() { _value.set(ItemKey::SUB_META_CULLED); _mask.set(ItemKey::SUB_META_CULLED); return (*this); } - Builder& withLODEnabled() { _value.reset(ItemKey::LOD_DISABLED); _mask.set(ItemKey::LOD_DISABLED); return (*this); } - Builder& withLODDisabled() { _value.set(ItemKey::LOD_DISABLED); _mask.set(ItemKey::LOD_DISABLED); return (*this); } - Builder& withoutTag(ItemKey::Tag tagIndex) { _value.reset(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); } Builder& withTag(ItemKey::Tag tagIndex) { _value.set(ItemKey::FIRST_TAG_BIT + tagIndex); _mask.set(ItemKey::FIRST_TAG_BIT + tagIndex); return (*this); } // Set ALL the tags in one call using the Tag bits and the Tag bits touched From 25213febfde8c915ca44198eb33294e283b9a6ef Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 23 Oct 2018 10:05:10 -0700 Subject: [PATCH 021/125] REmoving the uneeded extra property --- scripts/system/libraries/WebTablet.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 2ed54d12c8..8e5b41deda 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -133,8 +133,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth }, parentID: MyAvatar.SELF_ID, visible: visible, - isGroupCulled: true, - isLODEnabled: false + isGroupCulled: true }; // compute position, rotation & parentJointIndex of the tablet From 947391e49eb28140d58047d3d2194ac3a6701348 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 23 Oct 2018 11:44:16 -0700 Subject: [PATCH 022/125] Fix build errors --- interface/src/scripting/TTSScriptingInterface.cpp | 8 ++++++++ interface/src/scripting/TTSScriptingInterface.h | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/interface/src/scripting/TTSScriptingInterface.cpp b/interface/src/scripting/TTSScriptingInterface.cpp index 0cdb24e15d..f51a638471 100644 --- a/interface/src/scripting/TTSScriptingInterface.cpp +++ b/interface/src/scripting/TTSScriptingInterface.cpp @@ -13,6 +13,7 @@ #include "avatar/AvatarManager.h" TTSScriptingInterface::TTSScriptingInterface() { +#ifdef WIN32 // // Create text to speech engine // @@ -36,11 +37,13 @@ TTSScriptingInterface::TTSScriptingInterface() { if (FAILED(hr)) { qDebug() << "Can't set default voice."; } +#endif } TTSScriptingInterface::~TTSScriptingInterface() { } +#ifdef WIN32 class ReleaseOnExit { public: ReleaseOnExit(IUnknown* p) : m_p(p) {} @@ -53,6 +56,7 @@ public: private: IUnknown* m_p; }; +#endif void TTSScriptingInterface::testTone(const bool& alsoInject) { QByteArray byteArray(480000, 0); @@ -81,6 +85,7 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak, const int& sampleRate, const int& bitsPerSample, const bool& alsoInject) { +#ifdef WIN32 WAVEFORMATEX fmt; fmt.wFormatTag = WAVE_FORMAT_PCM; fmt.nSamplesPerSec = sampleRate; @@ -161,6 +166,9 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak, _lastSoundAudioInjector = AudioInjector::playSound(_lastSoundByteArray, options); } +#else + qDebug() << "Text-to-Speech isn't currently supported on non-Windows platforms."; +#endif } void TTSScriptingInterface::stopLastSpeech() { diff --git a/interface/src/scripting/TTSScriptingInterface.h b/interface/src/scripting/TTSScriptingInterface.h index f6eca081ab..7e4f3afa9d 100644 --- a/interface/src/scripting/TTSScriptingInterface.h +++ b/interface/src/scripting/TTSScriptingInterface.h @@ -13,11 +13,14 @@ #include #include +#ifdef WIN32 +#pragma warning(disable : 4996) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include // SAPI #include // SAPI Helper +#endif #include #include @@ -42,6 +45,7 @@ signals: void clearTTSBuffer(); private: +#ifdef WIN32 class CComAutoInit { public: // Initializes COM using CoInitialize. @@ -82,6 +86,7 @@ private: // Default voice token CComPtr m_voiceToken; +#endif QByteArray _lastSoundByteArray; AudioInjectorPointer _lastSoundAudioInjector; From 95b86bf5b7592a174c3ec629fd0c4a26b78dc218 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 23 Oct 2018 13:08:39 -0700 Subject: [PATCH 023/125] initial limited commerce for oculus store prototype --- .../qml/hifi/commerce/purchases/Purchases.qml | 5 +- .../qml/hifi/commerce/wallet/Wallet.qml | 68 +++++++++++++------ scripts/system/commerce/wallet.js | 3 +- scripts/system/html/js/marketplacesInject.js | 1 + 4 files changed, 53 insertions(+), 24 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 015ec3a172..24b365ffc4 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -180,7 +180,8 @@ Rectangle { HifiCommerceCommon.EmulatedMarketplaceHeader { id: titleBarContainer; z: 997; - visible: !needsLogIn.visible; + visible: false; //HRS FIXME !needsLogIn.visible; + height: 100; // HRS FIXME // Size width: parent.width; // Anchors @@ -475,7 +476,7 @@ Rectangle { anchors.left: parent.left; anchors.leftMargin: 16; width: paintedWidth; - text: isShowingMyItems ? "My Items" : "My Purchases"; + text: "Inventory"; // HRS FIXME isShowingMyItems ? "My Items" : "My Purchases"; color: hifi.colors.black; size: 22; } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index cbb77883df..958906a839 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -20,6 +20,7 @@ import "../../../controls" as HifiControls import "../common" as HifiCommerceCommon import "../common/sendAsset" import "../.." as HifiCommon +import "../purchases" as HifiPurchases Rectangle { HifiConstants { id: hifi; } @@ -64,7 +65,7 @@ Rectangle { } } else if (walletStatus === 5) { if (root.activeView !== "walletSetup") { - root.activeView = "walletHome"; + root.activeView = "walletInventory"; // HRS FIXME "walletHome"; Commerce.getSecurityImage(); } } else { @@ -124,7 +125,7 @@ Rectangle { // Title Bar text RalewaySemiBold { id: titleBarText; - text: "WALLET"; + text: "ASSETS"; //"WALLET"; // Text size size: hifi.fontSizes.overlayTitle; // Anchors @@ -342,6 +343,17 @@ Rectangle { } } } + } + + HifiPurchases.Purchases { + id: walletInventory; + visible: root.activeView === "walletInventory"; + anchors.top: titleBarContainer.bottom; + anchors.bottom: tabButtonsContainer.top; + anchors.left: parent.left; + anchors.right: parent.right; + + } HifiCommon.RootHttpRequest { @@ -354,7 +366,6 @@ Rectangle { listModelName: "Send Money Connections"; z: 997; visible: root.activeView === "sendMoney"; - keyboardContainer: root; anchors.fill: parent; parentAppTitleBarHeight: titleBarContainer.height; parentAppNavBarHeight: tabButtonsContainer.height; @@ -449,13 +460,13 @@ Rectangle { visible: !walletSetup.visible; color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: parent.left; + anchors.left: exchangeMoneyButtonContainer.right; // HRS FIXME parent.left; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; HiFiGlyphs { id: homeTabIcon; - text: hifi.glyphs.home2; + text: hifi.glyphs.leftRightArrows; // HRS FIXME hifi.glyphs.home2; // Size size: 50; // Anchors @@ -463,11 +474,11 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: hifi.colors.lightGray50; // HRS FIXME root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; } RalewaySemiBold { - text: "WALLET HOME"; + text: "RECENT ACTIVITY"; //"WALLET HOME"; // Text size size: 16; // Anchors @@ -478,13 +489,13 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: hifi.colors.lightGray50; // HRS FIXME root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignTop; } - MouseArea { + /* HRS FIXME MouseArea { id: walletHomeTabMouseArea; anchors.fill: parent; hoverEnabled: enabled; @@ -494,22 +505,22 @@ Rectangle { } onEntered: parent.color = hifi.colors.blueHighlight; onExited: parent.color = root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black; - } + }*/ } // "EXCHANGE MONEY" tab button Rectangle { id: exchangeMoneyButtonContainer; visible: !walletSetup.visible; - color: hifi.colors.black; + color: root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black; // HRS FIXME hifi.colors.black; anchors.top: parent.top; - anchors.left: walletHomeButtonContainer.right; + anchors.left: parent.left; // FIXME walletHomeButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; HiFiGlyphs { id: exchangeMoneyTabIcon; - text: hifi.glyphs.leftRightArrows; + text: hifi.glyphs.home2; // HRS FIXME hifi.glyphs.leftRightArrows; // Size size: 50; // Anchors @@ -517,11 +528,11 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: hifi.colors.lightGray50; + color: root.activeView === "walletInventory" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; // HRS FIXMEhifi.colors.lightGray50; } RalewaySemiBold { - text: "EXCHANGE MONEY"; + text: "INVENTORY"; // HRS FIXME "EXCHANGE MONEY"; // Text size size: 16; // Anchors @@ -532,12 +543,24 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: hifi.colors.lightGray50; + color: root.activeView === "walletInventory" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; // HRS FIXME hifi.colors.lightGray50; wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignTop; } + + MouseArea { + id: inventoryTabMouseArea; + anchors.fill: parent; + hoverEnabled: enabled; + onClicked: { + root.activeView = "walletInventory"; + tabButtonsContainer.resetTabButtonColors(); + } + onEntered: parent.color = hifi.colors.blueHighlight; + onExited: parent.color = root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black; + } } @@ -547,7 +570,7 @@ Rectangle { visible: !walletSetup.visible; color: root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: exchangeMoneyButtonContainer.right; + anchors.left: walletHomeButtonContainer.right; // HRS FIXME exchangeMoneyButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; @@ -561,7 +584,7 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: hifi.colors.lightGray50; // HRS FIXME root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; } RalewaySemiBold { @@ -576,14 +599,14 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: hifi.colors.lightGray50; // HRS FIXME root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignTop; } - MouseArea { + /* HRS FIXME MouseArea { id: sendMoneyTabMouseArea; anchors.fill: parent; hoverEnabled: enabled; @@ -593,7 +616,7 @@ Rectangle { } onEntered: parent.color = hifi.colors.blueHighlight; onExited: parent.color = root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black; - } + } */ } // "SECURITY" tab button @@ -710,6 +733,7 @@ Rectangle { sendMoneyButtonContainer.color = hifi.colors.black; securityButtonContainer.color = hifi.colors.black; helpButtonContainer.color = hifi.colors.black; + exchangeMoneyButtonContainer.color = hifi.colors.black; // HRS FIXME if (root.activeView === "walletHome") { walletHomeButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "sendMoney") { @@ -718,6 +742,8 @@ Rectangle { securityButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "help") { helpButtonContainer.color = hifi.colors.blueAccent; + } else if (root.activeView == "walletInventory") { // HRS FIXME + exchangeMoneyButtonContainer.color = hifi.colors.blueAccent; } } } diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 5b91afea33..afdd49c0d1 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -559,12 +559,13 @@ function uninstallMarketplaceItemTester() { } } -var BUTTON_NAME = "WALLET"; +var BUTTON_NAME = "ASSETS"; //HRS FIXME "WALLET"; var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; var ui; function startup() { ui = new AppUi({ buttonName: BUTTON_NAME, + buttonPrefix: "wallet-", sortOrder: 10, home: WALLET_QML_SOURCE, onOpened: walletOpened, diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 24a96023da..0bc0fac13d 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -219,6 +219,7 @@ purchasesElement.style = "height:100%;margin-top:18px;font-weight:bold;float:right;margin-right:" + (dropDownElement.offsetWidth + 30) + "px;position:relative;z-index:999;"; navbarBrandElement.parentNode.insertAdjacentElement('beforeend', purchasesElement); + $('#purchasesButton').css('display', 'none'); // HRS FIXME $('#purchasesButton').on('click', function () { EventBridge.emitWebEvent(JSON.stringify({ type: "PURCHASES", From 83951328300acdb409d6f4c08e74f11f8b1ee880 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 23 Oct 2018 15:11:02 -0700 Subject: [PATCH 024/125] Attempt to resolve final warnings --- libraries/audio-client/src/AudioClient.cpp | 4 ++-- libraries/audio-client/src/AudioClient.h | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 606763e4ab..1ce6c15951 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -186,11 +186,11 @@ AudioClient::AudioClient() : _networkToOutputResampler(NULL), _localToOutputResampler(NULL), _audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), _outgoingAvatarAudioSequenceNumber(0), _audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this), _stats(&_receivedAudioStream), - _positionGetter(DEFAULT_POSITION_GETTER), _TTSTimer(this), + _positionGetter(DEFAULT_POSITION_GETTER), _orientationGetter(DEFAULT_ORIENTATION_GETTER), #if defined(Q_OS_ANDROID) _checkInputTimer(this), _isHeadsetPluggedIn(false), #endif - _orientationGetter(DEFAULT_ORIENTATION_GETTER) { + _TTSTimer(this) { // avoid putting a lock in the device callback assert(_localSamplesAvailable.is_lock_free()); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 2e5ef65473..9b50d3eccb 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -293,12 +293,6 @@ private: bool mixLocalAudioInjectors(float* mixBuffer); float azimuthForSource(const glm::vec3& relativePosition); float gainForSource(float distance, float volume); - - Mutex _TTSMutex; - QTimer _TTSTimer; - bool _isProcessingTTS {false}; - QByteArray _TTSAudioBuffer; - int _TTSChunkSize = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * 50; #ifdef Q_OS_ANDROID QTimer _checkInputTimer; @@ -464,6 +458,12 @@ private: QTimer* _checkPeakValuesTimer { nullptr }; bool _isRecording { false }; + + Mutex _TTSMutex; + bool _isProcessingTTS { false }; + QByteArray _TTSAudioBuffer; + int _TTSChunkSize = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * 50; + QTimer _TTSTimer; }; From 7707d92a959657052a26fa4a5bd7afb60477ceb7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 23 Oct 2018 16:07:35 -0700 Subject: [PATCH 025/125] connect wiring on purchases --- interface/resources/qml/hifi/commerce/wallet/Wallet.qml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 958906a839..c2a8258cca 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -352,7 +352,11 @@ Rectangle { anchors.bottom: tabButtonsContainer.top; anchors.left: parent.left; anchors.right: parent.right; - + Connections { + onSendToScript: { + sendToScript(message); + } + } } @@ -822,7 +826,8 @@ Rectangle { // Because we don't have "channels" for sending messages to a specific QML object, the messages are broadcast to all QML Items. If an Item of yours happens to be visible when some script sends a message with a method you don't expect, you'll get "Unrecognized message..." logs. break; default: - console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); + // HRS FIXME console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); + walletInventory.fromScript(message); } } signal sendToScript(var message); From ce20b82408dc4b5f7ac38a032e5d5cd9cff82abe Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 24 Oct 2018 10:41:14 -0700 Subject: [PATCH 026/125] fix view this item in my purchases --- 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 3085145176..8f334674cb 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -23,7 +23,7 @@ Script.include("/~/system/libraries/connectionUtils.js"); var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml"; var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml"; var MARKETPLACE_ITEM_TESTER_QML_PATH = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"; -var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; +var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; // HRS FIXME "hifi/commerce/purchases/Purchases.qml"; var MARKETPLACE_WALLET_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); From 8ee6a0ceff9527da0fdd73137a267473f5e5ffc9 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 24 Oct 2018 11:37:02 -0700 Subject: [PATCH 027/125] disable priced items --- scripts/system/html/js/marketplacesInject.js | 25 +++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 0bc0fac13d..f24456cffc 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -284,6 +284,8 @@ $(this).attr('href', '#'); } cost = $(this).closest('.col-xs-3').find('.item-cost').text(); + var costInt = parseInt(cost, 10); + var disable = costInt > 0; // HRS FIXME $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); $(this).closest('.col-xs-3').attr("class", 'col-xs-6'); @@ -292,22 +294,27 @@ priceElement.css({ "padding": "3px 5px", "height": "40px", - "background": "linear-gradient(#00b4ef, #0093C5)", + "background": disable ? "grey" : "linear-gradient(#00b4ef, #0093C5)", "color": "#FFF", "font-weight": "600", "line-height": "34px" }); if (parseInt(cost) > 0) { - priceElement.css({ "width": "auto" }); - priceElement.html(' ' + cost); - priceElement.css({ "min-width": priceElement.width() + 30 }); + if (disable) { + priceElement.html('N/A'); + } else { + priceElement.css({ "width": "auto" }); + priceElement.html(' ' + cost); + priceElement.css({ "min-width": priceElement.width() + 30 }); + } } }); // change pricing to GET/BUY on button hover $('body').on('mouseenter', '#price-or-edit .price', function () { + if (disable) { return; } var $this = $(this); var buyString = "BUY"; var getString = "GET"; @@ -332,12 +339,14 @@ }); $('body').on('mouseleave', '#price-or-edit .price', function () { + if (disable) { return; } var $this = $(this); $this.html($this.data('initialHtml')); }); $('.grid-item').find('#price-or-edit').find('a').on('click', function () { + if (disable) { return false; } if ($(this).closest('.grid-item').find('.price').text() === 'invalidated') { return false; } @@ -420,7 +429,12 @@ var href = purchaseButton.attr('href'); purchaseButton.attr('href', '#'); + var cost = $('.item-cost').text(); + var costInt = parseInt(cost, 10); var availability = $.trim($('.item-availability').text()); + if (costInt > 0) { + availability = ''; // HRS FIXME + } if (availability === 'available') { purchaseButton.css({ "background": "linear-gradient(#00b4ef, #0093C5)", @@ -437,7 +451,6 @@ }); } - var cost = $('.item-cost').text(); var type = $('.item-type').text(); var isUpdating = window.location.href.indexOf('edition=') > -1; var urlParams = new URLSearchParams(window.location.search); From 993cb0cfa0392da8be8f94e34ad261d7f5152b26 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 24 Oct 2018 12:00:17 -0700 Subject: [PATCH 028/125] restore separation of inventory vs proofs. --- interface/resources/qml/hifi/commerce/purchases/Purchases.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 24b365ffc4..11c1ffc8c2 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -476,7 +476,7 @@ Rectangle { anchors.left: parent.left; anchors.leftMargin: 16; width: paintedWidth; - text: "Inventory"; // HRS FIXME isShowingMyItems ? "My Items" : "My Purchases"; + text: isShowingMyItems ? "My Items" : "Inventory"; //"My Purchases"; color: hifi.colors.black; size: 22; } From e1e9b7842785e63a1eb4a4684f64343a363cc011 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 24 Oct 2018 12:22:03 -0700 Subject: [PATCH 029/125] filter priced items from marketplace --- scripts/system/html/js/marketplacesInject.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index f24456cffc..21a2acad7d 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -302,7 +302,8 @@ if (parseInt(cost) > 0) { if (disable) { - priceElement.html('N/A'); + priceElement.html('N/A'); // In case the following fails + $(this).parent().parent().parent().parent().parent().css({"display": "none"}); // HRS FIXME } else { priceElement.css({ "width": "auto" }); priceElement.html(' Date: Wed, 24 Oct 2018 12:54:09 -0700 Subject: [PATCH 031/125] Extra joints on soft entities keep their local transform --- .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 6770cd7f96..36d336f910 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -488,8 +488,8 @@ void Avatar::relayJointDataToChildren() { glm::quat jointRotation; glm::vec3 jointTranslation; if (avatarJointIndex < 0) { - jointRotation = modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex); - jointTranslation = modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex); + jointRotation = modelEntity->getLocalJointRotation(jointIndex); + jointTranslation = modelEntity->getLocalJointTranslation(jointIndex); map.push_back(-1); } else { int jointIndex = getJointIndex(jointName); @@ -512,8 +512,8 @@ void Avatar::relayJointDataToChildren() { jointRotation = getJointRotation(avatarJointIndex); jointTranslation = getJointTranslation(avatarJointIndex); } else { - jointRotation = modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex); - jointTranslation = modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex); + jointRotation = modelEntity->getLocalJointRotation(jointIndex); + jointTranslation = modelEntity->getLocalJointTranslation(jointIndex); } modelEntity->setLocalJointRotation(jointIndex, jointRotation); modelEntity->setLocalJointTranslation(jointIndex, jointTranslation); From 4d34b12f06c9107e1d5c282711b6a8a6b7942d9b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 21 Oct 2018 18:48:38 -0700 Subject: [PATCH 032/125] rather than do checkDevices to find new audio devices every 2 seconds, do each check 2 seconds after the previous one has completed. This is done to avoid swamping the thread pool when the check takes a long time --- libraries/audio-client/src/AudioClient.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index d00bc29054..bdd6d0edc1 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -244,13 +244,20 @@ AudioClient::AudioClient() : // initialize wasapi; if getAvailableDevices is called from the CheckDevicesThread before this, it will crash getAvailableDevices(QAudio::AudioInput); getAvailableDevices(QAudio::AudioOutput); - + // start a thread to detect any device changes _checkDevicesTimer = new QTimer(this); - connect(_checkDevicesTimer, &QTimer::timeout, this, [this] { - QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkDevices(); }); - }); const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000; + connect(_checkDevicesTimer, &QTimer::timeout, this, [=] { + QtConcurrent::run(QThreadPool::globalInstance(), [=] { + checkDevices(); + // On some systems (Ubuntu) checking all the audio devices can take more than 2 seconds. To + // avoid consuming all of the thread pool, don't start the check interval until the previous + // check has completed. + QMetaObject::invokeMethod(_checkDevicesTimer, "start", Q_ARG(int, DEVICE_CHECK_INTERVAL_MSECS)); + }); + }); + _checkDevicesTimer->setSingleShot(true); _checkDevicesTimer->start(DEVICE_CHECK_INTERVAL_MSECS); // start a thread to detect peak value changes From c85dbb823327a66c0c1290eedfb9566ce8c4947f Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 25 Oct 2018 11:25:56 -0700 Subject: [PATCH 033/125] limitedCommerce switch --- interface/src/Application.cpp | 4 +++- interface/src/scripting/WalletScriptingInterface.cpp | 4 ---- interface/src/scripting/WalletScriptingInterface.h | 8 ++++++-- interface/src/ui/overlays/Web3DOverlay.cpp | 3 +++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e515a22403..d053bb2d2d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1144,7 +1144,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store static const QString OCULUS_STORE_ARG = "--oculus-store"; - setProperty(hifi::properties::OCULUS_STORE, arguments().indexOf(OCULUS_STORE_ARG) != -1); + bool isStore = arguments().indexOf(OCULUS_STORE_ARG) != -1; + setProperty(hifi::properties::OCULUS_STORE, isStore); + DependencyManager::get()->setLimitedCommerce(isStore); // Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties? updateHeartbeat(); diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index e2158b9fd7..77ca232712 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -15,10 +15,6 @@ CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(q Q_ASSERT(QThread::currentThread() == qApp->thread()); } -WalletScriptingInterface::WalletScriptingInterface() { - -} - void WalletScriptingInterface::refreshWalletStatus() { auto wallet = DependencyManager::get(); wallet->getWalletStatus(); diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 25955ca7a3..c4ab0da851 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -29,7 +29,6 @@ public: CheckoutProxy(QObject* qmlObject, QObject* parent = nullptr); }; - /**jsdoc * @namespace Wallet * @@ -40,11 +39,12 @@ public: */ class WalletScriptingInterface : public QObject, public Dependency { Q_OBJECT + SINGLETON_DEPENDENCY Q_PROPERTY(uint walletStatus READ getWalletStatus WRITE setWalletStatus NOTIFY walletStatusChanged) + Q_PROPERTY(bool limitedCommerce READ getLimitedCommerce WRITE setLimitedCommerce) public: - WalletScriptingInterface(); /**jsdoc * @function Wallet.refreshWalletStatus @@ -67,6 +67,9 @@ public: // scripts could cause the Wallet to incorrectly report its status. void setWalletStatus(const uint& status); + bool getLimitedCommerce() { return _limitedCommerce; } + void setLimitedCommerce(bool isLimited) { _limitedCommerce = isLimited; } + signals: /**jsdoc @@ -97,6 +100,7 @@ signals: private: uint _walletStatus; + bool _limitedCommerce = false; }; #endif // hifi_WalletScriptingInterface_h diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 084615cae2..1a1bd00655 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -53,6 +53,7 @@ #include "ui/AvatarInputs.h" #include "avatar/AvatarManager.h" #include "scripting/AccountServicesScriptingInterface.h" +#include "scripting/WalletScriptingInterface.h" #include #include "ui/Snapshot.h" #include "SoundCacheScriptingInterface.h" @@ -95,6 +96,7 @@ Web3DOverlay::Web3DOverlay() { _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED _webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("Wallet", DependencyManager::get().data()); } Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) : @@ -269,6 +271,7 @@ void Web3DOverlay::setupQmlSurface(bool isTablet) { _webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this); _webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface()); + _webSurface->getSurfaceContext()->setContextProperty("Wallet", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get().data()); From 6284c902be777e5342ee6c8579caeab7646a0027 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 25 Oct 2018 11:29:10 -0700 Subject: [PATCH 034/125] use the switch --- .../qml/hifi/commerce/purchases/Purchases.qml | 6 +-- .../qml/hifi/commerce/wallet/Wallet.qml | 51 +++++++++++-------- scripts/system/commerce/wallet.js | 3 +- scripts/system/html/js/marketplacesInject.js | 17 ++++--- scripts/system/marketplaces/marketplaces.js | 3 +- 5 files changed, 45 insertions(+), 35 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 11c1ffc8c2..b0d976b499 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -180,8 +180,8 @@ Rectangle { HifiCommerceCommon.EmulatedMarketplaceHeader { id: titleBarContainer; z: 997; - visible: false; //HRS FIXME !needsLogIn.visible; - height: 100; // HRS FIXME + visible: false; + height: 100; // HRS FIXME: get rid of the header and associated code entirely? // Size width: parent.width; // Anchors @@ -476,7 +476,7 @@ Rectangle { anchors.left: parent.left; anchors.leftMargin: 16; width: paintedWidth; - text: isShowingMyItems ? "My Items" : "Inventory"; //"My Purchases"; + text: isShowingMyItems ? "My Items" : "Inventory"; color: hifi.colors.black; size: 22; } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index c2a8258cca..c8fb346711 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -30,6 +30,7 @@ Rectangle { property string activeView: "initialize"; property bool keyboardRaised: false; property bool isPassword: false; + property bool limitedCommerce: true; anchors.fill: (typeof parent === undefined) ? undefined : parent; @@ -65,7 +66,7 @@ Rectangle { } } else if (walletStatus === 5) { if (root.activeView !== "walletSetup") { - root.activeView = "walletInventory"; // HRS FIXME "walletHome"; + root.activeView = "walletInventory"; Commerce.getSecurityImage(); } } else { @@ -125,7 +126,7 @@ Rectangle { // Title Bar text RalewaySemiBold { id: titleBarText; - text: "ASSETS"; //"WALLET"; + text: "ASSETS"; // Text size size: hifi.fontSizes.overlayTitle; // Anchors @@ -370,6 +371,7 @@ Rectangle { listModelName: "Send Money Connections"; z: 997; visible: root.activeView === "sendMoney"; + keyboardContainer: root; anchors.fill: parent; parentAppTitleBarHeight: titleBarContainer.height; parentAppNavBarHeight: tabButtonsContainer.height; @@ -464,13 +466,13 @@ Rectangle { visible: !walletSetup.visible; color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: exchangeMoneyButtonContainer.right; // HRS FIXME parent.left; + anchors.left: exchangeMoneyButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; HiFiGlyphs { id: homeTabIcon; - text: hifi.glyphs.leftRightArrows; // HRS FIXME hifi.glyphs.home2; + text: hifi.glyphs.leftRightArrows; // Size size: 50; // Anchors @@ -478,11 +480,11 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: hifi.colors.lightGray50; // HRS FIXME root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: root.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); } RalewaySemiBold { - text: "RECENT ACTIVITY"; //"WALLET HOME"; + text: "RECENT ACTIVITY"; // Text size size: 16; // Anchors @@ -493,15 +495,16 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: hifi.colors.lightGray50; // HRS FIXME root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: root.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignTop; } - /* HRS FIXME MouseArea { + MouseArea { id: walletHomeTabMouseArea; anchors.fill: parent; + enabled: !root.limitedCommerce; hoverEnabled: enabled; onClicked: { root.activeView = "walletHome"; @@ -509,22 +512,22 @@ Rectangle { } onEntered: parent.color = hifi.colors.blueHighlight; onExited: parent.color = root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black; - }*/ + } } // "EXCHANGE MONEY" tab button Rectangle { id: exchangeMoneyButtonContainer; visible: !walletSetup.visible; - color: root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black; // HRS FIXME hifi.colors.black; + color: root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: parent.left; // FIXME walletHomeButtonContainer.right; + anchors.left: parent.left; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; HiFiGlyphs { id: exchangeMoneyTabIcon; - text: hifi.glyphs.home2; // HRS FIXME hifi.glyphs.leftRightArrows; + text: hifi.glyphs.home2; // Size size: 50; // Anchors @@ -532,11 +535,11 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: root.activeView === "walletInventory" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; // HRS FIXMEhifi.colors.lightGray50; + color: root.activeView === "walletInventory" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; } RalewaySemiBold { - text: "INVENTORY"; // HRS FIXME "EXCHANGE MONEY"; + text: "INVENTORY"; // Text size size: 16; // Anchors @@ -547,7 +550,7 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: root.activeView === "walletInventory" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; // HRS FIXME hifi.colors.lightGray50; + color: root.activeView === "walletInventory" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; @@ -574,7 +577,7 @@ Rectangle { visible: !walletSetup.visible; color: root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: walletHomeButtonContainer.right; // HRS FIXME exchangeMoneyButtonContainer.right; + anchors.left: walletHomeButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; @@ -588,7 +591,7 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: hifi.colors.lightGray50; // HRS FIXME root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: root.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); } RalewaySemiBold { @@ -603,16 +606,17 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: hifi.colors.lightGray50; // HRS FIXME root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: root.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignTop; } - /* HRS FIXME MouseArea { + MouseArea { id: sendMoneyTabMouseArea; anchors.fill: parent; + enabled: !root.limitedCommerce; hoverEnabled: enabled; onClicked: { root.activeView = "sendMoney"; @@ -620,7 +624,7 @@ Rectangle { } onEntered: parent.color = hifi.colors.blueHighlight; onExited: parent.color = root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black; - } */ + } } // "SECURITY" tab button @@ -737,7 +741,7 @@ Rectangle { sendMoneyButtonContainer.color = hifi.colors.black; securityButtonContainer.color = hifi.colors.black; helpButtonContainer.color = hifi.colors.black; - exchangeMoneyButtonContainer.color = hifi.colors.black; // HRS FIXME + exchangeMoneyButtonContainer.color = hifi.colors.black; if (root.activeView === "walletHome") { walletHomeButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "sendMoney") { @@ -746,7 +750,7 @@ Rectangle { securityButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "help") { helpButtonContainer.color = hifi.colors.blueAccent; - } else if (root.activeView == "walletInventory") { // HRS FIXME + } else if (root.activeView == "walletInventory") { exchangeMoneyButtonContainer.color = hifi.colors.blueAccent; } } @@ -825,6 +829,9 @@ Rectangle { case 'avatarDisconnected': // Because we don't have "channels" for sending messages to a specific QML object, the messages are broadcast to all QML Items. If an Item of yours happens to be visible when some script sends a message with a method you don't expect, you'll get "Unrecognized message..." logs. break; + case 'setLimitedCommerce': + root.limitedCommerce = message.limitedCommerce; + break; default: // HRS FIXME console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); walletInventory.fromScript(message); diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index b1a00809fc..58f39ca54c 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -490,6 +490,7 @@ function walletOpened() { triggerPressMapping.enable(); shouldShowDot = false; ui.messagesWaiting(shouldShowDot); + ui.sendMessage({method: 'setLimitedCommerce', limitedCommerce: Wallet.limitedCommerce}); // HRS FIXME Wallet should be accessible in qml. Why isn't it? } function walletClosed() { @@ -582,7 +583,7 @@ function uninstallMarketplaceItemTester() { } } -var BUTTON_NAME = "ASSETS"; //HRS FIXME "WALLET"; +var BUTTON_NAME = "ASSETS"; var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; var ui; function startup() { diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 21a2acad7d..e1863f791a 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -27,6 +27,7 @@ var xmlHttpRequest = null; var isPreparing = false; // Explicitly track download request status. + var limitedCommerce = false; var commerceMode = false; var userIsLoggedIn = false; var walletNeedsSetup = false; @@ -219,7 +220,9 @@ purchasesElement.style = "height:100%;margin-top:18px;font-weight:bold;float:right;margin-right:" + (dropDownElement.offsetWidth + 30) + "px;position:relative;z-index:999;"; navbarBrandElement.parentNode.insertAdjacentElement('beforeend', purchasesElement); - $('#purchasesButton').css('display', 'none'); // HRS FIXME + if (limitedCommerce) { + $('#purchasesButton').css('display', 'none'); + } $('#purchasesButton').on('click', function () { EventBridge.emitWebEvent(JSON.stringify({ type: "PURCHASES", @@ -285,7 +288,7 @@ } cost = $(this).closest('.col-xs-3').find('.item-cost').text(); var costInt = parseInt(cost, 10); - var disable = costInt > 0; // HRS FIXME + var disable = limitedCommerce && (costInt > 0); $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); $(this).closest('.col-xs-3').attr("class", 'col-xs-6'); @@ -303,7 +306,7 @@ if (parseInt(cost) > 0) { if (disable) { priceElement.html('N/A'); // In case the following fails - $(this).parent().parent().parent().parent().parent().css({"display": "none"}); // HRS FIXME + $(this).parent().parent().parent().parent().parent().css({"display": "none"}); // HRS FIXME, oh and do I have to set display non-none in the other branch? } else { priceElement.css({ "width": "auto" }); priceElement.html(' 0) { - availability = ''; // HRS FIXME + if (limitedCommerce && (costInt > 0)) { + availability = ''; } if (availability === 'available') { purchaseButton.css({ @@ -757,6 +757,7 @@ cancelClaraDownload(); } else if (message.type === "marketplaces") { if (message.action === "commerceSetting") { + limitedCommerce = !!message.data.limitedCommerce; commerceMode = !!message.data.commerceMode; userIsLoggedIn = !!message.data.userIsLoggedIn; walletNeedsSetup = !!message.data.walletNeedsSetup; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 15b8c4e7cb..5b1d5c8897 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -230,7 +230,8 @@ function sendCommerceSettings() { userIsLoggedIn: Account.loggedIn, walletNeedsSetup: walletNeedsSetup(), metaverseServerURL: Account.metaverseServerURL, - messagesWaiting: shouldShowDot + messagesWaiting: shouldShowDot, + limitedCommerce: Wallet.limitedCommerce } }); } From 8b797952479eb1b5db73d20ac15fedf5926bc7de Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 25 Oct 2018 12:31:01 -0700 Subject: [PATCH 035/125] nix the word "purchase" where possible --- .../qml/hifi/commerce/checkout/Checkout.qml | 26 +++++++++---------- .../common/EmulatedMarketplaceHeader.qml | 2 +- .../hifi/commerce/common/FirstUseTutorial.qml | 4 +-- .../qml/hifi/commerce/purchases/Purchases.qml | 2 +- .../qml/hifi/commerce/wallet/Wallet.qml | 2 +- .../qml/hifi/commerce/wallet/WalletHome.qml | 2 +- scripts/system/html/js/marketplacesInject.js | 2 +- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index b13f23f17d..19cc0d2d9d 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -383,7 +383,7 @@ Rectangle { anchors.leftMargin: 16; width: paintedWidth; height: paintedHeight; - text: "Review Purchase:"; + text: "Review:"; color: hifi.colors.black; size: 28; } @@ -570,7 +570,7 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: root.isUpdating ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN MY PURCHASES"; + text: root.isUpdating ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN YOUR INVENTORY"; onClicked: { if (root.isUpdating) { sendToScript({method: 'checkout_goToPurchases', filterText: root.baseItemName}); @@ -608,9 +608,9 @@ Rectangle { } else if (root.isCertified) { if (!root.shouldBuyWithControlledFailure) { if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { - lightboxPopup.titleText = "Purchase Content Set"; + lightboxPopup.titleText = "Get Content Set"; lightboxPopup.bodyText = "You will not be able to replace this domain's content with " + root.itemName + - " until the server owner gives you 'Replace Content' permissions.

Are you sure you want to purchase this content set?"; + " until the server owner gives you 'Replace Content' permissions.

Are you sure you want to get this content set?"; lightboxPopup.button1text = "CANCEL"; lightboxPopup.button1method = function() { lightboxPopup.visible = false; @@ -694,7 +694,7 @@ Rectangle { id: completeText2; text: "The " + (root.itemTypesText)[itemTypesArray.indexOf(root.itemType)] + ' ' + root.itemName + '' + - " has been added to your Purchases and a receipt will appear in your Wallet's transaction history."; + " has been added to your Inventory."; // Text size size: 18; // Anchors @@ -864,7 +864,7 @@ Rectangle { RalewaySemiBold { id: myPurchasesLink; - text: 'View this item in My Purchases'; + text: 'View this item in your Inventory'; // Text size size: 18; // Anchors @@ -908,7 +908,7 @@ Rectangle { RalewayRegular { id: pendingText; - text: 'Your item is marked "pending" while your purchase is being confirmed. ' + + text: 'Your item is marked "pending" while it is being confirmed. ' + 'Learn More'; // Text size size: 18; @@ -925,8 +925,8 @@ Rectangle { horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignVCenter; onLinkActivated: { - lightboxPopup.titleText = "Purchase Confirmations"; - lightboxPopup.bodyText = 'Your item is marked "pending" while your purchase is being confirmed.

' + + lightboxPopup.titleText = "Confirmations"; + lightboxPopup.bodyText = 'Your item is marked "pending" while it is being confirmed.

' + 'Confirmations usually take about 90 seconds.'; lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = function() { @@ -971,7 +971,7 @@ Rectangle { RalewayRegular { id: failureHeaderText; - text: "Purchase Failed.
Your Purchases and HFC balance haven't changed."; + text: "Purchase Failed.
Your Inventory and HFC balance haven't changed."; // Text size size: 24; // Anchors @@ -1122,10 +1122,10 @@ Rectangle { if (root.balanceAfterPurchase < 0) { // If you already own the item... if (!root.alreadyOwned) { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item."; + buyText.text = "You do not have sufficient funds to purchase this item."; // Else if you don't already own the item... } else if (canBuyAgain()) { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item again."; + buyText.text = "You do not have sufficient funds to purchase this item again."; } else { buyText.text = "While you do not have sufficient funds to buy this, you already have this item." } @@ -1171,7 +1171,7 @@ Rectangle { buyText.text = ""; } } else { - buyText.text = 'This type of item cannot currently be certified, so it will not show up in "My Purchases". You can access it again for free from the Marketplace.'; + buyText.text = 'This type of item cannot currently be certified, so it will not show up in "Inventory". You can access it again for free from the Marketplace.'; buyTextContainer.color = hifi.colors.white; buyTextContainer.border.color = hifi.colors.white; buyGlyph.text = ""; diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml index 1b77dcd3e9..de9c62c197 100644 --- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml +++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml @@ -128,7 +128,7 @@ Item { RalewaySemiBold { id: myPurchasesText; - text: "My Purchases"; + text: "Inventory"; // Text size size: 18; // Style diff --git a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml index 5f874d3f04..0b982893f1 100644 --- a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml +++ b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml @@ -87,7 +87,7 @@ Rectangle { } RalewayRegular { id: introText2; - text: "My Purchases"; + text: "Inventory"; // Text size size: 22; // Anchors @@ -116,7 +116,7 @@ Rectangle { RalewayRegular { id: step1text; - text: "The 'REZ IT' button makes your purchase appear in front of you."; + text: "The 'REZ IT' button makes your item appear in front of you."; // Text size size: 20; // Anchors diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index b0d976b499..655af79e68 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -946,7 +946,7 @@ Rectangle { // Explanitory text RalewayRegular { id: haventPurchasedYet; - text: "You haven't purchased anything yet!

Get an item from Marketplace to add it to My Purchases."; + text: "You haven't gotten anything yet!

Get an item from Marketplace to add it to your Inventory."; // Text size size: 22; // Anchors diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index c8fb346711..d66ed62768 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -404,7 +404,7 @@ Rectangle { lightboxPopup.titleText = "Automatically Log Out"; lightboxPopup.bodyText = "By default, after you log in to High Fidelity, you will stay logged in to your High Fidelity " + "account even after you close and re-open Interface. This means anyone who opens Interface on your computer " + - "could make purchases with your Wallet.\n\n" + + "could make purchases with your HFC.\n\n" + "If you do not want to stay logged in across Interface sessions, check this box."; lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = function() { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 627da1d43f..246773ff07 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -181,7 +181,7 @@ Item { RalewaySemiBold { id: myPurchasesLink; - text: 'My Purchases'; + text: 'Inventory'; // Anchors anchors.top: parent.top; anchors.topMargin: 26; diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index e1863f791a..43fd9ab935 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -458,7 +458,7 @@ if (isUpdating) { purchaseButton.html('UPDATE FOR FREE'); } else if (availability !== 'available') { - purchaseButton.html('UNAVAILABLE (' + availability + ')'); + purchaseButton.html('UNAVAILABLE' + (availability ? ('(' + availability + ')') : '')); } else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { purchaseButton.html('PURCHASE ' + cost); From e3b1e14db6ffc94470f1ee2fabfb6a587816d04c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 25 Oct 2018 12:37:08 -0700 Subject: [PATCH 036/125] nix "buy" where possible. --- interface/resources/qml/LoginDialog/LinkAccountBody.qml | 2 +- interface/resources/qml/hifi/avatarapp/MessageBoxes.qml | 8 ++++---- .../resources/qml/hifi/commerce/wallet/NeedsLogIn.qml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 48cf124127..00980b0876 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -96,7 +96,7 @@ Item { topMargin: hifi.dimensions.contentSpacing.y } - text: qsTr("Sign in to High Fidelity to make friends, get HFC, and buy interesting things on the Marketplace!") + text: qsTr("Sign in to High Fidelity to make friends, get HFC, and get interesting things on the Marketplace!") width: parent.width wrapMode: Text.WordWrap lineHeight: 1 diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index b7782c697d..2c02272b46 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -63,8 +63,8 @@ MessageBox { popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase; popup.button1text = 'CANCEL' popup.titleText = 'Get Wearables' - popup.bodyText = 'Buy wearables from Marketplace.' + '
' + - 'Wear wearable from My Purchases.' + '
' + '
' + + popup.bodyText = 'Get wearables from Marketplace.' + '
' + + 'Wear wearable from Inventory.' + '
' + '
' + 'Visit “AvatarIsland” to get wearables' popup.imageSource = getWearablesUrl; @@ -128,8 +128,8 @@ MessageBox { popup.button1text = 'CANCEL' popup.titleText = 'Get Avatars' - popup.bodyText = 'Buy avatars from Marketplace.' + '
' + - 'Wear avatars in My Purchases.' + '
' + '
' + + popup.bodyText = 'Get avatars from Marketplace.' + '
' + + 'Wear avatars in Inventory.' + '
' + '
' + 'Visit “BodyMart” to get free avatars.' popup.imageSource = getAvatarsUrl; diff --git a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml index eadf1ca8a2..03af964830 100644 --- a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml +++ b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml @@ -93,7 +93,7 @@ Item { // Text below helper text RalewayRegular { id: loginDetailText; - text: "To buy/sell items on the Marketplace, or to use your Wallet, you must first log in to High Fidelity."; + text: "To get items on the Marketplace, or to use your Assets, you must first log in to High Fidelity."; // Text size size: 18; // Anchors From 3503d79b052f3aa174ab570b36162ebd7e32f0be Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 25 Oct 2018 12:39:33 -0700 Subject: [PATCH 037/125] missed a buy/purchase --- interface/resources/qml/hifi/commerce/checkout/Checkout.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 19cc0d2d9d..6ff7c1ef1c 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -594,7 +594,7 @@ Rectangle { anchors.left: parent.left; anchors.right: parent.right; text: (root.isUpdating && root.itemEdition > 0) ? "CONFIRM UPDATE" : (((root.isCertified) ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ? - ((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item")); + ((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Get It Again" : "Confirm") : "--") : "Get Item")); onClicked: { if (root.isUpdating && root.itemEdition > 0) { // If we're updating an app, the existing app needs to be uninstalled. From 936c2b2cab7a12f675e797a57e587a73665cafe7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 25 Oct 2018 12:46:30 -0700 Subject: [PATCH 038/125] don't show price for free items --- interface/resources/qml/hifi/commerce/checkout/Checkout.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 6ff7c1ef1c..f88a70ff05 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -448,7 +448,7 @@ Rectangle { // "HFC" balance label HiFiGlyphs { id: itemPriceTextLabel; - visible: !(root.isUpdating && root.itemEdition > 0); + visible: !(root.isUpdating && root.itemEdition > 0) && (root.itemPrice > 0); text: hifi.glyphs.hfc; // Size size: 30; @@ -464,7 +464,7 @@ Rectangle { } FiraSansSemiBold { id: itemPriceText; - text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : root.itemPrice); + text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : ((root.itemPrice > 0) ? root.itemPrice : "FREE")); // Text size size: (root.isUpdating && root.itemEdition > 0) ? 20 : 26; // Anchors From 9095df46416c51874aeb2cc1d9028aaddbea5a9e Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 25 Oct 2018 12:59:49 -0700 Subject: [PATCH 039/125] fix getting inventory back after showing my items --- interface/resources/qml/hifi/commerce/wallet/Wallet.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index d66ed62768..ee86844aaa 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -563,6 +563,7 @@ Rectangle { hoverEnabled: enabled; onClicked: { root.activeView = "walletInventory"; + walletInventory.isShowingMyItems = false; tabButtonsContainer.resetTabButtonColors(); } onEntered: parent.color = hifi.colors.blueHighlight; From df005eedbf7867fa886bdb8fc13c0e0fc09c049d Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 29 Oct 2018 17:43:16 -0700 Subject: [PATCH 040/125] fix shape not getting marked changed --- libraries/entities/src/EntityItemProperties.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index d901592759..c243f772e2 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -3207,6 +3207,8 @@ void EntityItemProperties::markAllChanged() { _queryAACubeChanged = true; + _shapeChanged = true; + _flyingAllowedChanged = true; _ghostingAllowedChanged = true; _filterURLChanged = true; From 015b5fa681e03bef2a676416212133c8daa8da5e Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 30 Oct 2018 08:01:22 -0700 Subject: [PATCH 041/125] Don't attempt purchase of sold out items --- scripts/system/html/js/marketplacesInject.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 24a96023da..c667d6dc28 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -312,11 +312,12 @@ var getString = "GET"; // Protection against the button getting stuck in the "BUY"/"GET" state. // That happens when the browser gets two MOUSEENTER events before getting a - // MOUSELEAVE event. - if ($this.text() === buyString || $this.text() === getString) { - return; - } - if ($this.text() === 'invalidated') { + // MOUSELEAVE event. Also, if not available for sale, just return. + if ($this.text() === buyString || + $this.text() === getString || + $this.text() === 'invalidated' || + $this.text() === 'sold out' || + $this.text() === 'not for sale' ) { return; } $this.data('initialHtml', $this.html()); @@ -337,7 +338,10 @@ $('.grid-item').find('#price-or-edit').find('a').on('click', function () { - if ($(this).closest('.grid-item').find('.price').text() === 'invalidated') { + var price = $(this).closest('.grid-item').find('.price').text(); + if (price === 'invalidated' || + price === 'sold out' || + price === 'not for sale') { return false; } buyButtonClicked($(this).closest('.grid-item').attr('data-item-id'), From 959c544fcdd9e4b6f2a09b3fb62a2e862ca79069 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 30 Oct 2018 12:05:41 -0700 Subject: [PATCH 042/125] Made path to VCPKG relative. --- tools/auto-tester/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/auto-tester/CMakeLists.txt b/tools/auto-tester/CMakeLists.txt index 06930ab0fe..1befa6332e 100644 --- a/tools/auto-tester/CMakeLists.txt +++ b/tools/auto-tester/CMakeLists.txt @@ -57,10 +57,10 @@ if (WIN32) ) # add a custom command to copy the SSL DLLs - add_custom_command( + add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_directory D:/GitHub/vcpkg/installed/x64-windows/bin "$" + COMMAND "${CMAKE_COMMAND}" -E copy_directory "$ENV{VCPKG_ROOT}/installed/x64-windows/bin" "$" ) endif () \ No newline at end of file From e64a4d0536be1a0f59b8b9c2d9739b6e4b7ec5b8 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 30 Oct 2018 12:47:44 -0700 Subject: [PATCH 043/125] try to fix unnecessary queryAACube updates --- libraries/shared/src/SpatiallyNestable.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index fd2ff6e790..97e20f5627 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -74,10 +74,12 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) { } }); - bool success = false; - auto parent = getParentPointer(success); - if (success && parent) { - parent->updateQueryAACube(); + if (!_parentKnowsMe) { + bool success = false; + auto parent = getParentPointer(success); + if (success && parent) { + parent->updateQueryAACube(); + } } } From b6344acc1d047dc630b266f594b08fb11f7e089a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 30 Oct 2018 15:03:10 -0700 Subject: [PATCH 044/125] Big checkpoint. be aware - things may be broken --- .../qml/hifi/avatarapp/MessageBoxes.qml | 2 +- .../qml/hifi/commerce/checkout/Checkout.qml | 15 +- .../common/EmulatedMarketplaceHeader.qml | 54 -- .../commerce/common/sendAsset/SendAsset.qml | 12 +- .../hifi/commerce/purchases/PurchasedItem.qml | 1 + .../qml/hifi/commerce/purchases/Purchases.qml | 26 +- .../qml/hifi/commerce/wallet/Wallet.qml | 93 ++- interface/src/Application.cpp | 8 +- interface/src/commerce/Ledger.cpp | 1 - .../src/scripting/WalletScriptingInterface.h | 28 +- interface/src/ui/overlays/Web3DOverlay.cpp | 3 +- scripts/system/avatarapp.js | 8 +- scripts/system/commerce/wallet.js | 161 +++-- scripts/system/edit.js | 2 +- scripts/system/html/js/marketplacesInject.js | 47 +- scripts/system/marketplaces/marketplace.js | 2 +- scripts/system/marketplaces/marketplaces.js | 568 +----------------- scripts/system/notifications.js | 2 +- 18 files changed, 279 insertions(+), 754 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index 2c02272b46..89a8eff025 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -89,7 +89,7 @@ MessageBox { function showDeleteFavorite(favoriteName, callback) { popup.titleText = 'Delete Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName) - popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from My Purchases.' + popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from Inventory.' popup.imageSource = null; popup.button1text = 'CANCEL' popup.button2text = 'DELETE' diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index f88a70ff05..95b040eceb 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -559,7 +559,7 @@ Rectangle { } } - // "View in My Purchases" button + // "View in Inventory" button HifiControlsUit.Button { id: viewInMyPurchasesButton; visible: false; @@ -833,7 +833,7 @@ Rectangle { } lightboxPopup.button2text = "OPEN GOTO"; lightboxPopup.button2method = function() { - sendToScript({method: 'purchases_openGoTo'}); + sendToScript({method: 'checkout_openGoTo'}); lightboxPopup.visible = false; }; lightboxPopup.visible = true; @@ -886,7 +886,8 @@ Rectangle { RalewaySemiBold { id: walletLink; - text: 'View receipt in Wallet'; + visible: !WalletScriptingInterface.limitedCommerce; + text: 'View receipt in Recent Activity'; // Text size size: 18; // Anchors @@ -902,18 +903,18 @@ Rectangle { horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignVCenter; onLinkActivated: { - sendToScript({method: 'purchases_openWallet'}); + sendToScript({method: 'checkout_openWallet'}); } } RalewayRegular { id: pendingText; - text: 'Your item is marked "pending" while it is being confirmed. ' + + text: 'Your item is marked "pending" while the transfer is being confirmed. ' + 'Learn More'; // Text size size: 18; // Anchors - anchors.top: walletLink.bottom; + anchors.top: walletLink.visible ? walletLink.bottom : myPurchasesLink.bottom; anchors.topMargin: 32; height: paintedHeight; anchors.left: parent.left; @@ -926,7 +927,7 @@ Rectangle { verticalAlignment: Text.AlignVCenter; onLinkActivated: { lightboxPopup.titleText = "Confirmations"; - lightboxPopup.bodyText = 'Your item is marked "pending" while it is being confirmed.

' + + lightboxPopup.bodyText = 'Your item is marked "pending" while the transfer is being confirmed.

' + 'Confirmations usually take about 90 seconds.'; lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = function() { diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml index de9c62c197..4b0166425a 100644 --- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml +++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml @@ -27,7 +27,6 @@ Item { property string referrerURL: (Account.metaverseServerURL + "/marketplace?"); readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin; property alias usernameDropdownVisible: usernameDropdown.visible; - property bool messagesWaiting: false; height: mainContainer.height + additionalDropdownHeight; @@ -38,7 +37,6 @@ Item { if (walletStatus === 0) { sendToParent({method: "needsLogIn"}); } else if (walletStatus === 5) { - Commerce.getAvailableUpdates(); Commerce.getSecurityImage(); } else if (walletStatus > 5) { console.log("ERROR in EmulatedMarketplaceHeader.qml: Unknown wallet status: " + walletStatus); @@ -59,14 +57,6 @@ Item { securityImage.source = "image://security/securityImage"; } } - - onAvailableUpdatesResult: { - if (result.status !== 'success') { - console.log("Failed to get Available Updates", result.data.message); - } else { - root.messagesWaiting = result.data.updates.length > 0; - } - } } Component.onCompleted: { @@ -118,50 +108,6 @@ Item { anchors.right: securityImage.left; anchors.rightMargin: 6; - Rectangle { - id: myPurchasesLink; - anchors.right: myUsernameButton.left; - anchors.rightMargin: 8; - anchors.verticalCenter: parent.verticalCenter; - height: 40; - width: myPurchasesText.paintedWidth + 10; - - RalewaySemiBold { - id: myPurchasesText; - text: "Inventory"; - // Text size - size: 18; - // Style - color: hifi.colors.blueAccent; - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignVCenter; - // Anchors - anchors.centerIn: parent; - } - - MouseArea { - anchors.fill: parent; - hoverEnabled: enabled; - onClicked: { - sendToParent({ method: 'header_goToPurchases', hasUpdates: root.messagesWaiting }); - } - onEntered: myPurchasesText.color = hifi.colors.blueHighlight; - onExited: myPurchasesText.color = hifi.colors.blueAccent; - } - } - - Rectangle { - id: messagesWaitingLight; - visible: root.messagesWaiting; - anchors.right: myPurchasesLink.left; - anchors.rightMargin: -2; - anchors.verticalCenter: parent.verticalCenter; - height: 10; - width: height; - radius: height/2; - color: "red"; - } - TextMetrics { id: textMetrics; font.family: "Raleway" diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index 0a5c3e8053..bb4bb624bc 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -73,6 +73,10 @@ Item { } onTransferAssetToNodeResult: { + if (!root.visible) { + return; + } + root.isCurrentlySendingAsset = false; if (result.status === 'success') { @@ -92,6 +96,10 @@ Item { } onTransferAssetToUsernameResult: { + if (!root.visible) { + return; + } + root.isCurrentlySendingAsset = false; if (result.status === 'success') { @@ -1309,13 +1317,13 @@ Item { Rectangle { anchors.top: parent.top; - anchors.topMargin: root.assetName === "" ? 15 : 150; + anchors.topMargin: root.assetName === "" ? 15 : 125; anchors.left: parent.left; anchors.leftMargin: root.assetName === "" ? 15 : 50; anchors.right: parent.right; anchors.rightMargin: root.assetName === "" ? 15 : 50; anchors.bottom: parent.bottom; - anchors.bottomMargin: root.assetName === "" ? 15 : 240; + anchors.bottomMargin: root.assetName === "" ? 15 : 125; color: "#FFFFFF"; RalewaySemiBold { diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index eeb9ac3c54..d13473af22 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -345,6 +345,7 @@ Item { Rectangle { id: permissionExplanationCard; + visible: false; anchors.left: parent.left; anchors.leftMargin: 30; anchors.top: parent.top; diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 655af79e68..eeb98eeb8c 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -19,7 +19,6 @@ import "../../../controls" as HifiControls import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. import "../wallet" as HifiWallet import "../common" as HifiCommerceCommon -import "../inspectionCertificate" as HifiInspectionCertificate import "../common/sendAsset" as HifiSendAsset import "../.." as HifiCommon @@ -118,19 +117,6 @@ Rectangle { } } - HifiInspectionCertificate.InspectionCertificate { - id: inspectionCertificate; - z: 998; - visible: false; - anchors.fill: parent; - - Connections { - onSendToScript: { - sendToScript(message); - } - } - } - HifiCommerceCommon.CommerceLightbox { id: lightboxPopup; z: 999; @@ -200,11 +186,6 @@ Rectangle { lightboxPopup.button1method = function() { lightboxPopup.visible = false; } - lightboxPopup.button2text = "GO TO WALLET"; - lightboxPopup.button2method = function() { - sendToScript({method: 'purchases_openWallet'}); - lightboxPopup.visible = false; - }; lightboxPopup.visible = true; } else { sendToScript(msg); @@ -627,8 +608,6 @@ Rectangle { sendToScript({ method: 'purchases_updateWearables' }); } } else if (msg.method === 'purchases_itemCertificateClicked') { - inspectionCertificate.visible = true; - inspectionCertificate.isLightbox = true; sendToScript(msg); } else if (msg.method === "showInvalidatedLightbox") { lightboxPopup.titleText = "Item Invalidated"; @@ -641,7 +620,7 @@ Rectangle { lightboxPopup.visible = true; } else if (msg.method === "showPendingLightbox") { lightboxPopup.titleText = "Item Pending"; - lightboxPopup.bodyText = 'Your item is marked "pending" while your purchase is being confirmed. ' + + lightboxPopup.bodyText = 'Your item is marked "pending" while the transfer is being confirmed. ' + "Usually, purchases take about 90 seconds to confirm."; lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = function() { @@ -1066,9 +1045,6 @@ Rectangle { titleBarContainer.referrerURL = message.referrerURL || ""; filterBar.text = message.filterText ? message.filterText : ""; break; - case 'inspectionCertificate_setCertificateId': - inspectionCertificate.fromScript(message); - break; case 'purchases_showMyItems': root.isShowingMyItems = true; break; diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index ee86844aaa..a958e62aad 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -21,6 +21,7 @@ import "../common" as HifiCommerceCommon import "../common/sendAsset" import "../.." as HifiCommon import "../purchases" as HifiPurchases +import "../inspectionCertificate" as HifiInspectionCertificate Rectangle { HifiConstants { id: hifi; } @@ -30,7 +31,6 @@ Rectangle { property string activeView: "initialize"; property bool keyboardRaised: false; property bool isPassword: false; - property bool limitedCommerce: true; anchors.fill: (typeof parent === undefined) ? undefined : parent; @@ -67,6 +67,7 @@ Rectangle { } else if (walletStatus === 5) { if (root.activeView !== "walletSetup") { root.activeView = "walletInventory"; + Commerce.getAvailableUpdates(); Commerce.getSecurityImage(); } } else { @@ -88,6 +89,14 @@ Rectangle { titleBarSecurityImage.source = "image://security/securityImage"; } } + + onAvailableUpdatesResult: { + if (result.status !== 'success') { + console.log("Failed to get Available Updates", result.data.message); + } else { + exchangeMoneyButtonContainer.messagesWaiting = result.data.updates.length > 0; + } + } } HifiCommerceCommon.CommerceLightbox { @@ -339,6 +348,10 @@ Rectangle { if (msg.method === 'transactionHistory_usernameLinkClicked') { userInfoViewer.url = msg.usernameLink; userInfoViewer.visible = true; + } else if (msg.method === 'goToPurchases_fromWalletHome') { + root.activeView = "walletInventory"; + walletInventory.isShowingMyItems = false; + tabButtonsContainer.resetTabButtonColors(); } else { sendToScript(msg); } @@ -346,7 +359,20 @@ Rectangle { } } - HifiPurchases.Purchases { + HifiInspectionCertificate.InspectionCertificate { + id: inspectionCertificate; + z: 998; + visible: false; + anchors.fill: parent; + + Connections { + onSendToScript: { + sendToScript(message); + } + } + } + + HifiPurchases.Purchases { id: walletInventory; visible: root.activeView === "walletInventory"; anchors.top: titleBarContainer.bottom; @@ -355,10 +381,15 @@ Rectangle { anchors.right: parent.right; Connections { onSendToScript: { - sendToScript(message); + if (message.method === 'purchases_itemCertificateClicked') { + inspectionCertificate.visible = true; + inspectionCertificate.isLightbox = true; + sendToScript(message); + } else { + sendToScript(message); + } } } - } HifiCommon.RootHttpRequest { @@ -480,7 +511,7 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: root.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); + color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); } RalewaySemiBold { @@ -495,7 +526,7 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: root.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); + color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; @@ -504,7 +535,7 @@ Rectangle { MouseArea { id: walletHomeTabMouseArea; anchors.fill: parent; - enabled: !root.limitedCommerce; + enabled: !WalletScriptingInterface.limitedCommerce; hoverEnabled: enabled; onClicked: { root.activeView = "walletHome"; @@ -518,6 +549,8 @@ Rectangle { // "EXCHANGE MONEY" tab button Rectangle { id: exchangeMoneyButtonContainer; + property bool messagesWaiting: false; + visible: !walletSetup.visible; color: root.activeView === "walletInventory" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; @@ -535,7 +568,20 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: root.activeView === "walletInventory" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: root.activeView === "walletInventory" || inventoryTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + } + + Rectangle { + id: messagesWaitingLight; + visible: parent.messagesWaiting; + anchors.right: exchangeMoneyTabIcon.left; + anchors.rightMargin: -4; + anchors.top: exchangeMoneyTabIcon.top; + anchors.topMargin: 16; + height: 10; + width: height; + radius: height/2; + color: "red"; } RalewaySemiBold { @@ -550,7 +596,7 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: root.activeView === "walletInventory" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; + color: root.activeView === "walletInventory" || inventoryTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; @@ -592,7 +638,7 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: root.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); + color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); } RalewaySemiBold { @@ -607,7 +653,7 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: root.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); + color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; @@ -617,7 +663,7 @@ Rectangle { MouseArea { id: sendMoneyTabMouseArea; anchors.fill: parent; - enabled: !root.limitedCommerce; + enabled: !WalletScriptingInterface.limitedCommerce; hoverEnabled: enabled; onClicked: { root.activeView = "sendMoney"; @@ -825,17 +871,26 @@ Rectangle { break; case 'http.response': http.handleHttpResponse(message); + // Duplicate handler is required because we don't track referrer for `http` + walletInventory.fromScript(message); break; case 'palIsStale': case 'avatarDisconnected': // Because we don't have "channels" for sending messages to a specific QML object, the messages are broadcast to all QML Items. If an Item of yours happens to be visible when some script sends a message with a method you don't expect, you'll get "Unrecognized message..." logs. break; - case 'setLimitedCommerce': - root.limitedCommerce = message.limitedCommerce; - break; - default: - // HRS FIXME console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); + case 'inspectionCertificate_setCertificateId': + inspectionCertificate.fromScript(message); + break; + case 'updatePurchases': + case 'purchases_showMyItems': + case 'updateConnections': + case 'selectRecipient': + case 'updateSelectedRecipientUsername': + case 'updateWearables': walletInventory.fromScript(message); + break; + default: + console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); } } signal sendToScript(var message); @@ -881,7 +936,9 @@ Rectangle { root.activeView = "initialize"; Commerce.getWalletStatus(); } else if (msg.referrer === 'purchases') { - sendToScript({method: 'goToPurchases'}); + root.activeView = "walletInventory"; + walletInventory.isShowingMyItems = false; + tabButtonsContainer.resetTabButtonColors(); } else if (msg.referrer === 'marketplace cta' || msg.referrer === 'mainPage') { sendToScript({method: 'goToMarketplaceMainPage', itemId: msg.referrer}); } else { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3f354f64b5..70c25ce823 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3154,7 +3154,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); surfaceContext->setContextProperty("Selection", DependencyManager::get().data()); surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get().data()); - surfaceContext->setContextProperty("Wallet", DependencyManager::get().data()); + surfaceContext->setContextProperty("WalletScriptingInterface", DependencyManager::get().data()); surfaceContext->setContextProperty("HiFiAbout", AboutUtil::getInstance()); surfaceContext->setContextProperty("ResourceRequestObserver", DependencyManager::get().data()); @@ -6876,7 +6876,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); scriptEngine->registerGlobalObject("Selection", DependencyManager::get().data()); scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("Wallet", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("WalletScriptingInterface", DependencyManager::get().data()); scriptEngine->registerGlobalObject("AddressManager", DependencyManager::get().data()); scriptEngine->registerGlobalObject("HifiAbout", AboutUtil::getInstance()); scriptEngine->registerGlobalObject("ResourceRequestObserver", DependencyManager::get().data()); @@ -7881,6 +7881,10 @@ void Application::loadAvatarBrowser() const { auto tablet = dynamic_cast(DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")); // construct the url to the marketplace item QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars"; + if (DependencyManager::get()->getLimitedCommerce()) { + url += "&isFree=1"; + } + QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js"; tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH); DependencyManager::get()->openTablet(); diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 67303f2a9b..f4bd7a84b3 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -240,7 +240,6 @@ QString transactionString(const QJsonObject& valueObject) { return result; } -static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/"; void Ledger::historySuccess(QNetworkReply* reply) { // here we send a historyResult with some extra stuff in it // Namely, the styled text we'd like to show. The issue is the diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index c4ab0da851..8aebb68a47 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -36,29 +36,29 @@ public: * @hifi-client-entity * * @property {number} walletStatus + * @property {bool} limitedCommerce */ class WalletScriptingInterface : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY - Q_PROPERTY(uint walletStatus READ getWalletStatus WRITE setWalletStatus NOTIFY walletStatusChanged) - Q_PROPERTY(bool limitedCommerce READ getLimitedCommerce WRITE setLimitedCommerce) + Q_PROPERTY(bool limitedCommerce READ getLimitedCommerce WRITE setLimitedCommerce NOTIFY limitedCommerceChanged) public: /**jsdoc - * @function Wallet.refreshWalletStatus + * @function WalletScriptingInterface.refreshWalletStatus */ Q_INVOKABLE void refreshWalletStatus(); /**jsdoc - * @function Wallet.getWalletStatus + * @function WalletScriptingInterface.getWalletStatus * @returns {number} */ Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } /**jsdoc - * @function Wallet.proveAvatarEntityOwnershipVerification + * @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification * @param {Uuid} entityID */ Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID); @@ -67,32 +67,38 @@ public: // scripts could cause the Wallet to incorrectly report its status. void setWalletStatus(const uint& status); - bool getLimitedCommerce() { return _limitedCommerce; } - void setLimitedCommerce(bool isLimited) { _limitedCommerce = isLimited; } + bool getLimitedCommerce() { return _limitedCommerce; } + void setLimitedCommerce(bool isLimited) { _limitedCommerce = isLimited; } signals: /**jsdoc - * @function Wallet.walletStatusChanged + * @function WalletScriptingInterface.walletStatusChanged * @returns {Signal} */ void walletStatusChanged(); /**jsdoc - * @function Wallet.walletNotSetup + * @function WalletScriptingInterface.limitedCommerceChanged + * @returns {Signal} + */ + void limitedCommerceChanged(); + + /**jsdoc + * @function WalletScriptingInterface.walletNotSetup * @returns {Signal} */ void walletNotSetup(); /**jsdoc - * @function Wallet.ownershipVerificationSuccess + * @function WalletScriptingInterface.ownershipVerificationSuccess * @param {Uuid} entityID * @returns {Signal} */ void ownershipVerificationSuccess(const QUuid& entityID); /**jsdoc - * @function Wallet.ownershipVerificationFailed + * @function WalletScriptingInterface.ownershipVerificationFailed * @param {Uuid} entityID * @returns {Signal} */ diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 1a1bd00655..f13d25f22c 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -96,7 +96,6 @@ Web3DOverlay::Web3DOverlay() { _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED _webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("Wallet", DependencyManager::get().data()); } Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) : @@ -271,8 +270,8 @@ void Web3DOverlay::setupQmlSurface(bool isTablet) { _webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this); _webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface()); - _webSurface->getSurfaceContext()->setContextProperty("Wallet", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("WalletScriptingInterface", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get().data()); // Override min fps for tablet UI, for silky smooth scrolling diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index ece35acce7..2f05b1b337 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -159,8 +159,8 @@ var selectedAvatarEntityGrabbable = false; var selectedAvatarEntityID = null; var grabbedAvatarEntityChangeNotifier = null; -var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; -var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; +var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; +var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace" + (WalletScriptingInterface.limitedCommerce ? "?isFree=1" : ""); var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("html/js/marketplacesInject.js"); function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml. @@ -285,9 +285,9 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See case 'navigate': var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system") if(message.url.indexOf('app://') === 0) { - if(message.url === 'app://marketplace') { + if (message.url === 'app://marketplace') { tablet.gotoWebScreen(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); - } else if(message.url === 'app://purchases') { + } else if (message.url === 'app://purchases') { tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); } diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 58f39ca54c..353145035e 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -11,15 +11,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global getConnectionData */ +/* global getConnectionData getControllerWorldLocation openLoginWindow WalletScriptingInterface */ (function () { // BEGIN LOCAL_SCOPE Script.include("/~/system/libraries/accountUtils.js"); Script.include("/~/system/libraries/connectionUtils.js"); var AppUi = Script.require('appUi'); -var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; - +var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace" + + (WalletScriptingInterface.limitedCommerce ? "?isFree=1" : ""); // BEGIN AVATAR SELECTOR LOGIC var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; @@ -48,7 +48,6 @@ ExtendedOverlay.prototype.editOverlay = function (properties) { // change displa function color(selected, hovering) { var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; function scale(component) { - var delta = 0xFF - component; return component; } return { red: scale(base.red), green: scale(base.green), blue: scale(base.blue) }; @@ -105,7 +104,8 @@ 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) { - var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. + // Depends on nearer coverOverlays to extend closer to us than farther ones. + var pickedOverlay = Overlays.findRayIntersection(pickRay); if (!pickedOverlay.intersects) { if (noHit) { return noHit(); @@ -131,6 +131,7 @@ function addAvatarNode(id) { } var pingPong = true; +var OVERLAY_SCALE = 0.032; function updateOverlays() { var eye = Camera.position; AvatarList.getAvatarIdentifiers().forEach(function (id) { @@ -148,7 +149,8 @@ function updateOverlays() { var target = avatar.position; var distance = Vec3.distance(target, eye); var offset = 0.2; - var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position) + // get diff between target and eye (a vector pointing to the eye from avatar position) + var diff = Vec3.subtract(target, eye); var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can if (headIndex > 0) { offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; @@ -164,7 +166,7 @@ function updateOverlays() { overlay.editOverlay({ color: color(ExtendedOverlay.isSelected(id), overlay.hovering), position: target, - dimensions: 0.032 * distance + dimensions: OVERLAY_SCALE * distance }); }); pingPong = !pingPong; @@ -380,6 +382,23 @@ function onUsernameChanged() { Settings.setValue("wallet/autoLogout", false); Settings.setValue("wallet/savedUsername", ""); } +} + +var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); +var METAVERSE_SERVER_URL = Account.metaverseServerURL; +var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. +function openMarketplace(optionalItemOrUrl) { + // This is a bit of a kluge, but so is the whole file. + // If given a whole path, use it with no cta. + // If given an id, build the appropriate url and use the id as the cta. + // Otherwise, use home and 'marketplace cta'. + // AND... if call onMarketplaceOpen to setupWallet if we need to. + var url = optionalItemOrUrl || MARKETPLACE_URL_INITIAL; + // If optionalItemOrUrl contains the metaverse base, then it's a url, not an item id. + if (optionalItemOrUrl && optionalItemOrUrl.indexOf(METAVERSE_SERVER_URL) === -1) { + url = MARKETPLACE_URL + '/items/' + optionalItemOrUrl; + } + ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL); } // Function Name: fromQml() @@ -387,8 +406,6 @@ function onUsernameChanged() { // Description: // -Called when a message is received from SpectatorCamera.qml. The "message" argument is what is sent from the QML // in the format "{method, params}", like json-rpc. See also sendToQml(). -var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; -var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); function fromQml(message) { switch (message.method) { case 'passphrasePopup_cancelClicked': @@ -422,10 +439,6 @@ function fromQml(message) { case 'transactionHistory_linkClicked': ui.open(message.marketplaceLink, MARKETPLACES_INJECT_SCRIPT_URL); break; - case 'goToPurchases_fromWalletHome': - case 'goToPurchases': - ui.open(MARKETPLACE_PURCHASES_QML_PATH); - break; case 'goToMarketplaceMainPage': ui.open(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); break; @@ -450,22 +463,20 @@ function fromQml(message) { removeOverlays(); break; case 'sendAsset_sendPublicly': - if (message.assetName === "") { - deleteSendMoneyParticleEffect(); - sendMoneyRecipient = message.recipient; - var amount = message.amount; - var props = SEND_MONEY_PARTICLE_PROPERTIES; - props.parentID = MyAvatar.sessionUUID; - props.position = MyAvatar.position; - props.position.y += 0.2; - if (message.effectImage) { - props.textures = message.effectImage; - } - sendMoneyParticleEffect = Entities.addEntity(props, true); - particleEffectTimestamp = Date.now(); - updateSendMoneyParticleEffect(); - sendMoneyParticleEffectUpdateTimer = Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE); + deleteSendMoneyParticleEffect(); + sendMoneyRecipient = message.recipient; + var props = SEND_MONEY_PARTICLE_PROPERTIES; + props.parentID = MyAvatar.sessionUUID; + props.position = MyAvatar.position; + props.position.y += 0.2; + if (message.effectImage) { + props.textures = message.effectImage; } + sendMoneyParticleEffect = Entities.addEntity(props, true); + particleEffectTimestamp = Date.now(); + updateSendMoneyParticleEffect(); + sendMoneyParticleEffectUpdateTimer = + Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE); break; case 'transactionHistory_goToBank': if (Account.metaverseServerURL.indexOf("staging") >= 0) { @@ -474,6 +485,46 @@ function fromQml(message) { Window.location = "hifi://BankOfHighFidelity"; } break; + case 'purchases_updateWearables': + var currentlyWornWearables = []; + var ATTACHMENT_SEARCH_RADIUS = 100; // meters (just in case) + + var nearbyEntities = Entities.findEntitiesByType('Model', MyAvatar.position, ATTACHMENT_SEARCH_RADIUS); + + for (var i = 0; i < nearbyEntities.length; i++) { + var currentProperties = Entities.getEntityProperties( + nearbyEntities[i], ['certificateID', 'editionNumber', 'parentID'] + ); + if (currentProperties.parentID === MyAvatar.sessionUUID) { + currentlyWornWearables.push({ + entityID: nearbyEntities[i], + entityCertID: currentProperties.certificateID, + entityEdition: currentProperties.editionNumber + }); + } + } + + ui.tablet.sendToQml({ method: 'updateWearables', wornWearables: currentlyWornWearables }); + break; + case 'purchases_availableUpdatesReceived': + shouldShowDot = message.numUpdates > 0; + ui.messagesWaiting(shouldShowDot && !ui.isOpen); + break; + case 'purchases_walletNotSetUp': + ui.tablet.sendToQml({ + method: 'updateWalletReferrer', + referrer: "purchases" + }); + break; + case 'purchases_openGoTo': + ui.open("hifi/tablet/TabletAddressDialog.qml"); + break; + case 'purchases_itemInfoClicked': + var itemId = message.itemId; + if (itemId && itemId !== "") { + openMarketplace(itemId); + } + break; case 'http.request': // Handled elsewhere, don't log. break; @@ -482,23 +533,28 @@ function fromQml(message) { } } +var isWired = false; function walletOpened() { Users.usernameFromIDReply.connect(usernameFromIDReply); Controller.mousePressEvent.connect(handleMouseEvent); Controller.mouseMoveEvent.connect(handleMouseMoveEvent); triggerMapping.enable(); triggerPressMapping.enable(); + isWired = true; shouldShowDot = false; ui.messagesWaiting(shouldShowDot); - ui.sendMessage({method: 'setLimitedCommerce', limitedCommerce: Wallet.limitedCommerce}); // HRS FIXME Wallet should be accessible in qml. Why isn't it? } function walletClosed() { off(); } -function notificationDataProcessPage(data) { - return data.data.updates; // HRS FIXME .history; +function notificationDataProcessPageUpdates(data) { + return data.data.updates; +} + +function notificationDataProcessPageHistory(data) { + return data.data.history; } var shouldShowDot = false; @@ -511,7 +567,7 @@ function notificationPollCallbackUpdates(updatesArray) { if (!ui.notificationInitialCallbackMade) { message = updatesArray.length + " of your purchased items " + (updatesArray.length === 1 ? "has an update " : "have updates ") + - "available. Open MARKET to update."; + "available. Open WALLET to update."; ui.notificationDisplayBanner(message); ui.notificationPollCaresAboutSince = true; @@ -519,13 +575,13 @@ function notificationPollCallbackUpdates(updatesArray) { for (var i = 0; i < updatesArray.length; i++) { message = "Update available for \"" + updatesArray[i].base_item_title + "\"." + - "Open MARKET to update."; + "Open WALLET to update."; ui.notificationDisplayBanner(message); } } } } -function notificationPollCallback(historyArray) { +function notificationPollCallbackHistory(historyArray) { if (!ui.isOpen) { var notificationCount = historyArray.length; shouldShowDot = shouldShowDot || notificationCount > 0; @@ -548,7 +604,12 @@ function notificationPollCallback(historyArray) { } } -function isReturnedDataEmpty(data) { +function isReturnedDataEmptyUpdates(data) { + var historyArray = data.data.history; + return historyArray.length === 0; +} + +function isReturnedDataEmptyHistory(data) { var historyArray = data.data.history; return historyArray.length === 0; } @@ -585,6 +646,7 @@ function uninstallMarketplaceItemTester() { var BUTTON_NAME = "ASSETS"; var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; +var NOTIFICATION_POLL_TIMEOUT = 300000; var ui; function startup() { ui = new AppUi({ @@ -595,13 +657,20 @@ function startup() { onOpened: walletOpened, onClosed: walletClosed, onMessage: fromQml, - // How are we going to handle two polls when --limitedCommerce is false? - notificationPollEndpoint: "/api/v1/commerce/available_updates?per_page=10", // HRS FIXME "/api/v1/commerce/history?per_page=10", +/* Gotta re-add all this stuff once I get it working + notificationPollEndpoint: ["/api/v1/commerce/available_updates?per_page=10", "/api/v1/commerce/history?per_page=10"], + notificationPollTimeoutMs: [NOTIFICATION_POLL_TIMEOUT, NOTIFICATION_POLL_TIMEOUT], + notificationDataProcessPage: [notificationDataProcessPageUpdates, notificationDataProcessPageHistory], + notificationPollCallback: [notificationPollCallbackUpdates, notificationPollCallbackHistory], + notificationPollStopPaginatingConditionMet: [isReturnedDataEmptyUpdates, isReturnedDataEmptyHistory], + notificationPollCaresAboutSince: [false, true] +*/ + notificationPollEndpoint: "/api/v1/commerce/available_updates?per_page=10", notificationPollTimeoutMs: 300000, - notificationDataProcessPage: notificationDataProcessPage, + notificationDataProcessPage: notificationDataProcessPageUpdates, notificationPollCallback: notificationPollCallbackUpdates, - notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - notificationPollCaresAboutSince: false // HRS FIXME true + notificationPollStopPaginatingConditionMet: isReturnedDataEmptyUpdates, + notificationPollCaresAboutSince: false }); GlobalServices.myUsernameChanged.connect(onUsernameChanged); installMarketplaceItemTester(); @@ -609,11 +678,13 @@ function startup() { var isUpdateOverlaysWired = false; function off() { - Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Controller.mousePressEvent.disconnect(handleMouseEvent); - Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); - triggerMapping.disable(); - triggerPressMapping.disable(); + if (isWired) { + Users.usernameFromIDReply.disconnect(usernameFromIDReply); + Controller.mousePressEvent.disconnect(handleMouseEvent); + Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); + triggerMapping.disable(); + triggerPressMapping.disable(); + } if (isUpdateOverlaysWired) { Script.update.disconnect(updateOverlays); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6425806771..ee1f5ecbec 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -193,7 +193,7 @@ var importingSVOTextOverlay = Overlays.addOverlay("text", { visible: false }); -var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; +var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace" + (WalletScriptingInterface.limitedCommerce ? "?isFree=1" : ""); var marketplaceWindow = new OverlayWebWindow({ title: 'Marketplace', source: "about:blank", diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 43fd9ab935..fd228e2596 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -60,7 +60,7 @@ ); // Footer. - var isInitialHiFiPage = location.href === marketplaceBaseURL + "/marketplace?"; + var isInitialHiFiPage = location.href === (marketplaceBaseURL + "/marketplace" + (limitedCommerce ? "?isFree=1" : "?")); $("body").append( '
' + (!isInitialHiFiPage ? '' : '') + @@ -72,7 +72,7 @@ // Footer actions. $("#back-button").on("click", function () { - (document.referrer !== "") ? window.history.back() : window.location = (marketplaceBaseURL + "/marketplace?"); + (document.referrer !== "") ? window.history.back() : window.location = (marketplaceBaseURL + "/marketplace?") + (limitedCommerce ? "isFree=1" : ""); }); $("#all-markets").on("click", function () { EventBridge.emitWebEvent(JSON.stringify({ @@ -93,7 +93,7 @@ window.location = "https://clara.io/library?gameCheck=true&public=true"; }); $('#exploreHifiMarketplace').on('click', function () { - window.location = marketplaceBaseURL + "/marketplace"; + window.location = marketplaceBaseURL + "/marketplace" + (limitedCommerce ? "?isFree=1" : "?"); }); } @@ -196,43 +196,6 @@ } } - function maybeAddPurchasesButton() { - if (userIsLoggedIn) { - // Why isn't this an id?! This really shouldn't be a class on the website, but it is. - var navbarBrandElement = document.getElementsByClassName('navbar-brand')[0]; - var purchasesElement = document.createElement('a'); - var dropDownElement = document.getElementById('user-dropdown'); - - $('#user-dropdown').find('.username')[0].style = "max-width:80px;white-space:nowrap;overflow:hidden;" + - "text-overflow:ellipsis;display:inline-block;position:relative;top:4px;"; - $('#user-dropdown').find('.caret')[0].style = "position:relative;top:-3px;"; - - purchasesElement.id = "purchasesButton"; - purchasesElement.setAttribute('href', "#"); - purchasesElement.innerHTML = ""; - if (messagesWaiting) { - purchasesElement.innerHTML += " "; - } - purchasesElement.innerHTML += "My Purchases"; - // FRONTEND WEBDEV RANT: The username dropdown should REALLY not be programmed to be on the same - // line as the search bar, overlaid on top of the search bar, floated right, and then relatively bumped up using "top:-50px". - $('.navbar-brand').css('margin-right', '10px'); - purchasesElement.style = "height:100%;margin-top:18px;font-weight:bold;float:right;margin-right:" + (dropDownElement.offsetWidth + 30) + - "px;position:relative;z-index:999;"; - navbarBrandElement.parentNode.insertAdjacentElement('beforeend', purchasesElement); - if (limitedCommerce) { - $('#purchasesButton').css('display', 'none'); - } - $('#purchasesButton').on('click', function () { - EventBridge.emitWebEvent(JSON.stringify({ - type: "PURCHASES", - referrerURL: window.location.href, - hasUpdates: messagesWaiting - })); - }); - } - } - function changeDropdownMenu() { var logInOrOutButton = document.createElement('a'); logInOrOutButton.id = "logInOrOutButton"; @@ -409,7 +372,6 @@ // Try this here in case it works (it will if the user just pressed the "back" button, // since that doesn't trigger another AJAX request. injectBuyButtonOnMainPage(); - maybeAddPurchasesButton(); } } @@ -458,7 +420,7 @@ if (isUpdating) { purchaseButton.html('UPDATE FOR FREE'); } else if (availability !== 'available') { - purchaseButton.html('UNAVAILABLE' + (availability ? ('(' + availability + ')') : '')); + purchaseButton.html('UNAVAILABLE ' + (availability ? ('(' + availability + ')') : '')); } else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { purchaseButton.html('PURCHASE ' + cost); @@ -476,7 +438,6 @@ type); } }); - maybeAddPurchasesButton(); } } diff --git a/scripts/system/marketplaces/marketplace.js b/scripts/system/marketplaces/marketplace.js index d90695c767..2f749656d3 100644 --- a/scripts/system/marketplaces/marketplace.js +++ b/scripts/system/marketplaces/marketplace.js @@ -15,7 +15,7 @@ Script.include("../libraries/WebTablet.js"); var toolIconUrl = Script.resolvePath("../assets/images/tools/"); -var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; +var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace" + (WalletScriptingInterface.limitedCommerce ? "?isFree=1" : ""); var marketplaceWindow = new OverlayWebWindow({ title: "Marketplace", source: "about:blank", diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 5b1d5c8897..9f55bade6a 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -10,7 +10,7 @@ /* global Tablet, Script, HMD, UserActivityLogger, Entities, Account, Wallet, ContextOverlay, Settings, Camera, Vec3, Quat, MyAvatar, Clipboard, Menu, Grid, Uuid, GlobalServices, openLoginWindow, getConnectionData, Overlays, SoundCache, - DesktopPreviewProvider */ + DesktopPreviewProvider, ResourceRequestObserver */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ var selectionDisplay = null; // for gridTool.js to ignore @@ -115,10 +115,10 @@ function setTabletVisibleInSecondaryCamera(visibleInSecondaryCam) { tabletShouldBeVisibleInSecondaryCamera = Overlays.getProperty(HMD.tabletID, "isVisibleInSecondaryCamera"); } - Overlays.editOverlay(HMD.tabletID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.homeButtonID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.homeButtonHighlightID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.tabletScreenID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); + Overlays.editOverlay(HMD.tabletID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); + Overlays.editOverlay(HMD.homeButtonID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); + Overlays.editOverlay(HMD.homeButtonHighlightID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); + Overlays.editOverlay(HMD.tabletScreenID, { isVisibleInSecondaryCamera: visibleInSecondaryCam }); } function openWallet() { @@ -137,7 +137,7 @@ function setupWallet(referrer) { } function onMarketplaceOpen(referrer) { - var cta = referrer, match; + var match; if (Account.loggedIn && walletNeedsSetup()) { if (referrer === MARKETPLACE_URL_INITIAL) { setupWallet('marketplace cta'); @@ -165,6 +165,9 @@ function openMarketplace(optionalItemOrUrl) { if (optionalItemOrUrl && optionalItemOrUrl.indexOf(METAVERSE_SERVER_URL) === -1) { url = MARKETPLACE_URL + '/items/' + optionalItemOrUrl; } + if (WalletScriptingInterface.limitedCommerce) { + url += "?isFree=1"; + } ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL); } @@ -218,7 +221,7 @@ function onUsernameChanged() { } function walletNeedsSetup() { - return Wallet.walletStatus === 1; + return WalletScriptingInterface.walletStatus === 1; } function sendCommerceSettings() { @@ -230,290 +233,11 @@ function sendCommerceSettings() { userIsLoggedIn: Account.loggedIn, walletNeedsSetup: walletNeedsSetup(), metaverseServerURL: Account.metaverseServerURL, - messagesWaiting: shouldShowDot, - limitedCommerce: Wallet.limitedCommerce + limitedCommerce: WalletScriptingInterface.limitedCommerce } }); } -// BEGIN AVATAR SELECTOR LOGIC -var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; -var SELECTED_COLOR = { red: 0xF3, green: 0x91, blue: 0x29 }; -var HOVER_COLOR = { red: 0xD0, green: 0xD0, blue: 0xD0 }; - -var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier. - -function ExtendedOverlay(key, type, properties) { // A wrapper around overlays to store the key it is associated with. - overlays[key] = this; - this.key = key; - this.selected = false; - this.hovering = false; - this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected... -} -// Instance methods: -ExtendedOverlay.prototype.deleteOverlay = function () { // remove display and data of this overlay - Overlays.deleteOverlay(this.activeOverlay); - delete overlays[this.key]; -}; - -ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay - Overlays.editOverlay(this.activeOverlay, properties); -}; - -function color(selected, hovering) { - var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; - function scale(component) { - return component; - } - return { red: scale(base.red), green: scale(base.green), blue: scale(base.blue) }; -} -// 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; - } - lastHoveringId = 0; - } - this.editOverlay({ color: color(this.selected, hovering) }); - if (hovering) { - // un-hover the last hovering overlay - if (lastHoveringId && lastHoveringId !== this.key) { - ExtendedOverlay.get(lastHoveringId).hover(false); - } - lastHoveringId = this.key; - } -}; -ExtendedOverlay.prototype.select = function (selected) { - if (this.selected === selected) { - return; - } - - this.editOverlay({ color: color(selected, this.hovering) }); - this.selected = selected; -}; -// Class methods: -var selectedId = false; -ExtendedOverlay.isSelected = function (id) { - return selectedId === id; -}; -ExtendedOverlay.get = function (key) { // answer the extended overlay data object associated with the given avatar identifier - return overlays[key]; -}; -ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator returns truthy. - var key; - for (key in overlays) { - if (iterator(ExtendedOverlay.get(key))) { - return; - } - } -}; -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) -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) { - return noHit(); - } - return; - } - ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours. - if ((overlay.activeOverlay) === pickedOverlay.overlayID) { - hit(overlay); - return true; - } - }); -}; - -function addAvatarNode(id) { - return new ExtendedOverlay(id, "sphere", { - drawInFront: true, - solid: true, - alpha: 0.8, - color: color(false, false), - ignoreRayIntersection: false - }); -} - -var pingPong = true; -function updateOverlays() { - var eye = Camera.position; - AvatarList.getAvatarIdentifiers().forEach(function (id) { - if (!id) { - return; // don't update ourself, or avatars we're not interested in - } - var avatar = AvatarList.getAvatar(id); - if (!avatar) { - return; // will be deleted below if there had been an overlay. - } - var overlay = ExtendedOverlay.get(id); - if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back. - overlay = addAvatarNode(id); - } - var target = avatar.position; - var distance = Vec3.distance(target, eye); - var offset = 0.2; - var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position) - var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can - if (headIndex > 0) { - offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; - } - - // move a bit in front, towards the camera - target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset)); - - // now bump it up a bit - target.y = target.y + offset; - - overlay.ping = pingPong; - overlay.editOverlay({ - color: color(ExtendedOverlay.isSelected(id), overlay.hovering), - position: target, - dimensions: 0.032 * distance - }); - }); - pingPong = !pingPong; - ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.) - if (overlay.ping === pingPong) { - overlay.deleteOverlay(); - } - }); -} -function removeOverlays() { - selectedId = false; - lastHoveringId = 0; - ExtendedOverlay.some(function (overlay) { - overlay.deleteOverlay(); - }); -} - -// -// Clicks. -// -function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { - if (selectedId === id) { - var message = { - method: 'updateSelectedRecipientUsername', - userName: username === "" ? "unknown username" : username - }; - ui.tablet.sendToQml(message); - } -} -function handleClick(pickRay) { - ExtendedOverlay.applyPickRay(pickRay, function (overlay) { - var nextSelectedStatus = !overlay.selected; - var avatarId = overlay.key; - selectedId = nextSelectedStatus ? avatarId : false; - if (nextSelectedStatus) { - Users.requestUsernameFromID(avatarId); - } - var message = { - method: 'selectRecipient', - id: avatarId, - isSelected: nextSelectedStatus, - displayName: '"' + AvatarList.getAvatar(avatarId).sessionDisplayName + '"', - userName: '' - }; - ui.tablet.sendToQml(message); - - ExtendedOverlay.some(function (overlay) { - var id = overlay.key; - var selected = ExtendedOverlay.isSelected(id); - overlay.select(selected); - }); - - return true; - }); -} -function handleMouseEvent(mousePressEvent) { // handleClick if we get one. - if (!mousePressEvent.isLeftButton) { - return; - } - 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(); - }); -} - -// handy global to keep track of which hand is the mouse (if any) -var currentHandPressed = 0; -var TRIGGER_CLICK_THRESHOLD = 0.85; -var TRIGGER_PRESS_THRESHOLD = 0.05; - -function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position - var pickRay; - if (HMD.active) { - if (currentHandPressed !== 0) { - pickRay = controllerComputePickRay(currentHandPressed); - } else { - // nothing should hover, so - ExtendedOverlay.unHover(); - return; - } - } else { - pickRay = Camera.computePickRay(event.x, event.y); - } - handleMouseMove(pickRay); -} -function handleTriggerPressed(hand, value) { - // The idea is if you press one trigger, it is the one - // we will consider the mouse. Even if the other is pressed, - // we ignore it until this one is no longer pressed. - var isPressed = value > TRIGGER_PRESS_THRESHOLD; - if (currentHandPressed === 0) { - currentHandPressed = isPressed ? hand : 0; - return; - } - 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) { - return { origin: controllerPose.position, direction: Quat.getUp(controllerPose.orientation) }; - } -} -function makeClickHandler(hand) { - return function (clicked) { - if (clicked > TRIGGER_CLICK_THRESHOLD) { - var pickRay = controllerComputePickRay(hand); - handleClick(pickRay); - } - }; -} -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)); -// END AVATAR SELECTOR LOGIC - var grid = new Grid(); function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) { // Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original @@ -563,6 +287,7 @@ function defaultFor(arg, val) { return typeof arg !== 'undefined' ? arg : val; } +var CERT_ID_URLPARAM_LENGTH = 15; // length of "certificate_id=" function rezEntity(itemHref, itemType, marketplaceItemTesterId) { var isWearable = itemType === "wearable"; var success = Clipboard.importEntities(itemHref, true, marketplaceItemTesterId); @@ -585,7 +310,7 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) { } var certPos = itemHref.search("certificate_id="); // TODO how do I parse a URL from here? if (certPos >= 0) { - certPos += 15; // length of "certificate_id=" + certPos += CERT_ID_URLPARAM_LENGTH; var certURLEncoded = itemHref.substring(certPos); var certB64Encoded = decodeURIComponent(certURLEncoded); for (var key in wearableTransforms) { @@ -594,7 +319,7 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) { if (certificateTransforms) { for (var certID in certificateTransforms) { if (certificateTransforms.hasOwnProperty(certID) && - certID == certB64Encoded) { + certID === certB64Encoded) { var certificateTransform = certificateTransforms[certID]; wearableLocalPosition = certificateTransform.localPosition; wearableLocalRotation = certificateTransform.localRotation; @@ -637,8 +362,10 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) { targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z); var targetPosition = getPositionToCreateEntity(); - var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection. - var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection. + // Distance to move entities parallel to targetDirection. + var deltaParallel = HALF_TREE_SCALE; + // Distance to move entities perpendicular to targetDirection. + var deltaPerpendicular = Vec3.ZERO; for (var i = 0, length = pastedEntityIDs.length; i < length; i++) { var curLoopEntityProps = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions", "registrationPoint", "rotation", "parentID"]); @@ -666,7 +393,8 @@ function rezEntity(itemHref, itemType, marketplaceItemTesterId) { } if (!Vec3.equal(deltaPosition, Vec3.ZERO)) { - for (var editEntityIndex = 0, numEntities = pastedEntityIDs.length; editEntityIndex < numEntities; editEntityIndex++) { + for (var editEntityIndex = 0, + numEntities = pastedEntityIDs.length; editEntityIndex < numEntities; editEntityIndex++) { if (Uuid.isNull(entityParentIDs[editEntityIndex])) { Entities.editEntity(pastedEntityIDs[editEntityIndex], { position: Vec3.sum(deltaPosition, entityPositions[editEntityIndex]) @@ -769,79 +497,6 @@ function onWebEventReceived(message) { }); } } -var sendAssetRecipient; -var sendAssetParticleEffectUpdateTimer; -var particleEffectTimestamp; -var sendAssetParticleEffect; -var SEND_ASSET_PARTICLE_TIMER_UPDATE = 250; -var SEND_ASSET_PARTICLE_EMITTING_DURATION = 3000; -var SEND_ASSET_PARTICLE_LIFETIME_SECONDS = 8; -var SEND_ASSET_PARTICLE_PROPERTIES = { - accelerationSpread: { x: 0, y: 0, z: 0 }, - alpha: 1, - alphaFinish: 1, - alphaSpread: 0, - alphaStart: 1, - azimuthFinish: 0, - azimuthStart: -6, - color: { red: 255, green: 222, blue: 255 }, - colorFinish: { red: 255, green: 229, blue: 225 }, - colorSpread: { red: 0, green: 0, blue: 0 }, - colorStart: { red: 243, green: 255, blue: 255 }, - emitAcceleration: { x: 0, y: 0, z: 0 }, // Immediately gets updated to be accurate - emitDimensions: { x: 0, y: 0, z: 0 }, - emitOrientation: { x: 0, y: 0, z: 0 }, - emitRate: 4, - emitSpeed: 2.1, - emitterShouldTrail: true, - isEmitting: 1, - lifespan: SEND_ASSET_PARTICLE_LIFETIME_SECONDS + 1, // Immediately gets updated to be accurate - lifetime: SEND_ASSET_PARTICLE_LIFETIME_SECONDS + 1, - maxParticles: 20, - name: 'asset-particles', - particleRadius: 0.2, - polarFinish: 0, - polarStart: 0, - radiusFinish: 0.05, - radiusSpread: 0, - radiusStart: 0.2, - speedSpread: 0, - textures: "http://hifi-content.s3.amazonaws.com/alan/dev/Particles/Bokeh-Particle-HFC.png", - type: 'ParticleEffect' -}; - -function updateSendAssetParticleEffect() { - var timestampNow = Date.now(); - if ((timestampNow - particleEffectTimestamp) > (SEND_ASSET_PARTICLE_LIFETIME_SECONDS * 1000)) { - deleteSendAssetParticleEffect(); - return; - } else if ((timestampNow - particleEffectTimestamp) > SEND_ASSET_PARTICLE_EMITTING_DURATION) { - Entities.editEntity(sendAssetParticleEffect, { - isEmitting: 0 - }); - } else if (sendAssetParticleEffect) { - var recipientPosition = AvatarList.getAvatar(sendAssetRecipient).position; - var distance = Vec3.distance(recipientPosition, MyAvatar.position); - var accel = Vec3.subtract(recipientPosition, MyAvatar.position); - accel.y -= 3.0; - var life = Math.sqrt(2 * distance / Vec3.length(accel)); - Entities.editEntity(sendAssetParticleEffect, { - emitAcceleration: accel, - lifespan: life - }); - } -} - -function deleteSendAssetParticleEffect() { - if (sendAssetParticleEffectUpdateTimer) { - Script.clearInterval(sendAssetParticleEffectUpdateTimer); - sendAssetParticleEffectUpdateTimer = null; - } - if (sendAssetParticleEffect) { - sendAssetParticleEffect = Entities.deleteEntity(sendAssetParticleEffect); - } - sendAssetRecipient = null; -} var savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview"); var UI_FADE_TIMEOUT_MS = 150; @@ -868,26 +523,17 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { } switch (message.method) { case 'gotoBank': - ui.close(); + ui.close(); if (Account.metaverseServerURL.indexOf("staging") >= 0) { Window.location = "hifi://hifiqa-master-metaverse-staging"; // So that we can test in staging. } else { Window.location = "hifi://BankOfHighFidelity"; } - break; - case 'purchases_openWallet': + break; case 'checkout_openWallet': case 'checkout_setUpClicked': openWallet(); break; - case 'purchases_walletNotSetUp': - wireQmlEventBridge(true); - ui.tablet.sendToQml({ - method: 'updateWalletReferrer', - referrer: "purchases" - }); - openWallet(); - break; case 'checkout_walletNotSetUp': wireQmlEventBridge(true); ui.tablet.sendToQml({ @@ -911,12 +557,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { case 'checkout_continueShopping': openMarketplace(); break; - case 'purchases_itemInfoClicked': - var itemId = message.itemId; - if (itemId && itemId !== "") { - openMarketplace(itemId); - } - break; case 'checkout_rezClicked': case 'purchases_rezClicked': case 'tester_rezClicked': @@ -945,7 +585,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { } break; case 'header_marketplaceImageClicked': - case 'purchases_backClicked': openMarketplace(message.referrerURL); break; case 'purchases_goToMarketplaceClicked': @@ -975,13 +614,9 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { case 'maybeEnableHmdPreview': maybeEnableHMDPreview(); break; - case 'purchases_openGoTo': + case 'checkout_openGoTo': ui.open("hifi/tablet/TabletAddressDialog.qml"); break; - case 'purchases_itemCertificateClicked': - contextOverlayEntity = ""; - setCertificateInfo(contextOverlayEntity, message.itemCertificateId); - break; case 'inspectionCertificate_closeClicked': ui.close(); break; @@ -1000,85 +635,11 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { method: 'purchases_showMyItems' }); break; - case 'refreshConnections': - // Guard to prevent this code from being executed while sending money -- - // we only want to execute this while sending non-HFC gifts - if (!onWalletScreen) { - print('Refreshing Connections...'); - getConnectionData(false); - } - break; - case 'enable_ChooseRecipientNearbyMode': - // Guard to prevent this code from being executed while sending money -- - // we only want to execute this while sending non-HFC gifts - if (!onWalletScreen) { - if (!isUpdateOverlaysWired) { - Script.update.connect(updateOverlays); - isUpdateOverlaysWired = true; - } - } - break; - case 'disable_ChooseRecipientNearbyMode': - // Guard to prevent this code from being executed while sending money -- - // we only want to execute this while sending non-HFC gifts - if (!onWalletScreen) { - if (isUpdateOverlaysWired) { - Script.update.disconnect(updateOverlays); - isUpdateOverlaysWired = false; - } - removeOverlays(); - } - break; - case 'purchases_availableUpdatesReceived': - shouldShowDot = message.numUpdates > 0; - ui.messagesWaiting(shouldShowDot && !ui.isOpen); - break; - case 'purchases_updateWearables': - var currentlyWornWearables = []; - var ATTACHMENT_SEARCH_RADIUS = 100; // meters (just in case) - - var nearbyEntities = Entities.findEntitiesByType('Model', MyAvatar.position, ATTACHMENT_SEARCH_RADIUS); - - for (var i = 0; i < nearbyEntities.length; i++) { - var currentProperties = Entities.getEntityProperties( - nearbyEntities[i], ['certificateID', 'editionNumber', 'parentID'] - ); - if (currentProperties.parentID === MyAvatar.sessionUUID) { - currentlyWornWearables.push({ - entityID: nearbyEntities[i], - entityCertID: currentProperties.certificateID, - entityEdition: currentProperties.editionNumber - }); - } - } - - ui.tablet.sendToQml({ method: 'updateWearables', wornWearables: currentlyWornWearables }); - break; - case 'sendAsset_sendPublicly': - if (message.assetName !== "") { - deleteSendAssetParticleEffect(); - sendAssetRecipient = message.recipient; - var props = SEND_ASSET_PARTICLE_PROPERTIES; - props.parentID = MyAvatar.sessionUUID; - props.position = MyAvatar.position; - props.position.y += 0.2; - if (message.effectImage) { - props.textures = message.effectImage; - } - sendAssetParticleEffect = Entities.addEntity(props, true); - particleEffectTimestamp = Date.now(); - updateSendAssetParticleEffect(); - sendAssetParticleEffectUpdateTimer = Script.setInterval(updateSendAssetParticleEffect, - SEND_ASSET_PARTICLE_TIMER_UPDATE); - } - break; case 'http.request': // Handled elsewhere, don't log. break; - case 'goToPurchases_fromWalletHome': // HRS FIXME What's this about? - break; default: - print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message)); + print('Unrecognized message from Checkout.qml: ' + JSON.stringify(message)); } }; @@ -1163,15 +724,7 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { } if (onCommerceScreen) { - if (!isWired) { - Users.usernameFromIDReply.connect(usernameFromIDReply); - Controller.mousePressEvent.connect(handleMouseEvent); - Controller.mouseMoveEvent.connect(handleMouseMoveEvent); - triggerMapping.enable(); - triggerPressMapping.enable(); - } - isWired = true; - Wallet.refreshWalletStatus(); + WalletScriptingInterface.refreshWalletStatus(); } else { if (onMarketplaceScreen) { onMarketplaceOpen('marketplace cta'); @@ -1193,44 +746,11 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { "\nNew screen URL: " + url + "\nCurrent app open status: " + ui.isOpen + "\n"); }; -function notificationDataProcessPage(data) { - return data.data.updates; -} - -var shouldShowDot = false; -function notificationPollCallback(updatesArray) { - shouldShowDot = shouldShowDot || updatesArray.length > 0; - ui.messagesWaiting(shouldShowDot && !ui.isOpen); - - if (updatesArray.length > 0) { - var message; - if (!ui.notificationInitialCallbackMade) { - message = updatesArray.length + " of your purchased items " + - (updatesArray.length === 1 ? "has an update " : "have updates ") + - "available. Open MARKET to update."; - ui.notificationDisplayBanner(message); - - ui.notificationPollCaresAboutSince = true; - } else { - for (var i = 0; i < updatesArray.length; i++) { - message = "Update available for \"" + - updatesArray[i].base_item_title + "\"." + - "Open MARKET to update."; - ui.notificationDisplayBanner(message); - } - } - } -} - -function isReturnedDataEmpty(data) { - var historyArray = data.data.updates; - return historyArray.length === 0; -} - var BUTTON_NAME = "MARKET"; -var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace"; -var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. +var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace" + (WalletScriptingInterface.limitedCommerce ? "?isFree=1" : ""); +// Append "?" if necessary to signal injected script that it's the initial page. +var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + (MARKETPLACE_URL.indexOf("?") > -1 ? "" : "?"); var ui; function startup() { ui = new AppUi({ @@ -1239,50 +759,26 @@ function startup() { inject: MARKETPLACES_INJECT_SCRIPT_URL, home: MARKETPLACE_URL_INITIAL, onScreenChanged: onTabletScreenChanged, - onMessage: onQmlMessageReceived, - // notificationPollEndpoint: "/api/v1/commerce/available_updates?per_page=10", - // notificationPollTimeoutMs: 300000, - // notificationDataProcessPage: notificationDataProcessPage, - // notificationPollCallback: notificationPollCallback, - // notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - // notificationPollCaresAboutSince: false // Changes to true after first poll + onMessage: onQmlMessageReceived }); ContextOverlay.contextOverlayClicked.connect(openInspectionCertificateQML); Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); GlobalServices.myUsernameChanged.connect(onUsernameChanged); ui.tablet.webEventReceived.connect(onWebEventReceived); - Wallet.walletStatusChanged.connect(sendCommerceSettings); + WalletScriptingInterface.walletStatusChanged.connect(sendCommerceSettings); Window.messageBoxClosed.connect(onMessageBoxClosed); ResourceRequestObserver.resourceRequestEvent.connect(onResourceRequestEvent); - Wallet.refreshWalletStatus(); + WalletScriptingInterface.refreshWalletStatus(); } -var isWired = false; -var isUpdateOverlaysWired = false; function off() { - if (isWired) { - Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Controller.mousePressEvent.disconnect(handleMouseEvent); - Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); - triggerMapping.disable(); - triggerPressMapping.disable(); - - isWired = false; - } - - if (isUpdateOverlaysWired) { - Script.update.disconnect(updateOverlays); - isUpdateOverlaysWired = false; - } - removeOverlays(); } function shutdown() { maybeEnableHMDPreview(); - deleteSendAssetParticleEffect(); Window.messageBoxClosed.disconnect(onMessageBoxClosed); - Wallet.walletStatusChanged.disconnect(sendCommerceSettings); + WalletScriptingInterface.walletStatusChanged.disconnect(sendCommerceSettings); ui.tablet.webEventReceived.disconnect(onWebEventReceived); GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 36fe264274..9558b99310 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -634,7 +634,7 @@ Window.notifyEditError = onEditError; Window.notify = onNotify; Tablet.tabletNotification.connect(tabletNotification); - Wallet.walletNotSetup.connect(walletNotSetup); + WalletScriptingInterface.walletNotSetup.connect(walletNotSetup); Messages.subscribe(NOTIFICATIONS_MESSAGE_CHANNEL); Messages.messageReceived.connect(onMessageReceived); From 91b199a456e61886464d07deb3144f1da2a18394 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 30 Oct 2018 16:29:20 -0700 Subject: [PATCH 045/125] Checkpoint Proofs Filter in Inventory --- .../resources/qml/controls-uit/FilterBar.qml | 70 +++++++++++-------- .../qml/hifi/commerce/purchases/Purchases.qml | 5 ++ interface/src/commerce/Ledger.cpp | 10 ++- interface/src/commerce/Ledger.h | 2 +- interface/src/commerce/QmlCommerce.cpp | 5 +- interface/src/commerce/QmlCommerce.h | 2 +- 6 files changed, 57 insertions(+), 37 deletions(-) diff --git a/interface/resources/qml/controls-uit/FilterBar.qml b/interface/resources/qml/controls-uit/FilterBar.qml index ecae790b22..71aa1f64ab 100644 --- a/interface/resources/qml/controls-uit/FilterBar.qml +++ b/interface/resources/qml/controls-uit/FilterBar.qml @@ -260,38 +260,50 @@ Item { interactive: false; anchors.fill: parent; model: filterBarModel; - delegate: Rectangle { - id: dropDownButton; - color: hifi.colors.white; - width: parent.width; + delegate: Item { + width: parent.width; height: 50; + Rectangle { + id: dropDownButton; + color: hifi.colors.white; + width: parent.width; + height: 50; + visible: true; - RalewaySemiBold { - id: dropDownButtonText; - text: model.displayName; - anchors.fill: parent; - anchors.leftMargin: 12; - color: hifi.colors.baseGray; - horizontalAlignment: Text.AlignLeft; - verticalAlignment: Text.AlignVCenter; - size: 18; + RalewaySemiBold { + id: dropDownButtonText; + text: model.displayName; + anchors.fill: parent; + anchors.topMargin: 2; + anchors.leftMargin: 12; + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + size: 18; + } + + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + propagateComposedEvents: false; + onEntered: { + dropDownButton.color = hifi.colors.blueHighlight; + } + onExited: { + dropDownButton.color = hifi.colors.white; + } + onClicked: { + textField.forceActiveFocus(); + root.primaryFilter_index = index; + dropdownContainer.visible = false; + } + } } - - MouseArea { - anchors.fill: parent; - hoverEnabled: true; - propagateComposedEvents: false; - onEntered: { - dropDownButton.color = hifi.colors.blueHighlight; - } - onExited: { - dropDownButton.color = hifi.colors.white; - } - onClicked: { - textField.forceActiveFocus(); - root.primaryFilter_index = index; - dropdownContainer.visible = false; - } + Rectangle { + height: 2; + width: parent.width; + color: hifi.colors.lightGray; + visible: model.separator } } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 655af79e68..9cbabb769f 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -518,8 +518,13 @@ Rectangle { "filterName": "wearable" }, { + "separator" : true, "displayName": "Updatable", "filterName": "updated" + }, + { + "displayName": "Proofs", + "filterName": "proofs" } ] filterBar.primaryFilterChoices.clear(); diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 67303f2a9b..d206d773de 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -152,10 +152,14 @@ void Ledger::balance(const QStringList& keys) { keysQuery("balance", "balanceSuccess", "balanceFailure"); } -void Ledger::inventory(const QString& editionFilter, const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage) { +void Ledger::inventory(const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage) { QJsonObject params; - params["edition_filter"] = editionFilter; - params["type_filter"] = typeFilter; + if (typeFilter == "proofs") { + params["edition_filter"] = "proofs"; + } else { + params["type_filter"] = typeFilter; + } + params["title_filter"] = titleFilter; params["page"] = page; params["per_page"] = perPage; diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 427395ee11..7bff9abe9b 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -29,7 +29,7 @@ public: bool receiveAt(const QString& hfc_key, const QString& signing_key, const QByteArray& locker); bool receiveAt(); void balance(const QStringList& keys); - void inventory(const QString& editionFilter, const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage); + void inventory(const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage); void history(const QStringList& keys, const int& pageNumber, const int& itemsPerPage); void account(); void updateLocation(const QString& asset_id, const QString& location, const bool& alsoUpdateSiblings = false, const bool controlledFailure = false); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index ffe89ffc5b..369b03d610 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -151,8 +151,7 @@ void QmlCommerce::balance() { } } -void QmlCommerce::inventory(const QString& editionFilter, - const QString& typeFilter, +void QmlCommerce::inventory(const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage) { @@ -160,7 +159,7 @@ void QmlCommerce::inventory(const QString& editionFilter, auto wallet = DependencyManager::get(); QStringList cachedPublicKeys = wallet->listPublicKeys(); if (!cachedPublicKeys.isEmpty()) { - ledger->inventory(editionFilter, typeFilter, titleFilter, page, perPage); + ledger->inventory(typeFilter, titleFilter, page, perPage); } } diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index 2e3c0ec24d..a8eb45261d 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -74,7 +74,7 @@ protected: Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false); Q_INVOKABLE void balance(); - Q_INVOKABLE void inventory(const QString& editionFilter = QString(), const QString& typeFilter = QString(), const QString& titleFilter = QString(), const int& page = 1, const int& perPage = 20); + Q_INVOKABLE void inventory(const QString& typeFilter = QString(), const QString& titleFilter = QString(), const int& page = 1, const int& perPage = 20); Q_INVOKABLE void history(const int& pageNumber, const int& itemsPerPage = 100); Q_INVOKABLE void generateKeyPair(); Q_INVOKABLE void account(); From becee7f010571d3ffd61deeb062d16a73a06e2a1 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 30 Oct 2018 17:28:42 -0700 Subject: [PATCH 046/125] Re-name FBXGeometry to HFMGeometry and do the same for related classes --- .../src/avatars/ScriptableAvatar.cpp | 12 +- interface/src/ModelPackager.cpp | 4 +- interface/src/ModelPackager.h | 6 +- interface/src/ModelPropertiesDialog.cpp | 4 +- interface/src/ModelPropertiesDialog.h | 4 +- interface/src/avatar/MyAvatar.cpp | 14 +- interface/src/avatar/MySkeletonModel.cpp | 2 +- interface/src/raypick/CollisionPick.cpp | 30 ++-- interface/src/ui/overlays/ModelOverlay.cpp | 22 +-- libraries/animation/src/AnimClip.cpp | 18 +-- libraries/animation/src/AnimSkeleton.cpp | 16 +- libraries/animation/src/AnimSkeleton.h | 8 +- libraries/animation/src/AnimationCache.cpp | 18 +-- libraries/animation/src/AnimationCache.h | 12 +- libraries/animation/src/AnimationObject.cpp | 8 +- libraries/animation/src/AnimationObject.h | 4 +- libraries/animation/src/Rig.cpp | 18 +-- libraries/animation/src/Rig.h | 22 +-- .../src/avatars-renderer/Avatar.cpp | 6 +- .../src/avatars-renderer/SkeletonModel.cpp | 16 +- .../src/avatars-renderer/SkeletonModel.h | 4 +- libraries/baking/src/FBXBaker.cpp | 6 +- libraries/baking/src/FBXBaker.h | 2 +- libraries/baking/src/ModelBaker.cpp | 2 +- libraries/baking/src/ModelBaker.h | 2 +- libraries/baking/src/OBJBaker.cpp | 8 +- libraries/baking/src/OBJBaker.h | 4 +- .../src/RenderableModelEntityItem.cpp | 66 ++++---- libraries/fbx/src/FBX.h | 90 +++++------ libraries/fbx/src/FBXReader.cpp | 150 +++++++++--------- libraries/fbx/src/FBXReader.h | 16 +- libraries/fbx/src/FBXReader_Material.cpp | 40 ++--- libraries/fbx/src/FBXReader_Mesh.cpp | 76 ++++----- libraries/fbx/src/GLTFReader.cpp | 62 ++++---- libraries/fbx/src/GLTFReader.h | 10 +- libraries/fbx/src/OBJReader.cpp | 84 +++++----- libraries/fbx/src/OBJReader.h | 12 +- .../src/model-networking/ModelCache.cpp | 54 +++---- .../src/model-networking/ModelCache.h | 14 +- .../render-utils/src/CauterizedModel.cpp | 18 +-- .../render-utils/src/MeshPartPayload.cpp | 4 +- libraries/render-utils/src/Model.cpp | 76 ++++----- libraries/render-utils/src/Model.h | 6 +- .../render-utils/src/SoftAttachmentModel.cpp | 6 +- tests-manual/gpu/src/TestFbx.cpp | 2 +- tests-manual/gpu/src/TestFbx.h | 2 +- .../src/AnimInverseKinematicsTests.cpp | 8 +- tools/skeleton-dump/src/SkeletonDumpApp.cpp | 4 +- tools/vhacd-util/src/VHACDUtil.cpp | 44 ++--- tools/vhacd-util/src/VHACDUtil.h | 12 +- tools/vhacd-util/src/VHACDUtilApp.cpp | 26 +-- tools/vhacd-util/src/VHACDUtilApp.h | 2 +- 52 files changed, 578 insertions(+), 578 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 7d2b267a05..385f94d9f3 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -69,10 +69,10 @@ void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { AvatarData::setSkeletonModelURL(skeletonModelURL); } -static AnimPose composeAnimPose(const FBXJoint& fbxJoint, const glm::quat rotation, const glm::vec3 translation) { +static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, const glm::vec3 translation) { glm::mat4 translationMat = glm::translate(translation); - glm::mat4 rotationMat = glm::mat4_cast(fbxJoint.preRotation * rotation * fbxJoint.postRotation); - glm::mat4 finalMat = translationMat * fbxJoint.preTransform * rotationMat * fbxJoint.postTransform; + glm::mat4 rotationMat = glm::mat4_cast(joint.preRotation * rotation * joint.postRotation); + glm::mat4 finalMat = translationMat * joint.preTransform * rotationMat * joint.postTransform; return AnimPose(finalMat); } @@ -93,7 +93,7 @@ void ScriptableAvatar::update(float deltatime) { } _animationDetails.currentFrame = currentFrame; - const QVector& modelJoints = _bind->getGeometry().joints; + const QVector& modelJoints = _bind->getGeometry().joints; QStringList animationJointNames = _animation->getJointNames(); const int nJoints = modelJoints.size(); @@ -102,8 +102,8 @@ void ScriptableAvatar::update(float deltatime) { } const int frameCount = _animation->getFrames().size(); - const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount); - const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); + const HFMAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount); + const HFMAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); const float frameFraction = glm::fract(currentFrame); std::vector poses = _animSkeleton->getRelativeDefaultPoses(); diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 3a5d92eb8c..0b2846006b 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -235,7 +235,7 @@ bool ModelPackager::zipModel() { return true; } -void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry) { +void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const HFMGeometry& geometry) { bool isBodyType = _modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL; @@ -370,7 +370,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename void ModelPackager::listTextures() { _textures.clear(); - foreach (const FBXMaterial mat, _geometry->materials) { + foreach (const HFMMaterial mat, _geometry->materials) { if (!mat.albedoTexture.filename.isEmpty() && mat.albedoTexture.content.isEmpty() && !_textures.contains(mat.albedoTexture.filename)) { _textures << mat.albedoTexture.filename; diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index 76295e5a85..b68d9e746d 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -19,7 +19,7 @@ #include "ui/ModelsBrowser.h" -class FBXGeometry; +class HFMGeometry; class ModelPackager : public QObject { public: @@ -32,7 +32,7 @@ private: bool editProperties(); bool zipModel(); - void populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry); + void populateBasicMapping(QVariantHash& mapping, QString filename, const HFMGeometry& geometry); void listTextures(); bool copyTextures(const QString& oldDir, const QDir& newDir); @@ -44,7 +44,7 @@ private: QString _scriptDir; QVariantHash _mapping; - std::unique_ptr _geometry; + std::unique_ptr _geometry; QStringList _textures; QStringList _scripts; }; diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index 8984f89d07..dcda85d117 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -27,7 +27,7 @@ ModelPropertiesDialog::ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const FBXGeometry& geometry) : + const QString& basePath, const HFMGeometry& geometry) : _modelType(modelType), _originalMapping(originalMapping), _basePath(basePath), @@ -249,7 +249,7 @@ QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const { if (withNone) { box->addItem("(none)"); } - foreach (const FBXJoint& joint, _geometry.joints) { + foreach (const HFMJoint& joint, _geometry.joints) { if (joint.isSkeletonJoint || !_geometry.hasSkeletonJoints) { box->addItem(joint.name); } diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h index e3c2d8ed6a..d1a020bec3 100644 --- a/interface/src/ModelPropertiesDialog.h +++ b/interface/src/ModelPropertiesDialog.h @@ -30,7 +30,7 @@ class ModelPropertiesDialog : public QDialog { public: ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const FBXGeometry& geometry); + const QString& basePath, const HFMGeometry& geometry); QVariantHash getMapping() const; @@ -50,7 +50,7 @@ private: FSTReader::ModelType _modelType; QVariantHash _originalMapping; QString _basePath; - FBXGeometry _geometry; + HFMGeometry _geometry; QLineEdit* _name = nullptr; QPushButton* _textureDirectory = nullptr; QPushButton* _scriptDirectory = nullptr; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3299bd10e7..b3a66f70b8 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -155,7 +155,7 @@ MyAvatar::MyAvatar(QThread* thread) : }); connect(_skeletonModel.get(), &Model::rigReady, this, [this]() { if (_shouldLoadScripts) { - auto geometry = getSkeletonModel()->getFBXGeometry(); + auto geometry = getSkeletonModel()->getHFMGeometry(); qApp->loadAvatarScripts(geometry.scripts); _shouldLoadScripts = false; } @@ -2429,10 +2429,10 @@ void MyAvatar::attachmentDataToEntityProperties(const AttachmentData& data, Enti void MyAvatar::initHeadBones() { int neckJointIndex = -1; if (_skeletonModel->isLoaded()) { - neckJointIndex = _skeletonModel->getFBXGeometry().neckJointIndex; + neckJointIndex = _skeletonModel->getHFMGeometry().neckJointIndex; } if (neckJointIndex == -1) { - neckJointIndex = (_skeletonModel->getFBXGeometry().headJointIndex - 1); + neckJointIndex = (_skeletonModel->getHFMGeometry().headJointIndex - 1); if (neckJointIndex < 0) { // return if the head is not even there. can't cauterize!! return; @@ -2443,7 +2443,7 @@ void MyAvatar::initHeadBones() { q.push(neckJointIndex); _headBoneSet.insert(neckJointIndex); - // fbxJoints only hold links to parents not children, so we have to do a bit of extra work here. + // hfmJoints only hold links to parents not children, so we have to do a bit of extra work here. while (q.size() > 0) { int jointIndex = q.front(); for (int i = 0; i < _skeletonModel->getJointStateCount(); i++) { @@ -2592,11 +2592,11 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { if (_skeletonModel && _skeletonModel->isLoaded()) { const Rig& rig = _skeletonModel->getRig(); - const FBXGeometry& geometry = _skeletonModel->getFBXGeometry(); + const HFMGeometry& geometry = _skeletonModel->getHFMGeometry(); for (int i = 0; i < rig.getJointStateCount(); i++) { AnimPose jointPose; rig.getAbsoluteJointPoseInRigFrame(i, jointPose); - const FBXJointShapeInfo& shapeInfo = geometry.joints[i].shapeInfo; + const HFMJointShapeInfo& shapeInfo = geometry.joints[i].shapeInfo; const AnimPose pose = rigToWorldPose * jointPose; for (size_t j = 0; j < shapeInfo.debugLines.size() / 2; j++) { glm::vec3 pointA = pose.xformPoint(shapeInfo.debugLines[2 * j]); @@ -4012,7 +4012,7 @@ float MyAvatar::getSitStandStateChange() const { } QVector MyAvatar::getScriptUrls() { - QVector scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector(); + QVector scripts = _skeletonModel->isLoaded() ? _skeletonModel->getHFMGeometry().scripts : QVector(); return scripts; } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index c6aae6124a..3ec40d372b 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -90,7 +90,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { // Called within Model::simulate call, below. void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); Head* head = _owningAvatar->getHead(); diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 25927c5b68..e8a53aa9b6 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -131,7 +131,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // should never fall in here when collision model not fully loaded // TODO: assert that all geometries exist and are loaded //assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); - const FBXGeometry& collisionGeometry = resource->getFBXGeometry(); + const HFMGeometry& collisionGeometry = resource->getHFMGeometry(); ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); @@ -139,15 +139,15 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case. - foreach (const FBXMesh& mesh, collisionGeometry.meshes) { + foreach (const HFMMesh& mesh, collisionGeometry.meshes) { // each meshPart is a convex hull - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { pointCollection.push_back(QVector()); ShapeInfo::PointList& pointsInPart = pointCollection[i]; // run through all the triangles and (uniquely) add each point to the hull uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -168,7 +168,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // run through all the quads and (uniquely) add each point to the hull numIndices = (uint32_t)meshPart.quadIndices.size(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % QUAD_STRIDE == 0); numIndices -= numIndices % QUAD_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -206,7 +206,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // to the visual model and apply them to the collision model (without regard for the // collision model's extents). - glm::vec3 scaleToFit = dimensions / resource->getFBXGeometry().getUnscaledMeshExtents().size(); + glm::vec3 scaleToFit = dimensions / resource->getHFMGeometry().getUnscaledMeshExtents().size(); // multiply each point by scale for (int32_t i = 0; i < pointCollection.size(); i++) { for (int32_t j = 0; j < pointCollection[i].size(); j++) { @@ -216,11 +216,11 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha } shapeInfo.setParams(type, dimensions, resource->getURL().toString()); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { - const FBXGeometry& fbxGeometry = resource->getFBXGeometry(); - int numFbxMeshes = fbxGeometry.meshes.size(); + const HFMGeometry& hfmGeometry = resource->getHFMGeometry(); + int numHFMMeshes = hfmGeometry.meshes.size(); int totalNumVertices = 0; - for (int i = 0; i < numFbxMeshes; i++) { - const FBXMesh& mesh = fbxGeometry.meshes.at(i); + for (int i = 0; i < numHFMMeshes; i++) { + const HFMMesh& mesh = hfmGeometry.meshes.at(i); totalNumVertices += mesh.vertices.size(); } const int32_t MAX_VERTICES_PER_STATIC_MESH = 1e6; @@ -230,7 +230,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha return; } - auto& meshes = resource->getFBXGeometry().meshes; + auto& meshes = resource->getHFMGeometry().meshes; int32_t numMeshes = (int32_t)(meshes.size()); const int MAX_ALLOWED_MESH_COUNT = 1000; @@ -285,12 +285,12 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha if (type == SHAPE_TYPE_STATIC_MESH) { // copy into triangleIndices size_t triangleIndicesCount = 0; - for (const FBXMeshPart& meshPart : mesh.parts) { + for (const HFMMeshPart& meshPart : mesh.parts) { triangleIndicesCount += meshPart.triangleIndices.count(); } triangleIndices.reserve((int)triangleIndicesCount); - for (const FBXMeshPart& meshPart : mesh.parts) { + for (const HFMMeshPart& meshPart : mesh.parts) { const int* indexItr = meshPart.triangleIndices.cbegin(); while (indexItr != meshPart.triangleIndices.cend()) { triangleIndices.push_back(*indexItr); @@ -299,11 +299,11 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha } } else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { // for each mesh copy unique part indices, separated by special bogus (flag) index values - for (const FBXMeshPart& meshPart : mesh.parts) { + for (const HFMMeshPart& meshPart : mesh.parts) { // collect unique list of indices for this part std::set uniqueIndices; auto numIndices = meshPart.triangleIndices.count(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices% TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index eee8222051..1b66ff08ad 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -446,7 +446,7 @@ QVariant ModelOverlay::getProperty(const QString& property) { if (property == "jointNames") { if (_model && _model->isActive()) { - // note: going through Rig because Model::getJointNames() (which proxies to FBXGeometry) was always empty + // note: going through Rig because Model::getJointNames() (which proxies to HFMGeometry) was always empty const Rig* rig = &(_model->getRig()); return mapJoints([rig](int jointIndex) -> QString { return rig->nameOfJoint(jointIndex); @@ -574,7 +574,7 @@ void ModelOverlay::animate() { QVector jointsData; - const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy + const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy int frameCount = frames.size(); if (frameCount <= 0) { return; @@ -606,10 +606,10 @@ void ModelOverlay::animate() { } QStringList animationJointNames = _animation->getGeometry().getJointNames(); - auto& fbxJoints = _animation->getGeometry().joints; + auto& hfmJoints = _animation->getGeometry().joints; - auto& originalFbxJoints = _model->getFBXGeometry().joints; - auto& originalFbxIndices = _model->getFBXGeometry().jointIndices; + auto& originalHFMJoints = _model->getHFMGeometry().joints; + auto& originalFbxIndices = _model->getHFMGeometry().jointIndices; const QVector& rotations = frames[_lastKnownCurrentFrame].rotations; const QVector& translations = frames[_lastKnownCurrentFrame].translations; @@ -626,23 +626,23 @@ void ModelOverlay::animate() { translationMat = glm::translate(translations[index]); } } else if (index < animationJointNames.size()) { - QString jointName = fbxJoints[index].name; + QString jointName = hfmJoints[index].name; if (originalFbxIndices.contains(jointName)) { // Making sure the joint names exist in the original model the animation is trying to apply onto. If they do, then remap and get its translation. int remappedIndex = originalFbxIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. - translationMat = glm::translate(originalFbxJoints[remappedIndex].translation); + translationMat = glm::translate(originalHFMJoints[remappedIndex].translation); } } glm::mat4 rotationMat; if (index < rotations.size()) { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * rotations[index] * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * rotations[index] * hfmJoints[index].postRotation); } else { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * hfmJoints[index].postRotation); } - glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform * - rotationMat * fbxJoints[index].postTransform); + glm::mat4 finalMat = (translationMat * hfmJoints[index].preTransform * + rotationMat * hfmJoints[index].postTransform); auto& jointData = jointsData[j]; jointData.translation = extractTranslation(finalMat); jointData.translationIsDefaultPose = false; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index f9195a608b..d630218165 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -101,7 +101,7 @@ void AnimClip::copyFromNetworkAnim() { // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. - const FBXGeometry& geom = _networkAnim->getGeometry(); + const HFMGeometry& geom = _networkAnim->getGeometry(); AnimSkeleton animSkeleton(geom); const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); @@ -120,7 +120,7 @@ void AnimClip::copyFromNetworkAnim() { for (int frame = 0; frame < frameCount; frame++) { - const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; + const HFMAnimationFrame& hfmAnimFrame = geom.animationFrames[frame]; // init all joints in animation to default pose // this will give us a resonable result for bones in the model skeleton but not in the animation. @@ -132,8 +132,8 @@ void AnimClip::copyFromNetworkAnim() { for (int animJoint = 0; animJoint < animJointCount; animJoint++) { int skeletonJoint = jointMap[animJoint]; - const glm::vec3& fbxAnimTrans = fbxAnimFrame.translations[animJoint]; - const glm::quat& fbxAnimRot = fbxAnimFrame.rotations[animJoint]; + const glm::vec3& hfmAnimTrans = hfmAnimFrame.translations[animJoint]; + const glm::quat& hfmAnimRot = hfmAnimFrame.rotations[animJoint]; // skip joints that are in the animation but not in the skeleton. if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { @@ -146,19 +146,19 @@ void AnimClip::copyFromNetworkAnim() { preRot.scale() = glm::vec3(1.0f); postRot.scale() = glm::vec3(1.0f); - AnimPose rot(glm::vec3(1.0f), fbxAnimRot, glm::vec3()); + AnimPose rot(glm::vec3(1.0f), hfmAnimRot, glm::vec3()); // adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. - const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint]; + const glm::vec3& hfmZeroTrans = geom.animationFrames[0].translations[animJoint]; const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); float boneLengthScale = 1.0f; const float EPSILON = 0.0001f; - if (fabsf(glm::length(fbxZeroTrans)) > EPSILON) { - boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(fbxZeroTrans); + if (fabsf(glm::length(hfmZeroTrans)) > EPSILON) { + boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(hfmZeroTrans); } - AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (fbxAnimTrans - fbxZeroTrans)); + AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (hfmAnimTrans - hfmZeroTrans)); _anim[frame][skeletonJoint] = trans * preRot * rot * postRot; } diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index bed9c590be..fc4114ac7b 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -16,17 +16,17 @@ #include "AnimationLogging.h" -AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { +AnimSkeleton::AnimSkeleton(const HFMGeometry& geometry) { // convert to std::vector of joints - std::vector joints; - joints.reserve(fbxGeometry.joints.size()); - for (auto& joint : fbxGeometry.joints) { + std::vector joints; + joints.reserve(geometry.joints.size()); + for (auto& joint : geometry.joints) { joints.push_back(joint); } buildSkeletonFromJoints(joints); } -AnimSkeleton::AnimSkeleton(const std::vector& joints) { +AnimSkeleton::AnimSkeleton(const std::vector& joints) { buildSkeletonFromJoints(joints); } @@ -166,7 +166,7 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { } } -void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { +void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { _joints = joints; _jointsSize = (int)joints.size(); // build a cache of bind poses @@ -177,7 +177,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) _relativePreRotationPoses.reserve(_jointsSize); _relativePostRotationPoses.reserve(_jointsSize); - // iterate over FBXJoints and extract the bind pose information. + // iterate over HFMJoints and extract the bind pose information. for (int i = 0; i < _jointsSize; i++) { // build pre and post transforms @@ -240,7 +240,7 @@ void AnimSkeleton::dump(bool verbose) const { qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i); qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i); if (verbose) { - qCDebug(animation) << " fbxJoint ="; + qCDebug(animation) << " hfmJoint ="; qCDebug(animation) << " isFree =" << _joints[i].isFree; qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage; qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex; diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 2ebf3f4f5d..1717d75985 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -23,8 +23,8 @@ public: using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; - explicit AnimSkeleton(const FBXGeometry& fbxGeometry); - explicit AnimSkeleton(const std::vector& joints); + explicit AnimSkeleton(const HFMGeometry& geometry); + explicit AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; const QString& getJointName(int jointIndex) const; int getNumJoints() const; @@ -64,9 +64,9 @@ public: std::vector lookUpJointIndices(const std::vector& jointNames) const; protected: - void buildSkeletonFromJoints(const std::vector& joints); + void buildSkeletonFromJoints(const std::vector& joints); - std::vector _joints; + std::vector _joints; int _jointsSize { 0 }; AnimPoseVec _relativeDefaultPoses; AnimPoseVec _absoluteDefaultPoses; diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index 04b7952ddb..b5b27c3a24 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -69,7 +69,7 @@ void AnimationReader::run() { if (urlValid) { // Parse the FBX directly from the QNetworkReply - FBXGeometry::Pointer fbxgeo; + HFMGeometry::Pointer fbxgeo; if (_url.path().toLower().endsWith(".fbx")) { fbxgeo.reset(readFBX(_data, QVariantHash(), _url.path())); } else { @@ -100,40 +100,40 @@ QStringList Animation::getJointNames() const { } QStringList names; if (_geometry) { - foreach (const FBXJoint& joint, _geometry->joints) { + foreach (const HFMJoint& joint, _geometry->joints) { names.append(joint.name); } } return names; } -QVector Animation::getFrames() const { +QVector Animation::getFrames() const { if (QThread::currentThread() != thread()) { - QVector result; + QVector result; BLOCKING_INVOKE_METHOD(const_cast(this), "getFrames", - Q_RETURN_ARG(QVector, result)); + Q_RETURN_ARG(QVector, result)); return result; } if (_geometry) { return _geometry->animationFrames; } else { - return QVector(); + return QVector(); } } -const QVector& Animation::getFramesReference() const { +const QVector& Animation::getFramesReference() const { return _geometry->animationFrames; } void Animation::downloadFinished(const QByteArray& data) { // parse the animation/fbx file on a background thread. AnimationReader* animationReader = new AnimationReader(_url, data); - connect(animationReader, SIGNAL(onSuccess(FBXGeometry::Pointer)), SLOT(animationParseSuccess(FBXGeometry::Pointer))); + connect(animationReader, SIGNAL(onSuccess(HFMGeometry::Pointer)), SLOT(animationParseSuccess(HFMGeometry::Pointer))); connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString))); QThreadPool::globalInstance()->start(animationReader); } -void Animation::animationParseSuccess(FBXGeometry::Pointer geometry) { +void Animation::animationParseSuccess(HFMGeometry::Pointer geometry) { qCDebug(animation) << "Animation parse success" << _url.toDisplayString(); diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index 483350e2b5..302f23a4e7 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -66,7 +66,7 @@ public: QString getType() const override { return "Animation"; } - const FBXGeometry& getGeometry() const { return *_geometry; } + const HFMGeometry& getGeometry() const { return *_geometry; } virtual bool isLoaded() const override; @@ -80,20 +80,20 @@ public: * @function AnimationObject.getFrames * @returns {FBXAnimationFrame[]} */ - Q_INVOKABLE QVector getFrames() const; + Q_INVOKABLE QVector getFrames() const; - const QVector& getFramesReference() const; + const QVector& getFramesReference() const; protected: virtual void downloadFinished(const QByteArray& data) override; protected slots: - void animationParseSuccess(FBXGeometry::Pointer geometry); + void animationParseSuccess(HFMGeometry::Pointer geometry); void animationParseError(int error, QString str); private: - FBXGeometry::Pointer _geometry; + HFMGeometry::Pointer _geometry; }; /// Reads geometry in a worker thread. @@ -105,7 +105,7 @@ public: virtual void run() override; signals: - void onSuccess(FBXGeometry::Pointer geometry); + void onSuccess(HFMGeometry::Pointer geometry); void onError(int error, QString str); private: diff --git a/libraries/animation/src/AnimationObject.cpp b/libraries/animation/src/AnimationObject.cpp index 7f0f35b104..bcbf497199 100644 --- a/libraries/animation/src/AnimationObject.cpp +++ b/libraries/animation/src/AnimationObject.cpp @@ -19,17 +19,17 @@ QStringList AnimationObject::getJointNames() const { return qscriptvalue_cast(thisObject())->getJointNames(); } -QVector AnimationObject::getFrames() const { +QVector AnimationObject::getFrames() const { return qscriptvalue_cast(thisObject())->getFrames(); } QVector AnimationFrameObject::getRotations() const { - return qscriptvalue_cast(thisObject()).rotations; + return qscriptvalue_cast(thisObject()).rotations; } void registerAnimationTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType >(engine); - engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( + qScriptRegisterSequenceMetaType >(engine); + engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( new AnimationFrameObject(), QScriptEngine::ScriptOwnership)); engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( new AnimationObject(), QScriptEngine::ScriptOwnership)); diff --git a/libraries/animation/src/AnimationObject.h b/libraries/animation/src/AnimationObject.h index aa69e78ceb..83880ed2ab 100644 --- a/libraries/animation/src/AnimationObject.h +++ b/libraries/animation/src/AnimationObject.h @@ -23,13 +23,13 @@ class QScriptEngine; class AnimationObject : public QObject, protected QScriptable { Q_OBJECT Q_PROPERTY(QStringList jointNames READ getJointNames) - Q_PROPERTY(QVector frames READ getFrames) + Q_PROPERTY(QVector frames READ getFrames) public: Q_INVOKABLE QStringList getJointNames() const; - Q_INVOKABLE QVector getFrames() const; + Q_INVOKABLE QVector getFrames() const; }; /// Scriptable wrapper for animation frames. diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 33b9569758..2641be92da 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -260,7 +260,7 @@ void Rig::destroyAnimGraph() { _rightEyeJointChildren.clear(); } -void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset) { +void Rig::initJointStates(const HFMGeometry& geometry, const glm::mat4& modelOffset) { _geometryOffset = AnimPose(geometry.offset); _invGeometryOffset = _geometryOffset.inverse(); _geometryToRigTransform = modelOffset * geometry.offset; @@ -307,7 +307,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.rightEyeJointIndex); } -void Rig::reset(const FBXGeometry& geometry) { +void Rig::reset(const HFMGeometry& geometry) { _geometryOffset = AnimPose(geometry.offset); _invGeometryOffset = _geometryOffset.inverse(); _animSkeleton = std::make_shared(geometry); @@ -1253,7 +1253,7 @@ const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = { // returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose. // if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward // such that it lies on the surface of the kdop. -static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const FBXJointShapeInfo& shapeInfo, glm::vec3& displacementOut) { +static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) { // transform point into local space of jointShape. glm::vec3 localPoint = shapePose.inverse().xformPoint(point); @@ -1299,8 +1299,8 @@ static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& sh } } -glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) const { +glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const { glm::vec3 position = handPosition; glm::vec3 displacement; int hipsJoint = indexOfJoint("Hips"); @@ -1349,8 +1349,8 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJoin void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated, bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt, const AnimPose& leftHandPose, const AnimPose& rightHandPose, - const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo, + const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo, const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) { const bool ENABLE_POLE_VECTORS = true; @@ -2008,7 +2008,7 @@ void Rig::computeExternalPoses(const glm::mat4& modelOffsetMat) { } void Rig::computeAvatarBoundingCapsule( - const FBXGeometry& geometry, + const HFMGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& localOffsetOut) const { @@ -2041,7 +2041,7 @@ void Rig::computeAvatarBoundingCapsule( // from the head to the hips when computing the rest of the bounding capsule. int index = indexOfJoint("Head"); while (index != -1) { - const FBXJointShapeInfo& shapeInfo = geometry.joints.at(index).shapeInfo; + const HFMJointShapeInfo& shapeInfo = geometry.joints.at(index).shapeInfo; AnimPose pose = _animSkeleton->getAbsoluteDefaultPose(index); if (shapeInfo.points.size() > 0) { for (auto& point : shapeInfo.points) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 7a090bd7bd..61e8672972 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -86,10 +86,10 @@ public: AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space uint8_t secondaryControllerFlags[NumSecondaryControllerTypes]; bool isTalking; - FBXJointShapeInfo hipsShapeInfo; - FBXJointShapeInfo spineShapeInfo; - FBXJointShapeInfo spine1ShapeInfo; - FBXJointShapeInfo spine2ShapeInfo; + HFMJointShapeInfo hipsShapeInfo; + HFMJointShapeInfo spineShapeInfo; + HFMJointShapeInfo spine1ShapeInfo; + HFMJointShapeInfo spine2ShapeInfo; }; struct EyeParameters { @@ -122,8 +122,8 @@ public: void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreRoleAnimation(const QString& role); - void initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset); - void reset(const FBXGeometry& geometry); + void initJointStates(const HFMGeometry& geometry, const glm::mat4& modelOffset); + void reset(const HFMGeometry& geometry); bool jointStatesEmpty(); int getJointStateCount() const; int indexOfJoint(const QString& jointName) const; @@ -210,7 +210,7 @@ public: void copyJointsFromJointData(const QVector& jointDataVec); void computeExternalPoses(const glm::mat4& modelOffsetMat); - void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; + void computeAvatarBoundingCapsule(const HFMGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; void setEnableInverseKinematics(bool enable); void setEnableAnimations(bool enable); @@ -245,8 +245,8 @@ protected: void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated, bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt, const AnimPose& leftHandPose, const AnimPose& rightHandPose, - const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo, + const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo, const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix); void updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose, @@ -257,8 +257,8 @@ protected: bool calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int oppositeArmIndex, glm::vec3& poleVector) const; glm::vec3 calculateKneePoleVector(int footJointIndex, int kneeJoint, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const; - glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) const; + glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const; AnimPose _modelOffset; // model to rig space diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 86f5bd69a5..b8448e389d 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1311,7 +1311,7 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { case CAMERA_MATRIX_INDEX: { glm::quat rotation; if (_skeletonModel && _skeletonModel->isActive()) { - int headJointIndex = _skeletonModel->getFBXGeometry().headJointIndex; + int headJointIndex = _skeletonModel->getHFMGeometry().headJointIndex; if (headJointIndex >= 0) { _skeletonModel->getAbsoluteJointRotationInRigFrame(headJointIndex, rotation); } @@ -1360,7 +1360,7 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { case CAMERA_MATRIX_INDEX: { glm::vec3 translation; if (_skeletonModel && _skeletonModel->isActive()) { - int headJointIndex = _skeletonModel->getFBXGeometry().headJointIndex; + int headJointIndex = _skeletonModel->getHFMGeometry().headJointIndex; if (headJointIndex >= 0) { _skeletonModel->getAbsoluteJointTranslationInRigFrame(headJointIndex, translation); } @@ -1416,7 +1416,7 @@ void Avatar::withValidJointIndicesCache(std::function const& worker) con if (!_modelJointsCached) { _modelJointIndicesCache.clear(); if (_skeletonModel && _skeletonModel->isActive()) { - _modelJointIndicesCache = _skeletonModel->getFBXGeometry().jointIndices; + _modelJointIndicesCache = _skeletonModel->getHFMGeometry().jointIndices; _modelJointsCached = true; } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 1ec58fd704..a41cff528b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -54,7 +54,7 @@ void SkeletonModel::setTextures(const QVariantMap& textures) { } void SkeletonModel::initJointStates() { - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); _rig.initJointStates(geometry, modelOffset); @@ -96,7 +96,7 @@ void SkeletonModel::initJointStates() { // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { assert(!_owningAvatar->isMyAvatar()); - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); Head* head = _owningAvatar->getHead(); @@ -259,22 +259,22 @@ bool SkeletonModel::getRightShoulderPosition(glm::vec3& position) const { } bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const { - return isActive() && getJointPositionInWorldFrame(getFBXGeometry().headJointIndex, headPosition); + return isActive() && getJointPositionInWorldFrame(getHFMGeometry().headJointIndex, headPosition); } bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPositionInWorldFrame(getFBXGeometry().neckJointIndex, neckPosition); + return isActive() && getJointPositionInWorldFrame(getHFMGeometry().neckJointIndex, neckPosition); } bool SkeletonModel::getLocalNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPosition(getFBXGeometry().neckJointIndex, neckPosition); + return isActive() && getJointPosition(getHFMGeometry().neckJointIndex, neckPosition); } bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { return false; } - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); if (getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) && getJointPosition(geometry.rightEyeJointIndex, secondEyePosition)) { @@ -330,7 +330,7 @@ void SkeletonModel::computeBoundingShape() { return; } - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) { // rootJointIndex == -1 if the avatar model has no skeleton return; @@ -369,7 +369,7 @@ void SkeletonModel::renderBoundingCollisionShapes(RenderArgs* args, gpu::Batch& } bool SkeletonModel::hasSkeleton() { - return isActive() ? getFBXGeometry().rootJointIndex != -1 : false; + return isActive() ? getHFMGeometry().rootJointIndex != -1 : false; } void SkeletonModel::onInvalidate() { diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index d82fce7412..6c533a5941 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -41,10 +41,10 @@ public: void updateAttitude(const glm::quat& orientation); /// Returns the index of the left hand joint, or -1 if not found. - int getLeftHandJointIndex() const { return isActive() ? getFBXGeometry().leftHandJointIndex : -1; } + int getLeftHandJointIndex() const { return isActive() ? getHFMGeometry().leftHandJointIndex : -1; } /// Returns the index of the right hand joint, or -1 if not found. - int getRightHandJointIndex() const { return isActive() ? getFBXGeometry().rightHandJointIndex : -1; } + int getRightHandJointIndex() const { return isActive() ? getHFMGeometry().rightHandJointIndex : -1; } bool getLeftGrabPosition(glm::vec3& position) const; bool getRightGrabPosition(glm::vec3& position) const; diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index b90082d969..0b76c275d4 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -206,7 +206,7 @@ void FBXBaker::importScene() { } #endif - _geometry = reader.extractFBXGeometry({}, _modelURL.toString()); + _geometry = reader.extractHFMGeometry({}, _modelURL.toString()); _textureContentMap = reader._textureContent; } @@ -329,7 +329,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { for (FBXNode& textureChild : object->children) { if (textureChild.name == "RelativeFilename") { - QString fbxTextureFileName { textureChild.properties.at(0).toString() }; + QString hfmTextureFileName { textureChild.properties.at(0).toString() }; // grab the ID for this texture so we can figure out the // texture type from the loaded materials @@ -337,7 +337,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { auto textureType = textureTypes[textureID]; // Compress the texture information and return the new filename to be added into the FBX scene - auto bakedTextureFile = compressTexture(fbxTextureFileName, textureType); + auto bakedTextureFile = compressTexture(hfmTextureFileName, textureType); // If no errors or warnings have occurred during texture compression add the filename to the FBX scene if (!bakedTextureFile.isNull()) { diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 9d41209d4c..8edaf91c79 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -53,7 +53,7 @@ private: void rewriteAndBakeSceneModels(); void rewriteAndBakeSceneTextures(); - FBXGeometry* _geometry; + HFMGeometry* _geometry; QHash _textureNameMatchCount; QHash _remappedTexturePaths; diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index 75e10c54ab..ca352cebae 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -75,7 +75,7 @@ void ModelBaker::abort() { } } -bool ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback) { +bool ModelBaker::compressMesh(HFMMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback) { if (mesh.wasCompressed) { handleError("Cannot re-bake a file that contains compressed mesh"); return false; diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h index 1fd77ab761..cda4478b1d 100644 --- a/libraries/baking/src/ModelBaker.h +++ b/libraries/baking/src/ModelBaker.h @@ -39,7 +39,7 @@ public: const QString& bakedOutputDirectory, const QString& originalOutputDirectory = ""); virtual ~ModelBaker(); - bool compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback = nullptr); + bool compressMesh(HFMMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback = nullptr); QString compressTexture(QString textureFileName, image::TextureUsage::Type = image::TextureUsage::Type::DEFAULT_TEXTURE); virtual void setWasAborted(bool wasAborted) override; diff --git a/libraries/baking/src/OBJBaker.cpp b/libraries/baking/src/OBJBaker.cpp index cf62bc4fa8..e9130e3fbd 100644 --- a/libraries/baking/src/OBJBaker.cpp +++ b/libraries/baking/src/OBJBaker.cpp @@ -153,7 +153,7 @@ void OBJBaker::bakeOBJ() { checkIfTexturesFinished(); } -void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { +void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMGeometry& geometry) { // Generating FBX Header Node FBXNode headerNode; headerNode.name = FBX_HEADER_EXTENSION; @@ -235,7 +235,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { auto size = meshParts.size(); for (int i = 0; i < size; i++) { QString material = meshParts[i].materialID; - FBXMaterial currentMaterial = geometry.materials[material]; + HFMMaterial currentMaterial = geometry.materials[material]; if (!currentMaterial.albedoTexture.filename.isEmpty() || !currentMaterial.specularTexture.filename.isEmpty()) { auto textureID = nextNodeID(); _mapTextureMaterial.emplace_back(textureID, i); @@ -325,12 +325,12 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { } // Set properties for material nodes -void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry) { +void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMGeometry& geometry) { auto materialID = nextNodeID(); _materialIDs.push_back(materialID); materialNode.properties = { materialID, material, MESH }; - FBXMaterial currentMaterial = geometry.materials[material]; + HFMMaterial currentMaterial = geometry.materials[material]; // Setting the hierarchy: Material -> Properties70 -> P -> Properties FBXNode properties70Node; diff --git a/libraries/baking/src/OBJBaker.h b/libraries/baking/src/OBJBaker.h index 8e49692d35..875a500129 100644 --- a/libraries/baking/src/OBJBaker.h +++ b/libraries/baking/src/OBJBaker.h @@ -39,8 +39,8 @@ private slots: private: void loadOBJ(); - void createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry); - void setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry); + void createFBXNodeTree(FBXNode& rootNode, HFMGeometry& geometry); + void setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMGeometry& geometry); NodeID nextNodeID() { return _nodeID++; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index c6337dc872..c36f60600f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -268,7 +268,7 @@ EntityItemProperties RenderableModelEntityItem::getProperties(const EntityProper if (model->isLoaded()) { // TODO: improve naturalDimensions in the future, // for now we've added this hack for setting natural dimensions of models - Extents meshExtents = model->getFBXGeometry().getUnscaledMeshExtents(); + Extents meshExtents = model->getHFMGeometry().getUnscaledMeshExtents(); properties.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); properties.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); } @@ -403,7 +403,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // should never fall in here when collision model not fully loaded // TODO: assert that all geometries exist and are loaded //assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); - const FBXGeometry& collisionGeometry = _compoundShapeResource->getFBXGeometry(); + const HFMGeometry& collisionGeometry = _compoundShapeResource->getHFMGeometry(); ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); @@ -411,15 +411,15 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case. - foreach (const FBXMesh& mesh, collisionGeometry.meshes) { + foreach (const HFMMesh& mesh, collisionGeometry.meshes) { // each meshPart is a convex hull - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { pointCollection.push_back(QVector()); ShapeInfo::PointList& pointsInPart = pointCollection[i]; // run through all the triangles and (uniquely) add each point to the hull uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -440,7 +440,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // run through all the quads and (uniquely) add each point to the hull numIndices = (uint32_t)meshPart.quadIndices.size(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % QUAD_STRIDE == 0); numIndices -= numIndices % QUAD_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -478,7 +478,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // to the visual model and apply them to the collision model (without regard for the // collision model's extents). - glm::vec3 scaleToFit = dimensions / model->getFBXGeometry().getUnscaledMeshExtents().size(); + glm::vec3 scaleToFit = dimensions / model->getHFMGeometry().getUnscaledMeshExtents().size(); // multiply each point by scale before handing the point-set off to the physics engine. // also determine the extents of the collision model. glm::vec3 registrationOffset = dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()); @@ -498,14 +498,14 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // compute meshPart local transforms QVector localTransforms; - const FBXGeometry& fbxGeometry = model->getFBXGeometry(); - int numFbxMeshes = fbxGeometry.meshes.size(); + const HFMGeometry& hfmGeometry = model->getHFMGeometry(); + int numHFMMeshes = hfmGeometry.meshes.size(); int totalNumVertices = 0; glm::mat4 invRegistraionOffset = glm::translate(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); - for (int i = 0; i < numFbxMeshes; i++) { - const FBXMesh& mesh = fbxGeometry.meshes.at(i); + for (int i = 0; i < numHFMMeshes; i++) { + const HFMMesh& mesh = hfmGeometry.meshes.at(i); if (mesh.clusters.size() > 0) { - const FBXCluster& cluster = mesh.clusters.at(0); + const HFMCluster& cluster = mesh.clusters.at(0); auto jointMatrix = model->getRig().getJointTransform(cluster.jointIndex); // we backtranslate by the registration offset so we can apply that offset to the shapeInfo later localTransforms.push_back(invRegistraionOffset * jointMatrix * cluster.inverseBindMatrix); @@ -524,10 +524,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { std::vector> meshes; if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { - auto& fbxMeshes = _compoundShapeResource->getFBXGeometry().meshes; - meshes.reserve(fbxMeshes.size()); - for (auto& fbxMesh : fbxMeshes) { - meshes.push_back(fbxMesh._mesh); + auto& hfmMeshes = _compoundShapeResource->getHFMGeometry().meshes; + meshes.reserve(hfmMeshes.size()); + for (auto& hfmMesh : hfmMeshes) { + meshes.push_back(hfmMesh._mesh); } } else { meshes = model->getGeometry()->getMeshes(); @@ -594,7 +594,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { while (partItr != parts.cend()) { auto numIndices = partItr->_numIndices; if (partItr->_topology == graphics::Mesh::TRIANGLES) { - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -605,7 +605,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { ++indexItr; } } else if (partItr->_topology == graphics::Mesh::TRIANGLE_STRIP) { - // TODO: resurrect assert after we start sanitizing FBXMesh higher up + // TODO: resurrect assert after we start sanitizing HFMMesh higher up //assert(numIndices > 2); uint32_t approxNumIndices = TRIANGLE_STRIDE * numIndices; @@ -651,7 +651,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { std::set uniqueIndices; auto numIndices = partItr->_numIndices; if (partItr->_topology == graphics::Mesh::TRIANGLES) { - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices% TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -662,7 +662,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { ++indexItr; } } else if (partItr->_topology == graphics::Mesh::TRIANGLE_STRIP) { - // TODO: resurrect assert after we start sanitizing FBXMesh higher up + // TODO: resurrect assert after we start sanitizing HFMMesh higher up //assert(numIndices > TRIANGLE_STRIDE - 1); auto indexItr = indices.cbegin() + partItr->_startIndex; @@ -755,7 +755,7 @@ int RenderableModelEntityItem::avatarJointIndex(int modelJointIndex) { bool RenderableModelEntityItem::contains(const glm::vec3& point) const { auto model = getModel(); if (EntityItem::contains(point) && model && _compoundShapeResource && _compoundShapeResource->isLoaded()) { - return _compoundShapeResource->getFBXGeometry().convexHullContains(worldToEntity(point)); + return _compoundShapeResource->getHFMGeometry().convexHullContains(worldToEntity(point)); } return false; @@ -1135,7 +1135,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { QVector jointsData; - const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy + const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy int frameCount = frames.size(); if (frameCount <= 0) { return; @@ -1160,10 +1160,10 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { } QStringList animationJointNames = _animation->getGeometry().getJointNames(); - auto& fbxJoints = _animation->getGeometry().joints; + auto& hfmJoints = _animation->getGeometry().joints; - auto& originalFbxJoints = _model->getFBXGeometry().joints; - auto& originalFbxIndices = _model->getFBXGeometry().jointIndices; + auto& originalHFMJoints = _model->getHFMGeometry().joints; + auto& originalHFMIndices = _model->getHFMGeometry().jointIndices; bool allowTranslation = entity->getAnimationAllowTranslation(); @@ -1182,22 +1182,22 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { translationMat = glm::translate(translations[index]); } } else if (index < animationJointNames.size()) { - QString jointName = fbxJoints[index].name; // Pushing this here so its not done on every entity, with the exceptions of those allowing for translation - if (originalFbxIndices.contains(jointName)) { + QString jointName = hfmJoints[index].name; // Pushing this here so its not done on every entity, with the exceptions of those allowing for translation + if (originalHFMIndices.contains(jointName)) { // Making sure the joint names exist in the original model the animation is trying to apply onto. If they do, then remap and get it's translation. - int remappedIndex = originalFbxIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. - translationMat = glm::translate(originalFbxJoints[remappedIndex].translation); + int remappedIndex = originalHFMIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. + translationMat = glm::translate(originalHFMJoints[remappedIndex].translation); } } glm::mat4 rotationMat; if (index < rotations.size()) { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * rotations[index] * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * rotations[index] * hfmJoints[index].postRotation); } else { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * hfmJoints[index].postRotation); } - glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform * - rotationMat * fbxJoints[index].postTransform); + glm::mat4 finalMat = (translationMat * hfmJoints[index].preTransform * + rotationMat * hfmJoints[index].postTransform); auto& jointData = jointsData[j]; jointData.translation = extractTranslation(finalMat); jointData.translationSet = true; diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index fdebb16bc8..8051dbafea 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -70,8 +70,8 @@ public: }; -/// A single blendshape extracted from an FBX document. -class FBXBlendshape { +/// A single blendshape. +class HFMBlendshape { public: QVector indices; QVector vertices; @@ -79,19 +79,19 @@ public: QVector tangents; }; -struct FBXJointShapeInfo { - // same units and frame as FBXJoint.translation +struct HFMJointShapeInfo { + // same units and frame as HFMJoint.translation glm::vec3 avgPoint; std::vector dots; std::vector points; std::vector debugLines; }; -/// A single joint (transformation node) extracted from an FBX document. -class FBXJoint { +/// A single joint (transformation node). +class HFMJoint { public: - FBXJointShapeInfo shapeInfo; + HFMJointShapeInfo shapeInfo; QVector freeLineage; bool isFree; int parentIndex; @@ -126,8 +126,8 @@ public: }; -/// A single binding to a joint in an FBX document. -class FBXCluster { +/// A single binding to a joint. +class HFMCluster { public: int jointIndex; @@ -137,8 +137,8 @@ public: const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; -/// A texture map in an FBX document. -class FBXTexture { +/// A texture map. +class HFMTexture { public: QString id; QString name; @@ -156,7 +156,7 @@ public: }; /// A single part of a mesh (with the same material). -class FBXMeshPart { +class HFMMeshPart { public: QVector quadIndices; // original indices from the FBX mesh @@ -166,10 +166,10 @@ public: QString materialID; }; -class FBXMaterial { +class HFMMaterial { public: - FBXMaterial() {}; - FBXMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, + HFMMaterial() {}; + HFMMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, float shininess, float opacity) : diffuseColor(diffuseColor), specularColor(specularColor), @@ -203,17 +203,17 @@ public: QString shadingModel; graphics::MaterialPointer _material; - FBXTexture normalTexture; - FBXTexture albedoTexture; - FBXTexture opacityTexture; - FBXTexture glossTexture; - FBXTexture roughnessTexture; - FBXTexture specularTexture; - FBXTexture metallicTexture; - FBXTexture emissiveTexture; - FBXTexture occlusionTexture; - FBXTexture scatteringTexture; - FBXTexture lightmapTexture; + HFMTexture normalTexture; + HFMTexture albedoTexture; + HFMTexture opacityTexture; + HFMTexture glossTexture; + HFMTexture roughnessTexture; + HFMTexture specularTexture; + HFMTexture metallicTexture; + HFMTexture emissiveTexture; + HFMTexture occlusionTexture; + HFMTexture scatteringTexture; + HFMTexture lightmapTexture; glm::vec2 lightmapParams{ 0.0f, 1.0f }; @@ -232,10 +232,10 @@ public: }; /// A single mesh (with optional blendshapes) extracted from an FBX document. -class FBXMesh { +class HFMMesh { public: - QVector parts; + QVector parts; QVector vertices; QVector normals; @@ -247,12 +247,12 @@ public: QVector clusterWeights; QVector originalIndices; - QVector clusters; + QVector clusters; Extents meshExtents; glm::mat4 modelTransform; - QVector blendshapes; + QVector blendshapes; unsigned int meshIndex; // the order the meshes appeared in the object file @@ -265,7 +265,7 @@ public: class ExtractedMesh { public: - FBXMesh mesh; + HFMMesh mesh; QMultiHash newIndices; QVector > blendshapeIndexMaps; QVector > partMaterialTextures; @@ -278,14 +278,14 @@ public: * @property {Vec3[]} translations */ /// A single animation frame extracted from an FBX document. -class FBXAnimationFrame { +class HFMAnimationFrame { public: QVector rotations; QVector translations; }; -/// A light in an FBX document. -class FBXLight { +/// A light. +class HFMLight { public: QString name; Transform transform; @@ -293,7 +293,7 @@ public: float fogValue; glm::vec3 color; - FBXLight() : + HFMLight() : name(), transform(), intensity(1.0f), @@ -302,26 +302,26 @@ public: {} }; -Q_DECLARE_METATYPE(FBXAnimationFrame) -Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(HFMAnimationFrame) +Q_DECLARE_METATYPE(QVector) /// A set of meshes extracted from an FBX document. -class FBXGeometry { +class HFMGeometry { public: - using Pointer = std::shared_ptr; + using Pointer = std::shared_ptr; QString originalURL; QString author; QString applicationName; ///< the name of the application that generated the model - QVector joints; + QVector joints; QHash jointIndices; ///< 1-based, so as to more easily detect missing indices bool hasSkeletonJoints; - QVector meshes; + QVector meshes; QVector scripts; - QHash materials; + QHash materials; glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file @@ -348,7 +348,7 @@ public: Extents bindExtents; Extents meshExtents; - QVector animationFrames; + QVector animationFrames; int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; @@ -368,7 +368,7 @@ public: QList blendshapeChannelNames; }; -Q_DECLARE_METATYPE(FBXGeometry) -Q_DECLARE_METATYPE(FBXGeometry::Pointer) +Q_DECLARE_METATYPE(HFMGeometry) +Q_DECLARE_METATYPE(HFMGeometry::Pointer) #endif // hifi_FBX_h_ diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index dd766f002c..df6abbfdf2 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -40,19 +40,19 @@ using namespace std; -int FBXGeometryPointerMetaTypeId = qRegisterMetaType(); +int HFMGeometryPointerMetaTypeId = qRegisterMetaType(); -QStringList FBXGeometry::getJointNames() const { +QStringList HFMGeometry::getJointNames() const { QStringList names; - foreach (const FBXJoint& joint, joints) { + foreach (const HFMJoint& joint, joints) { names.append(joint.name); } return names; } -bool FBXGeometry::hasBlendedMeshes() const { +bool HFMGeometry::hasBlendedMeshes() const { if (!meshes.isEmpty()) { - foreach (const FBXMesh& mesh, meshes) { + foreach (const HFMMesh& mesh, meshes) { if (!mesh.blendshapes.isEmpty()) { return true; } @@ -61,7 +61,7 @@ bool FBXGeometry::hasBlendedMeshes() const { return false; } -Extents FBXGeometry::getUnscaledMeshExtents() const { +Extents HFMGeometry::getUnscaledMeshExtents() const { const Extents& extents = meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which @@ -74,12 +74,12 @@ Extents FBXGeometry::getUnscaledMeshExtents() const { } // TODO: Move to graphics::Mesh when Sam's ready -bool FBXGeometry::convexHullContains(const glm::vec3& point) const { +bool HFMGeometry::convexHullContains(const glm::vec3& point) const { if (!getUnscaledMeshExtents().containsPoint(point)) { return false; } - auto checkEachPrimitive = [=](FBXMesh& mesh, QVector indices, int primitiveSize) -> bool { + auto checkEachPrimitive = [=](HFMMesh& mesh, QVector indices, int primitiveSize) -> bool { // Check whether the point is "behind" all the primitives. int verticesSize = mesh.vertices.size(); for (int j = 0; @@ -124,16 +124,16 @@ bool FBXGeometry::convexHullContains(const glm::vec3& point) const { return false; } -QString FBXGeometry::getModelNameOfMesh(int meshIndex) const { +QString HFMGeometry::getModelNameOfMesh(int meshIndex) const { if (meshIndicesToModelNames.contains(meshIndex)) { return meshIndicesToModelNames.value(meshIndex); } return QString(); } -int fbxGeometryMetaTypeId = qRegisterMetaType(); -int fbxAnimationFrameMetaTypeId = qRegisterMetaType(); -int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType >(); +int hfmGeometryMetaTypeId = qRegisterMetaType(); +int hfmAnimationFrameMetaTypeId = qRegisterMetaType(); +int hfmAnimationFrameVectorMetaTypeId = qRegisterMetaType >(); glm::vec3 parseVec3(const QString& string) { @@ -303,7 +303,7 @@ glm::mat4 getGlobalTransform(const QMultiMap& _connectionParen class ExtractedBlendshape { public: QString id; - FBXBlendshape blendshape; + HFMBlendshape blendshape; }; void printNode(const FBXNode& node, int indentLevel) { @@ -346,8 +346,8 @@ void appendModelIDs(const QString& parentID, const QMultiMap& } } -FBXBlendshape extractBlendshape(const FBXNode& object) { - FBXBlendshape blendshape; +HFMBlendshape extractBlendshape(const FBXNode& object) { + HFMBlendshape blendshape; foreach (const FBXNode& data, object.children) { if (data.name == "Indexes") { blendshape.indices = FBXReader::getIntVector(data); @@ -362,9 +362,9 @@ FBXBlendshape extractBlendshape(const FBXNode& object) { return blendshape; } -using IndexAccessor = std::function; +using IndexAccessor = std::function; -static void setTangents(const FBXMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex, +static void setTangents(const HFMMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex, const QVector& vertices, const QVector& normals, QVector& tangents) { glm::vec3 vertex[2]; glm::vec3 normal; @@ -381,14 +381,14 @@ static void setTangents(const FBXMesh& mesh, const IndexAccessor& vertexAccessor } } -static void createTangents(const FBXMesh& mesh, bool generateFromTexCoords, +static void createTangents(const HFMMesh& mesh, bool generateFromTexCoords, const QVector& vertices, const QVector& normals, QVector& tangents, IndexAccessor accessor) { // if we have a normal map (and texture coordinates), we must compute tangents if (generateFromTexCoords && !mesh.texCoords.isEmpty()) { tangents.resize(vertices.size()); - foreach(const FBXMeshPart& part, mesh.parts) { + foreach(const HFMMeshPart& part, mesh.parts) { for (int i = 0; i < part.quadIndices.size(); i += 4) { setTangents(mesh, accessor, part.quadIndices.at(i), part.quadIndices.at(i + 1), vertices, normals, tangents); setTangents(mesh, accessor, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2), vertices, normals, tangents); @@ -403,27 +403,27 @@ static void createTangents(const FBXMesh& mesh, bool generateFromTexCoords, setTangents(mesh, accessor, part.triangleIndices.at(i + 2), part.triangleIndices.at(i), vertices, normals, tangents); } if ((part.triangleIndices.size() % 3) != 0) { - qCDebug(modelformat) << "Error in extractFBXGeometry part.triangleIndices.size() is not divisible by three "; + qCDebug(modelformat) << "Error in extractHFMGeometry part.triangleIndices.size() is not divisible by three "; } } } } -static void _createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape); +static void _createBlendShapeTangents(HFMMesh& mesh, bool generateFromTexCoords, HFMBlendshape& blendShape); -void FBXMesh::createBlendShapeTangents(bool generateTangents) { +void HFMMesh::createBlendShapeTangents(bool generateTangents) { for (auto& blendShape : blendshapes) { _createBlendShapeTangents(*this, generateTangents, blendShape); } } -void FBXMesh::createMeshTangents(bool generateFromTexCoords) { - FBXMesh& mesh = *this; +void HFMMesh::createMeshTangents(bool generateFromTexCoords) { + HFMMesh& mesh = *this; // This is the only workaround I've found to trick the compiler into understanding that mesh.tangents isn't // const in the lambda function. auto& tangents = mesh.tangents; createTangents(mesh, generateFromTexCoords, mesh.vertices, mesh.normals, mesh.tangents, - [&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { + [&](const HFMMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { outVertices[0] = mesh.vertices[firstIndex]; outVertices[1] = mesh.vertices[secondIndex]; outNormal = mesh.normals[firstIndex]; @@ -431,7 +431,7 @@ void FBXMesh::createMeshTangents(bool generateFromTexCoords) { }); } -static void _createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape) { +static void _createBlendShapeTangents(HFMMesh& mesh, bool generateFromTexCoords, HFMBlendshape& blendShape) { // Create lookup to get index in blend shape from vertex index in mesh std::vector reverseIndices; reverseIndices.resize(mesh.vertices.size()); @@ -443,7 +443,7 @@ static void _createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, } createTangents(mesh, generateFromTexCoords, blendShape.vertices, blendShape.normals, blendShape.tangents, - [&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { + [&](const HFMMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { const auto index1 = reverseIndices[firstIndex]; const auto index2 = reverseIndices[secondIndex]; @@ -481,7 +481,7 @@ void addBlendshapes(const ExtractedBlendshape& extracted, const QList& blendshapeIndexMap = extractedMesh.blendshapeIndexMaps[index.first]; for (int i = 0; i < extracted.blendshape.indices.size(); i++) { int oldIndex = extracted.blendshape.indices.at(i); @@ -539,7 +539,7 @@ public: QVector values; }; -bool checkMaterialsHaveTextures(const QHash& materials, +bool checkMaterialsHaveTextures(const QHash& materials, const QHash& textureFilenames, const QMultiMap& _connectionChildMap) { foreach (const QString& materialID, materials.keys()) { foreach (const QString& childID, _connectionChildMap.values(materialID)) { @@ -569,8 +569,8 @@ int matchTextureUVSetToAttributeChannel(const QString& texUVSetName, const QHash } -FBXLight extractLight(const FBXNode& object) { - FBXLight light; +HFMLight extractLight(const FBXNode& object) { + HFMLight light; foreach (const FBXNode& subobject, object.children) { QString childname = QString(subobject.name); if (subobject.name == "Properties70") { @@ -615,7 +615,7 @@ QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) { return filepath.mid(filepath.lastIndexOf('/') + 1); } -FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) { +HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QString& url) { const FBXNode& node = _rootNode; QMap meshes; QHash modelIDsToNames; @@ -636,7 +636,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QHash yComponents; QHash zComponents; - std::map lights; + std::map lights; QVariantHash joints = mapping.value("joint").toHash(); QString jointEyeLeftName = processID(getString(joints.value("jointEyeLeft", "jointEyeLeft"))); @@ -689,8 +689,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS #if defined(DEBUG_FBXREADER) int unknown = 0; #endif - FBXGeometry* geometryPtr = new FBXGeometry; - FBXGeometry& geometry = *geometryPtr; + HFMGeometry* geometryPtr = new HFMGeometry; + HFMGeometry& geometry = *geometryPtr; geometry.originalURL = url; @@ -944,7 +944,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS lightprop = vprop.toString(); } - FBXLight light = extractLight(object); + HFMLight light = extractLight(object); } } } else { @@ -1102,7 +1102,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS _textureContent.insert(filepath, content); } } else if (object.name == "Material") { - FBXMaterial material; + HFMMaterial material; material.name = (object.properties.at(1).toString()); foreach (const FBXNode& subobject, object.children) { bool properties = false; @@ -1255,7 +1255,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS #endif } material.materialID = getID(object.properties); - _fbxMaterials.insert(material.materialID, material); + _hfmMaterials.insert(material.materialID, material); } else if (object.name == "NodeAttribute") { @@ -1276,7 +1276,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (!attributetype.isEmpty()) { if (attributetype == "Light") { - FBXLight light = extractLight(object); + HFMLight light = extractLight(object); lights[attribID] = light; } } @@ -1345,7 +1345,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QString parentID = getID(connection.properties, 2); ooChildToParent.insert(childID, parentID); if (!hifiGlobalNodeID.isEmpty() && (parentID == hifiGlobalNodeID)) { - std::map< QString, FBXLight >::iterator lightIt = lights.find(childID); + std::map< QString, HFMLight >::iterator lightIt = lights.find(childID); if (lightIt != lights.end()) { _lightmapLevel = (*lightIt).second.intensity; if (_lightmapLevel <= 0.0f) { @@ -1504,7 +1504,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS frameCount = qMax(frameCount, curve.values.size()); } for (int i = 0; i < frameCount; i++) { - FBXAnimationFrame frame; + HFMAnimationFrame frame; frame.rotations.resize(modelIDs.size()); frame.translations.resize(modelIDs.size()); geometry.animationFrames.append(frame); @@ -1515,7 +1515,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS geometry.hasSkeletonJoints = false; foreach (const QString& modelID, modelIDs) { const FBXModel& model = models[modelID]; - FBXJoint joint; + HFMJoint joint; joint.isFree = freeJoints.contains(model.name); joint.parentIndex = model.parentIndex; @@ -1553,7 +1553,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS joint.distanceToParent = 0.0f; } else { - const FBXJoint& parentJoint = geometry.joints.at(joint.parentIndex); + const HFMJoint& parentJoint = geometry.joints.at(joint.parentIndex); joint.transform = parentJoint.transform * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation) * parentJoint.inverseDefaultRotation; @@ -1631,7 +1631,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS geometry.meshExtents.reset(); // Create the Material Library - consolidateFBXMaterials(mapping); + consolidateHFMMaterials(mapping); // We can't allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image // Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key @@ -1643,7 +1643,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // 33 - 128 textures --> 512 // etc... QSet uniqueTextures; - for (auto& material : _fbxMaterials) { + for (auto& material : _hfmMaterials) { material.getTextureNames(uniqueTextures); } int numTextures = uniqueTextures.size(); @@ -1659,15 +1659,15 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } while (numTextureThreshold < numTextures && maxWidth > MIN_MIP_TEXTURE_WIDTH); qCDebug(modelformat) << "Capped square texture width =" << maxWidth << "for model" << url << "with" << numTextures << "textures"; - for (auto& material : _fbxMaterials) { + for (auto& material : _hfmMaterials) { material.setMaxNumPixelsPerTexture(maxWidth * maxWidth); } } #endif - geometry.materials = _fbxMaterials; + geometry.materials = _hfmMaterials; // see if any materials have texture children - bool materialsHaveTextures = checkMaterialsHaveTextures(_fbxMaterials, _textureFilenames, _connectionChildMap); + bool materialsHaveTextures = checkMaterialsHaveTextures(_hfmMaterials, _textureFilenames, _connectionChildMap); for (QMap::iterator it = meshes.begin(); it != meshes.end(); it++) { ExtractedMesh& extracted = it.value(); @@ -1698,13 +1698,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS for (int i = children.size() - 1; i >= 0; i--) { const QString& childID = children.at(i); - if (_fbxMaterials.contains(childID)) { + if (_hfmMaterials.contains(childID)) { // the pure material associated with this part - FBXMaterial material = _fbxMaterials.value(childID); + HFMMaterial material = _hfmMaterials.value(childID); for (int j = 0; j < extracted.partMaterialTextures.size(); j++) { if (extracted.partMaterialTextures.at(j).first == materialIndex) { - FBXMeshPart& part = extracted.mesh.parts[j]; + HFMMeshPart& part = extracted.mesh.parts[j]; part.materialID = material.materialID; generateTangents |= material.needTangentSpace(); } @@ -1713,7 +1713,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS materialIndex++; } else if (_textureFilenames.contains(childID)) { - FBXTexture texture = getTexture(childID); + HFMTexture texture = getTexture(childID); for (int j = 0; j < extracted.partMaterialTextures.size(); j++) { int partTexture = extracted.partMaterialTextures.at(j).second; if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) { @@ -1736,34 +1736,34 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (!clusters.contains(clusterID)) { continue; } - FBXCluster fbxCluster; + HFMCluster hfmCluster; const Cluster& cluster = clusters[clusterID]; clusterIDs.append(clusterID); // see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion // of skinning information in FBX QString jointID = _connectionChildMap.value(clusterID); - fbxCluster.jointIndex = modelIDs.indexOf(jointID); - if (fbxCluster.jointIndex == -1) { + hfmCluster.jointIndex = modelIDs.indexOf(jointID); + if (hfmCluster.jointIndex == -1) { qCDebug(modelformat) << "Joint not in model list: " << jointID; - fbxCluster.jointIndex = 0; + hfmCluster.jointIndex = 0; } - fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; + hfmCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; // slam bottom row to (0, 0, 0, 1), we KNOW this is not a perspective matrix and // sometimes floating point fuzz can be introduced after the inverse. - fbxCluster.inverseBindMatrix[0][3] = 0.0f; - fbxCluster.inverseBindMatrix[1][3] = 0.0f; - fbxCluster.inverseBindMatrix[2][3] = 0.0f; - fbxCluster.inverseBindMatrix[3][3] = 1.0f; + hfmCluster.inverseBindMatrix[0][3] = 0.0f; + hfmCluster.inverseBindMatrix[1][3] = 0.0f; + hfmCluster.inverseBindMatrix[2][3] = 0.0f; + hfmCluster.inverseBindMatrix[3][3] = 1.0f; - fbxCluster.inverseBindTransform = Transform(fbxCluster.inverseBindMatrix); + hfmCluster.inverseBindTransform = Transform(hfmCluster.inverseBindMatrix); - extracted.mesh.clusters.append(fbxCluster); + extracted.mesh.clusters.append(hfmCluster); // override the bind rotation with the transform link - FBXJoint& joint = geometry.joints[fbxCluster.jointIndex]; + HFMJoint& joint = geometry.joints[hfmCluster.jointIndex]; joint.inverseBindRotation = glm::inverse(extractRotation(cluster.transformLink)); joint.bindTransform = cluster.transformLink; joint.bindTransformFoundInCluster = true; @@ -1776,7 +1776,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // if we don't have a skinned joint, parent to the model itself if (extracted.mesh.clusters.isEmpty()) { - FBXCluster cluster; + HFMCluster cluster; cluster.jointIndex = modelIDs.indexOf(modelID); if (cluster.jointIndex == -1) { qCDebug(modelformat) << "Model not in model list: " << modelID; @@ -1786,7 +1786,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } // whether we're skinned depends on how many clusters are attached - const FBXCluster& firstFBXCluster = extracted.mesh.clusters.at(0); + const HFMCluster& firstHFMCluster = extracted.mesh.clusters.at(0); glm::mat4 inverseModelTransform = glm::inverse(modelTransform); if (clusterIDs.size() > 1) { // this is a multi-mesh joint @@ -1799,9 +1799,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS for (int i = 0; i < clusterIDs.size(); i++) { QString clusterID = clusterIDs.at(i); const Cluster& cluster = clusters[clusterID]; - const FBXCluster& fbxCluster = extracted.mesh.clusters.at(i); - int jointIndex = fbxCluster.jointIndex; - FBXJoint& joint = geometry.joints[jointIndex]; + const HFMCluster& hfmCluster = extracted.mesh.clusters.at(i); + int jointIndex = hfmCluster.jointIndex; + HFMJoint& joint = geometry.joints[jointIndex]; glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform; glm::vec3 boneEnd = extractTranslation(transformJointToMesh); glm::vec3 boneBegin = boneEnd; @@ -1881,8 +1881,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } else { // this is a single-mesh joint - int jointIndex = firstFBXCluster.jointIndex; - FBXJoint& joint = geometry.joints[jointIndex]; + int jointIndex = firstHFMCluster.jointIndex; + HFMJoint& joint = geometry.joints[jointIndex]; // transform cluster vertices to joint-frame and save for later glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform; @@ -1924,7 +1924,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // now that all joints have been scanned compute a k-Dop bounding volume of mesh for (int i = 0; i < geometry.joints.size(); ++i) { - FBXJoint& joint = geometry.joints[i]; + HFMJoint& joint = geometry.joints[i]; // NOTE: points are in joint-frame ShapeVertices& points = shapeVertices.at(i); @@ -1994,13 +1994,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS return geometryPtr; } -FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +HFMGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); return readFBX(&buffer, mapping, url, loadLightmaps, lightmapLevel); } -FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +HFMGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { FBXReader reader; reader._rootNode = FBXReader::parseFBX(device); reader._loadLightmaps = loadLightmaps; @@ -2008,5 +2008,5 @@ FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QStri qCDebug(modelformat) << "Reading FBX: " << url; - return reader.extractFBXGeometry(mapping, url); + return reader.extractHFMGeometry(mapping, url); } diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index c391ea6647..f95ba7fe73 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -36,11 +36,11 @@ class FBXNode; /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +HFMGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +HFMGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); class TextureParam { public: @@ -103,20 +103,20 @@ class ExtractedMesh; class FBXReader { public: - FBXGeometry* _fbxGeometry; + HFMGeometry* _hfmGeometry; FBXNode _rootNode; static FBXNode parseFBX(QIODevice* device); - FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url); + HFMGeometry* extractHFMGeometry(const QVariantHash& mapping, const QString& url); static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate = true); QHash meshes; - static void buildModelMesh(FBXMesh& extractedMesh, const QString& url); + static void buildModelMesh(HFMMesh& extractedMesh, const QString& url); static glm::vec3 normalizeDirForPacking(const glm::vec3& dir); - FBXTexture getTexture(const QString& textureID); + HFMTexture getTexture(const QString& textureID); QHash _textureNames; // Hashes the original RelativeFilename of textures @@ -142,9 +142,9 @@ public: QHash ambientFactorTextures; QHash occlusionTextures; - QHash _fbxMaterials; + QHash _hfmMaterials; - void consolidateFBXMaterials(const QVariantHash& mapping); + void consolidateHFMMaterials(const QVariantHash& mapping); bool _loadLightmaps = true; float _lightmapOffset = 0.0f; diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp index d5902962e5..ff1de30b97 100644 --- a/libraries/fbx/src/FBXReader_Material.cpp +++ b/libraries/fbx/src/FBXReader_Material.cpp @@ -27,7 +27,7 @@ #include "ModelFormatLogging.h" -void FBXMaterial::getTextureNames(QSet& textureList) const { +void HFMMaterial::getTextureNames(QSet& textureList) const { if (!normalTexture.isNull()) { textureList.insert(normalTexture.name); } @@ -63,7 +63,7 @@ void FBXMaterial::getTextureNames(QSet& textureList) const { } } -void FBXMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) { +void HFMMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) { normalTexture.maxNumPixels = maxNumPixels; albedoTexture.maxNumPixels = maxNumPixels; opacityTexture.maxNumPixels = maxNumPixels; @@ -77,12 +77,12 @@ void FBXMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) { lightmapTexture.maxNumPixels = maxNumPixels; } -bool FBXMaterial::needTangentSpace() const { +bool HFMMaterial::needTangentSpace() const { return !normalTexture.isNull(); } -FBXTexture FBXReader::getTexture(const QString& textureID) { - FBXTexture texture; +HFMTexture FBXReader::getTexture(const QString& textureID) { + HFMTexture texture; const QByteArray& filepath = _textureFilepaths.value(textureID); texture.content = _textureContent.value(filepath); @@ -123,7 +123,7 @@ FBXTexture FBXReader::getTexture(const QString& textureID) { return texture; } -void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { +void FBXReader::consolidateHFMMaterials(const QVariantHash& mapping) { QString materialMapString = mapping.value("materialMap").toString(); QJsonDocument materialMapDocument = QJsonDocument::fromJson(materialMapString.toUtf8()); @@ -133,16 +133,16 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { qCDebug(modelformat) << "fbx Material Map found but did not produce valid JSON:" << materialMapString; } } - for (QHash::iterator it = _fbxMaterials.begin(); it != _fbxMaterials.end(); it++) { - FBXMaterial& material = (*it); + for (QHash::iterator it = _hfmMaterials.begin(); it != _hfmMaterials.end(); it++) { + HFMMaterial& material = (*it); // Maya is the exporting the shading model and we are trying to use it bool isMaterialLambert = (material.shadingModel.toLower() == "lambert"); // the pure material associated with this part bool detectDifferentUVs = false; - FBXTexture diffuseTexture; - FBXTexture diffuseFactorTexture; + HFMTexture diffuseTexture; + HFMTexture diffuseFactorTexture; QString diffuseTextureID = diffuseTextures.value(material.materialID); QString diffuseFactorTextureID = diffuseFactorTextures.value(material.materialID); @@ -169,7 +169,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity()); } - FBXTexture transparentTexture; + HFMTexture transparentTexture; QString transparentTextureID = transparentTextures.value(material.materialID); // If PBS Material, systematically bind the albedo texture as transparency texture and check for the alpha channel if (material.isPBSMaterial) { @@ -181,7 +181,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (transparentTexture.texcoordSet != 0) || (!transparentTexture.transform.isIdentity()); } - FBXTexture normalTexture; + HFMTexture normalTexture; QString bumpTextureID = bumpTextures.value(material.materialID); QString normalTextureID = normalTextures.value(material.materialID); if (!normalTextureID.isNull()) { @@ -198,7 +198,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (normalTexture.texcoordSet != 0) || (!normalTexture.transform.isIdentity()); } - FBXTexture specularTexture; + HFMTexture specularTexture; QString specularTextureID = specularTextures.value(material.materialID); if (!specularTextureID.isNull()) { specularTexture = getTexture(specularTextureID); @@ -206,7 +206,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { material.specularTexture = specularTexture; } - FBXTexture metallicTexture; + HFMTexture metallicTexture; QString metallicTextureID = metallicTextures.value(material.materialID); if (!metallicTextureID.isNull()) { metallicTexture = getTexture(metallicTextureID); @@ -214,7 +214,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { material.metallicTexture = metallicTexture; } - FBXTexture roughnessTexture; + HFMTexture roughnessTexture; QString roughnessTextureID = roughnessTextures.value(material.materialID); if (!roughnessTextureID.isNull()) { roughnessTexture = getTexture(roughnessTextureID); @@ -222,7 +222,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (roughnessTexture.texcoordSet != 0) || (!roughnessTexture.transform.isIdentity()); } - FBXTexture shininessTexture; + HFMTexture shininessTexture; QString shininessTextureID = shininessTextures.value(material.materialID); if (!shininessTextureID.isNull()) { shininessTexture = getTexture(shininessTextureID); @@ -230,7 +230,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (shininessTexture.texcoordSet != 0) || (!shininessTexture.transform.isIdentity()); } - FBXTexture emissiveTexture; + HFMTexture emissiveTexture; QString emissiveTextureID = emissiveTextures.value(material.materialID); if (!emissiveTextureID.isNull()) { emissiveTexture = getTexture(emissiveTextureID); @@ -245,7 +245,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { } } - FBXTexture occlusionTexture; + HFMTexture occlusionTexture; QString occlusionTextureID = occlusionTextures.value(material.materialID); if (occlusionTextureID.isNull()) { // 2nd chance @@ -265,7 +265,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { lightmapParams.x = _lightmapOffset; lightmapParams.y = _lightmapLevel; - FBXTexture ambientTexture; + HFMTexture ambientTexture; QString ambientTextureID = ambientTextures.value(material.materialID); if (ambientTextureID.isNull()) { // 2nd chance @@ -326,7 +326,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { if (materialOptions.contains("scatteringMap")) { QByteArray scatteringMap = materialOptions.value("scatteringMap").toVariant().toByteArray(); - material.scatteringTexture = FBXTexture(); + material.scatteringTexture = HFMTexture(); material.scatteringTexture.name = material.name + ".scatteringMap"; material.scatteringTexture.filename = scatteringMap; } diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index c9b004c3a8..e098aff99a 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -42,9 +42,9 @@ using vec2h = glm::tvec2; -#define FBX_PACK_COLORS 1 +#define HFM_PACK_COLORS 1 -#if FBX_PACK_COLORS +#if HFM_PACK_COLORS using ColorType = glm::uint32; #define FBX_COLOR_ELEMENT gpu::Element::COLOR_RGBA_32 #else @@ -469,7 +469,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn QPair materialTexture(materialID, 0); - // grab or setup the FBXMeshPart for the part this face belongs to + // grab or setup the HFMMeshPart for the part this face belongs to int& partIndexPlusOne = materialTextureParts[materialTexture]; if (partIndexPlusOne == 0) { data.extracted.partMaterialTextures.append(materialTexture); @@ -478,7 +478,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn } // give the mesh part this index - FBXMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; + HFMMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; part.triangleIndices.append(firstCorner.value()); part.triangleIndices.append(dracoFace[1].value()); part.triangleIndices.append(dracoFace[2].value()); @@ -511,7 +511,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); partIndex = data.extracted.mesh.parts.size(); } - FBXMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; + HFMMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; if (endIndex - beginIndex == 4) { appendIndex(data, part.quadIndices, beginIndex++, deduplicate); @@ -565,9 +565,9 @@ glm::vec3 FBXReader::normalizeDirForPacking(const glm::vec3& dir) { return dir; } -void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { +void FBXReader::buildModelMesh(HFMMesh& extractedMesh, const QString& url) { unsigned int totalSourceIndices = 0; - foreach(const FBXMeshPart& part, extractedMesh.parts) { + foreach(const HFMMeshPart& part, extractedMesh.parts) { totalSourceIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size()); } @@ -583,17 +583,17 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { return; } - FBXMesh& fbxMesh = extractedMesh; + HFMMesh& hfmMesh = extractedMesh; graphics::MeshPointer mesh(new graphics::Mesh()); int numVerts = extractedMesh.vertices.size(); - if (!fbxMesh.normals.empty() && fbxMesh.tangents.empty()) { + if (!hfmMesh.normals.empty() && hfmMesh.tangents.empty()) { // Fill with a dummy value to force tangents to be present if there are normals - fbxMesh.tangents.reserve(fbxMesh.normals.size()); - std::fill_n(std::back_inserter(fbxMesh.tangents), fbxMesh.normals.size(), Vectors::UNIT_X); + hfmMesh.tangents.reserve(hfmMesh.normals.size()); + std::fill_n(std::back_inserter(hfmMesh.tangents), hfmMesh.normals.size(), Vectors::UNIT_X); } // Same thing with blend shapes - for (auto& blendShape : fbxMesh.blendshapes) { + for (auto& blendShape : hfmMesh.blendshapes) { if (!blendShape.normals.empty() && blendShape.tangents.empty()) { // Fill with a dummy value to force tangents to be present if there are normals blendShape.tangents.reserve(blendShape.normals.size()); @@ -609,8 +609,8 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Normal and tangent are always there together packed in normalized xyz32bits word (times 2) const auto normalElement = FBX_NORMAL_ELEMENT; - const int normalsSize = fbxMesh.normals.size() * normalElement.getSize(); - const int tangentsSize = fbxMesh.tangents.size() * normalElement.getSize(); + const int normalsSize = hfmMesh.normals.size() * normalElement.getSize(); + const int tangentsSize = hfmMesh.tangents.size() * normalElement.getSize(); // If there are normals then there should be tangents assert(normalsSize <= tangentsSize); if (tangentsSize > normalsSize) { @@ -620,22 +620,22 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Color attrib const auto colorElement = FBX_COLOR_ELEMENT; - const int colorsSize = fbxMesh.colors.size() * colorElement.getSize(); + const int colorsSize = hfmMesh.colors.size() * colorElement.getSize(); // Texture coordinates are stored in 2 half floats const auto texCoordsElement = gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV); - const int texCoordsSize = fbxMesh.texCoords.size() * texCoordsElement.getSize(); - const int texCoords1Size = fbxMesh.texCoords1.size() * texCoordsElement.getSize(); + const int texCoordsSize = hfmMesh.texCoords.size() * texCoordsElement.getSize(); + const int texCoords1Size = hfmMesh.texCoords1.size() * texCoordsElement.getSize(); // Support for 4 skinning clusters: // 4 Indices are uint8 ideally, uint16 if more than 256. - const auto clusterIndiceElement = (fbxMesh.clusters.size() < UINT8_MAX ? gpu::Element(gpu::VEC4, gpu::UINT8, gpu::XYZW) : gpu::Element(gpu::VEC4, gpu::UINT16, gpu::XYZW)); + const auto clusterIndiceElement = (hfmMesh.clusters.size() < UINT8_MAX ? gpu::Element(gpu::VEC4, gpu::UINT8, gpu::XYZW) : gpu::Element(gpu::VEC4, gpu::UINT16, gpu::XYZW)); // 4 Weights are normalized 16bits const auto clusterWeightElement = gpu::Element(gpu::VEC4, gpu::NUINT16, gpu::XYZW); // Cluster indices and weights must be the same sizes const int NUM_CLUSTERS_PER_VERT = 4; - const int numVertClusters = (fbxMesh.clusterIndices.size() == fbxMesh.clusterWeights.size() ? fbxMesh.clusterIndices.size() / NUM_CLUSTERS_PER_VERT : 0); + const int numVertClusters = (hfmMesh.clusterIndices.size() == hfmMesh.clusterWeights.size() ? hfmMesh.clusterIndices.size() / NUM_CLUSTERS_PER_VERT : 0); const int clusterIndicesSize = numVertClusters * clusterIndiceElement.getSize(); const int clusterWeightsSize = numVertClusters * clusterWeightElement.getSize(); @@ -660,9 +660,9 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { if (normalsSize > 0) { std::vector normalsAndTangents; - normalsAndTangents.reserve(fbxMesh.normals.size() + fbxMesh.tangents.size()); - for (auto normalIt = fbxMesh.normals.constBegin(), tangentIt = fbxMesh.tangents.constBegin(); - normalIt != fbxMesh.normals.constEnd(); + normalsAndTangents.reserve(hfmMesh.normals.size() + hfmMesh.tangents.size()); + for (auto normalIt = hfmMesh.normals.constBegin(), tangentIt = hfmMesh.tangents.constBegin(); + normalIt != hfmMesh.normals.constEnd(); ++normalIt, ++tangentIt) { #if FBX_PACK_NORMALS const auto normal = normalizeDirForPacking(*normalIt); @@ -681,24 +681,24 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Pack colors if (colorsSize > 0) { -#if FBX_PACK_COLORS +#if HFM_PACK_COLORS std::vector colors; - colors.reserve(fbxMesh.colors.size()); - for (const auto& color : fbxMesh.colors) { + colors.reserve(hfmMesh.colors.size()); + for (const auto& color : hfmMesh.colors) { colors.push_back(glm::packUnorm4x8(glm::vec4(color, 1.0f))); } vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) colors.data()); #else - vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) fbxMesh.colors.constData()); + vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) hfmMesh.colors.constData()); #endif } // Pack Texcoords 0 and 1 (if exists) if (texCoordsSize > 0) { QVector texCoordData; - texCoordData.reserve(fbxMesh.texCoords.size()); - for (auto& texCoordVec2f : fbxMesh.texCoords) { + texCoordData.reserve(hfmMesh.texCoords.size()); + for (auto& texCoordVec2f : hfmMesh.texCoords) { vec2h texCoordVec2h; texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x); @@ -709,8 +709,8 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { } if (texCoords1Size > 0) { QVector texCoordData; - texCoordData.reserve(fbxMesh.texCoords1.size()); - for (auto& texCoordVec2f : fbxMesh.texCoords1) { + texCoordData.reserve(hfmMesh.texCoords1.size()); + for (auto& texCoordVec2f : hfmMesh.texCoords1) { vec2h texCoordVec2h; texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x); @@ -722,22 +722,22 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Clusters data if (clusterIndicesSize > 0) { - if (fbxMesh.clusters.size() < UINT8_MAX) { + if (hfmMesh.clusters.size() < UINT8_MAX) { // yay! we can fit the clusterIndices within 8-bits - int32_t numIndices = fbxMesh.clusterIndices.size(); + int32_t numIndices = hfmMesh.clusterIndices.size(); QVector clusterIndices; clusterIndices.resize(numIndices); for (int32_t i = 0; i < numIndices; ++i) { - assert(fbxMesh.clusterIndices[i] <= UINT8_MAX); - clusterIndices[i] = (uint8_t)(fbxMesh.clusterIndices[i]); + assert(hfmMesh.clusterIndices[i] <= UINT8_MAX); + clusterIndices[i] = (uint8_t)(hfmMesh.clusterIndices[i]); } vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) clusterIndices.constData()); } else { - vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) fbxMesh.clusterIndices.constData()); + vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) hfmMesh.clusterIndices.constData()); } } if (clusterWeightsSize > 0) { - vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) fbxMesh.clusterWeights.constData()); + vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) hfmMesh.clusterWeights.constData()); } @@ -856,7 +856,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Index and Part Buffers unsigned int totalIndices = 0; - foreach(const FBXMeshPart& part, extractedMesh.parts) { + foreach(const HFMMeshPart& part, extractedMesh.parts) { totalIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size()); } @@ -875,7 +875,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { if (extractedMesh.parts.size() > 1) { indexNum = 0; } - foreach(const FBXMeshPart& part, extractedMesh.parts) { + foreach(const HFMMeshPart& part, extractedMesh.parts) { graphics::Mesh::Part modelPart(indexNum, 0, 0, graphics::Mesh::TRIANGLES); if (part.quadTrianglesIndices.size()) { diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index b93dc3541b..05534b5264 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -697,7 +697,7 @@ glm::mat4 GLTFReader::getModelTransform(const GLTFNode& node) { return tmat; } -bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { +bool GLTFReader::buildGeometry(HFMGeometry& geometry, const QUrl& url) { //Build dependencies QVector> nodeDependencies(_file.nodes.size()); @@ -750,10 +750,10 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { for (int i = 0; i < materialIDs.size(); i++) { QString& matid = materialIDs[i]; - geometry.materials[matid] = FBXMaterial(); - FBXMaterial& fbxMaterial = geometry.materials[matid]; - fbxMaterial._material = std::make_shared(); - setFBXMaterial(fbxMaterial, _file.materials[i]); + geometry.materials[matid] = HFMMaterial(); + HFMMaterial& hfmMaterial = geometry.materials[matid]; + hfmMaterial._material = std::make_shared(); + setHFMMaterial(hfmMaterial, _file.materials[i]); } @@ -765,9 +765,9 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { if (node.defined["mesh"]) { qCDebug(modelformat) << "node_transforms" << node.transforms; foreach(auto &primitive, _file.meshes[node.mesh].primitives) { - geometry.meshes.append(FBXMesh()); - FBXMesh& mesh = geometry.meshes[geometry.meshes.size() - 1]; - FBXCluster cluster; + geometry.meshes.append(HFMMesh()); + HFMMesh& mesh = geometry.meshes[geometry.meshes.size() - 1]; + HFMCluster cluster; cluster.jointIndex = 0; cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, 0, 1, 0, 0, @@ -775,7 +775,7 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { 0, 0, 0, 1); mesh.clusters.append(cluster); - FBXMeshPart part = FBXMeshPart(); + HFMMeshPart part = HFMMeshPart(); int indicesAccessorIdx = primitive.indices; @@ -910,7 +910,7 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { return true; } -FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping, +HFMGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping, const QUrl& url, bool loadLightmaps, float lightmapLevel) { _url = url; @@ -924,8 +924,8 @@ FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping parseGLTF(model); //_file.dump(); - FBXGeometry* geometryPtr = new FBXGeometry(); - FBXGeometry& geometry = *geometryPtr; + HFMGeometry* geometryPtr = new HFMGeometry(); + HFMGeometry& geometry = *geometryPtr; buildGeometry(geometry, url); @@ -997,8 +997,8 @@ QNetworkReply* GLTFReader::request(QUrl& url, bool isTest) { return netReply; // trying to sync later on. } -FBXTexture GLTFReader::getFBXTexture(const GLTFTexture& texture) { - FBXTexture fbxtex = FBXTexture(); +HFMTexture GLTFReader::getHFMTexture(const GLTFTexture& texture) { + HFMTexture fbxtex = HFMTexture(); fbxtex.texcoordSet = 0; if (texture.defined["source"]) { @@ -1014,7 +1014,7 @@ FBXTexture GLTFReader::getFBXTexture(const GLTFTexture& texture) { return fbxtex; } -void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& material) { +void GLTFReader::setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& material) { if (material.defined["name"]) { @@ -1029,17 +1029,17 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia } if (material.defined["emissiveTexture"]) { - fbxmat.emissiveTexture = getFBXTexture(_file.textures[material.emissiveTexture]); + fbxmat.emissiveTexture = getHFMTexture(_file.textures[material.emissiveTexture]); fbxmat.useEmissiveMap = true; } if (material.defined["normalTexture"]) { - fbxmat.normalTexture = getFBXTexture(_file.textures[material.normalTexture]); + fbxmat.normalTexture = getHFMTexture(_file.textures[material.normalTexture]); fbxmat.useNormalMap = true; } if (material.defined["occlusionTexture"]) { - fbxmat.occlusionTexture = getFBXTexture(_file.textures[material.occlusionTexture]); + fbxmat.occlusionTexture = getHFMTexture(_file.textures[material.occlusionTexture]); fbxmat.useOcclusionMap = true; } @@ -1050,14 +1050,14 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia fbxmat.metallic = material.pbrMetallicRoughness.metallicFactor; } if (material.pbrMetallicRoughness.defined["baseColorTexture"]) { - fbxmat.opacityTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); - fbxmat.albedoTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); + fbxmat.opacityTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); + fbxmat.albedoTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); fbxmat.useAlbedoMap = true; } if (material.pbrMetallicRoughness.defined["metallicRoughnessTexture"]) { - fbxmat.roughnessTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + fbxmat.roughnessTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); fbxmat.useRoughnessMap = true; - fbxmat.metallicTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + fbxmat.metallicTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); fbxmat.useMetallicMap = true; } if (material.pbrMetallicRoughness.defined["roughnessFactor"]) { @@ -1181,8 +1181,8 @@ void GLTFReader::retriangulate(const QVector& inIndices, const QVector materialMeshIdMap; - QVector fbxMeshParts; + QVector hfmMeshParts; for (int i = 0, meshPartCount = 0; i < mesh.parts.count(); i++, meshPartCount++) { - FBXMeshPart& meshPart = mesh.parts[i]; + HFMMeshPart& meshPart = mesh.parts[i]; FaceGroup faceGroup = faceGroups[meshPartCount]; bool specifiesUV = false; foreach(OBJFace face, faceGroup) { // Go through all of the OBJ faces and determine the number of different materials necessary (each different material will be a unique mesh). // NOTE (trent/mittens 3/30/17): this seems hardcore wasteful and is slowed down a bit by iterating through the face group twice, but it's the best way I've thought of to hack multi-material support in an OBJ into this pipeline. if (!materialMeshIdMap.contains(face.materialName)) { - // Create a new FBXMesh for this material mapping. + // Create a new HFMMesh for this material mapping. materialMeshIdMap.insert(face.materialName, materialMeshIdMap.count()); - fbxMeshParts.append(FBXMeshPart()); - FBXMeshPart& meshPartNew = fbxMeshParts.last(); + hfmMeshParts.append(HFMMeshPart()); + HFMMeshPart& meshPartNew = hfmMeshParts.last(); meshPartNew.quadIndices = QVector(meshPart.quadIndices); // Copy over quad indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. meshPartNew.quadTrianglesIndices = QVector(meshPart.quadTrianglesIndices); // Copy over quad triangulated indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. meshPartNew.triangleIndices = QVector(meshPart.triangleIndices); // Copy over triangle indices. @@ -745,14 +745,14 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m // clean up old mesh parts. int unmodifiedMeshPartCount = mesh.parts.count(); mesh.parts.clear(); - mesh.parts = QVector(fbxMeshParts); + mesh.parts = QVector(hfmMeshParts); for (int i = 0, meshPartCount = 0; i < unmodifiedMeshPartCount; i++, meshPartCount++) { FaceGroup faceGroup = faceGroups[meshPartCount]; // Now that each mesh has been created with its own unique material mappings, fill them with data (vertex data is duplicated, face data is not). foreach(OBJFace face, faceGroup) { - FBXMeshPart& meshPart = mesh.parts[materialMeshIdMap[face.materialName]]; + HFMMeshPart& meshPart = mesh.parts[materialMeshIdMap[face.materialName]]; glm::vec3 v0 = checked_at(vertices, face.vertexIndices[0]); glm::vec3 v1 = checked_at(vertices, face.vertexIndices[1]); @@ -885,38 +885,38 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m if (!objMaterial.used) { continue; } - geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor, + geometry.materials[materialID] = HFMMaterial(objMaterial.diffuseColor, objMaterial.specularColor, objMaterial.emissiveColor, objMaterial.shininess, objMaterial.opacity); - FBXMaterial& fbxMaterial = geometry.materials[materialID]; - fbxMaterial.materialID = materialID; - fbxMaterial._material = std::make_shared(); - graphics::MaterialPointer modelMaterial = fbxMaterial._material; + HFMMaterial& hfmMaterial = geometry.materials[materialID]; + hfmMaterial.materialID = materialID; + hfmMaterial._material = std::make_shared(); + graphics::MaterialPointer modelMaterial = hfmMaterial._material; if (!objMaterial.diffuseTextureFilename.isEmpty()) { - fbxMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; + hfmMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; } if (!objMaterial.specularTextureFilename.isEmpty()) { - fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename; + hfmMaterial.specularTexture.filename = objMaterial.specularTextureFilename; } if (!objMaterial.emissiveTextureFilename.isEmpty()) { - fbxMaterial.emissiveTexture.filename = objMaterial.emissiveTextureFilename; + hfmMaterial.emissiveTexture.filename = objMaterial.emissiveTextureFilename; } if (!objMaterial.bumpTextureFilename.isEmpty()) { - fbxMaterial.normalTexture.filename = objMaterial.bumpTextureFilename; - fbxMaterial.normalTexture.isBumpmap = true; - fbxMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier; + hfmMaterial.normalTexture.filename = objMaterial.bumpTextureFilename; + hfmMaterial.normalTexture.isBumpmap = true; + hfmMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier; } if (!objMaterial.opacityTextureFilename.isEmpty()) { - fbxMaterial.opacityTexture.filename = objMaterial.opacityTextureFilename; + hfmMaterial.opacityTexture.filename = objMaterial.opacityTextureFilename; } - modelMaterial->setEmissive(fbxMaterial.emissiveColor); - modelMaterial->setAlbedo(fbxMaterial.diffuseColor); - modelMaterial->setMetallic(glm::length(fbxMaterial.specularColor)); - modelMaterial->setRoughness(graphics::Material::shininessToRoughness(fbxMaterial.shininess)); + modelMaterial->setEmissive(hfmMaterial.emissiveColor); + modelMaterial->setAlbedo(hfmMaterial.diffuseColor); + modelMaterial->setMetallic(glm::length(hfmMaterial.specularColor)); + modelMaterial->setRoughness(graphics::Material::shininessToRoughness(hfmMaterial.shininess)); bool applyTransparency = false; bool applyShininess = false; @@ -971,7 +971,7 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m } if (applyTransparency) { - fbxMaterial.opacity = std::max(fbxMaterial.opacity, ILLUMINATION_MODEL_MIN_OPACITY); + hfmMaterial.opacity = std::max(hfmMaterial.opacity, ILLUMINATION_MODEL_MIN_OPACITY); } if (applyShininess) { modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_SHININESS); @@ -985,18 +985,18 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m modelMaterial->setFresnel(glm::vec3(1.0f)); } - modelMaterial->setOpacity(fbxMaterial.opacity); + modelMaterial->setOpacity(hfmMaterial.opacity); } return geometryPtr; } -void fbxDebugDump(const FBXGeometry& fbxgeo) { - qCDebug(modelformat) << "---------------- fbxGeometry ----------------"; +void fbxDebugDump(const HFMGeometry& fbxgeo) { + qCDebug(modelformat) << "---------------- hfmGeometry ----------------"; qCDebug(modelformat) << " hasSkeletonJoints =" << fbxgeo.hasSkeletonJoints; qCDebug(modelformat) << " offset =" << fbxgeo.offset; qCDebug(modelformat) << " meshes.count() =" << fbxgeo.meshes.count(); - foreach (FBXMesh mesh, fbxgeo.meshes) { + foreach (HFMMesh mesh, fbxgeo.meshes) { qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.count(); qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); qCDebug(modelformat) << " normals.count() =" << mesh.normals.count(); @@ -1014,7 +1014,7 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << " meshExtents =" << mesh.meshExtents; qCDebug(modelformat) << " modelTransform =" << mesh.modelTransform; qCDebug(modelformat) << " parts.count() =" << mesh.parts.count(); - foreach (FBXMeshPart meshPart, mesh.parts) { + foreach (HFMMeshPart meshPart, mesh.parts) { qCDebug(modelformat) << " quadIndices.count() =" << meshPart.quadIndices.count(); qCDebug(modelformat) << " triangleIndices.count() =" << meshPart.triangleIndices.count(); /* @@ -1031,7 +1031,7 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { */ } qCDebug(modelformat) << " clusters.count() =" << mesh.clusters.count(); - foreach (FBXCluster cluster, mesh.clusters) { + foreach (HFMCluster cluster, mesh.clusters) { qCDebug(modelformat) << " jointIndex =" << cluster.jointIndex; qCDebug(modelformat) << " inverseBindMatrix =" << cluster.inverseBindMatrix; } @@ -1040,7 +1040,7 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << " jointIndices =" << fbxgeo.jointIndices; qCDebug(modelformat) << " joints.count() =" << fbxgeo.joints.count(); - foreach (FBXJoint joint, fbxgeo.joints) { + foreach (HFMJoint joint, fbxgeo.joints) { qCDebug(modelformat) << " isFree =" << joint.isFree; qCDebug(modelformat) << " freeLineage" << joint.freeLineage; qCDebug(modelformat) << " parentIndex" << joint.parentIndex; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index e432a3ea51..edfe6f23e8 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -42,7 +42,7 @@ public: bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices, const QVector& vertexColors); // Return a set of one or more OBJFaces from this one, in which each is just a triangle. - // Even though FBXMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. + // Even though HFMMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. QVector triangulate(); private: void addFrom(const OBJFace* face, int index); @@ -54,7 +54,7 @@ public: } ; // Materials and references to material names can come in any order, and different mesh parts can refer to the same material. -// Therefore it would get pretty hacky to try to use FBXMeshPart to store these as we traverse the files. +// Therefore it would get pretty hacky to try to use HFMMeshPart to store these as we traverse the files. class OBJMaterial { public: float shininess; @@ -87,13 +87,13 @@ public: QString currentMaterialName; QHash materials; - FBXGeometry::Pointer readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); + HFMGeometry::Pointer readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); private: QUrl _url; QHash librariesSeen; - bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, + bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, HFMGeometry& geometry, float& scaleGuess, bool combineParts); void parseMaterialLibrary(QIODevice* device); void parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions); @@ -103,5 +103,5 @@ private: }; // What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility. -void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID); -void fbxDebugDump(const FBXGeometry& fbxgeo); +void setMeshPartDefaults(HFMMeshPart& meshPart, QString materialID); +void fbxDebugDump(const HFMGeometry& fbxgeo); diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index e96815d391..a950e1df3c 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -128,7 +128,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) { void GeometryMappingResource::onGeometryMappingLoaded(bool success) { if (success && _geometryResource) { - _fbxGeometry = _geometryResource->_fbxGeometry; + _hfmGeometry = _geometryResource->_hfmGeometry; _meshParts = _geometryResource->_meshParts; _meshes = _geometryResource->_meshes; _materials = _geometryResource->_materials; @@ -193,38 +193,38 @@ void GeometryReader::run() { _url.path().toLower().endsWith(".obj.gz") || _url.path().toLower().endsWith(".gltf"))) { - FBXGeometry::Pointer fbxGeometry; + HFMGeometry::Pointer hfmGeometry; if (_url.path().toLower().endsWith(".fbx")) { - fbxGeometry.reset(readFBX(_data, _mapping, _url.path())); - if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) { + hfmGeometry.reset(readFBX(_data, _mapping, _url.path())); + if (hfmGeometry->meshes.size() == 0 && hfmGeometry->joints.size() == 0) { throw QString("empty geometry, possibly due to an unsupported FBX version"); } } else if (_url.path().toLower().endsWith(".obj")) { - fbxGeometry = OBJReader().readOBJ(_data, _mapping, _combineParts, _url); + hfmGeometry = OBJReader().readOBJ(_data, _mapping, _combineParts, _url); } else if (_url.path().toLower().endsWith(".obj.gz")) { QByteArray uncompressedData; if (gunzip(_data, uncompressedData)){ - fbxGeometry = OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url); + hfmGeometry = OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url); } else { throw QString("failed to decompress .obj.gz"); } } else if (_url.path().toLower().endsWith(".gltf")) { std::shared_ptr glreader = std::make_shared(); - fbxGeometry.reset(glreader->readGLTF(_data, _mapping, _url)); - if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) { + hfmGeometry.reset(glreader->readGLTF(_data, _mapping, _url)); + if (hfmGeometry->meshes.size() == 0 && hfmGeometry->joints.size() == 0) { throw QString("empty geometry, possibly due to an unsupported GLTF version"); } } else { throw QString("unsupported format"); } - // Add scripts to fbxgeometry + // Add scripts to hfmGeometry if (!_mapping.value(SCRIPT_FIELD).isNull()) { QVariantList scripts = _mapping.values(SCRIPT_FIELD); for (auto &script : scripts) { - fbxGeometry->scripts.push_back(script.toString()); + hfmGeometry->scripts.push_back(script.toString()); } } @@ -234,7 +234,7 @@ void GeometryReader::run() { qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; } else { QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", - Q_ARG(FBXGeometry::Pointer, fbxGeometry)); + Q_ARG(HFMGeometry::Pointer, hfmGeometry)); } } else { throw QString("url is invalid"); @@ -262,7 +262,7 @@ public: virtual void downloadFinished(const QByteArray& data) override; protected: - Q_INVOKABLE void setGeometryDefinition(FBXGeometry::Pointer fbxGeometry); + Q_INVOKABLE void setGeometryDefinition(HFMGeometry::Pointer hfmGeometry); private: QVariantHash _mapping; @@ -277,13 +277,13 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { QThreadPool::globalInstance()->start(new GeometryReader(_self, _effectiveBaseURL, _mapping, data, _combineParts)); } -void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxGeometry) { +void GeometryDefinitionResource::setGeometryDefinition(HFMGeometry::Pointer hfmGeometry) { // Assume ownership of the geometry pointer - _fbxGeometry = fbxGeometry; + _hfmGeometry = hfmGeometry; // Copy materials QHash materialIDAtlas; - for (const FBXMaterial& material : _fbxGeometry->materials) { + for (const HFMMaterial& material : _hfmGeometry->materials) { materialIDAtlas[material.materialID] = _materials.size(); _materials.push_back(std::make_shared(material, _textureBaseUrl)); } @@ -291,11 +291,11 @@ void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxG std::shared_ptr meshes = std::make_shared(); std::shared_ptr parts = std::make_shared(); int meshID = 0; - for (const FBXMesh& mesh : _fbxGeometry->meshes) { + for (const HFMMesh& mesh : _hfmGeometry->meshes) { // Copy mesh pointers meshes->emplace_back(mesh._mesh); int partID = 0; - for (const FBXMeshPart& part : mesh.parts) { + for (const HFMMeshPart& part : mesh.parts) { // Construct local parts parts->push_back(std::make_shared(meshID, partID, (int)materialIDAtlas[part.materialID])); partID++; @@ -371,7 +371,7 @@ const QVariantMap Geometry::getTextures() const { // FIXME: The materials should only be copied when modified, but the Model currently caches the original Geometry::Geometry(const Geometry& geometry) { - _fbxGeometry = geometry._fbxGeometry; + _hfmGeometry = geometry._hfmGeometry; _meshes = geometry._meshes; _meshParts = geometry._meshParts; @@ -444,8 +444,8 @@ void GeometryResource::deleter() { } void GeometryResource::setTextures() { - if (_fbxGeometry) { - for (const FBXMaterial& material : _fbxGeometry->materials) { + if (_hfmGeometry) { + for (const HFMMaterial& material : _hfmGeometry->materials) { _materials.push_back(std::make_shared(material, _textureBaseUrl)); } } @@ -512,7 +512,7 @@ const QString& NetworkMaterial::getTextureName(MapChannel channel) { return NO_TEXTURE; } -QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& texture) { +QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const HFMTexture& texture) { if (texture.content.isEmpty()) { // External file: search relative to the baseUrl, in case filename is relative return baseUrl.resolved(QUrl(texture.filename)); @@ -529,22 +529,22 @@ QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& textu } } -graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, +graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture, image::TextureUsage::Type type, MapChannel channel) { if (baseUrl.isEmpty()) { return nullptr; } - const auto url = getTextureUrl(baseUrl, fbxTexture); - const auto texture = DependencyManager::get()->getTexture(url, type, fbxTexture.content, fbxTexture.maxNumPixels); - _textures[channel] = Texture { fbxTexture.name, texture }; + const auto url = getTextureUrl(baseUrl, hfmTexture); + const auto texture = DependencyManager::get()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels); + _textures[channel] = Texture { hfmTexture.name, texture }; auto map = std::make_shared(); if (texture) { map->setTextureSource(texture->_textureSource); } - map->setTextureTransform(fbxTexture.transform); + map->setTextureTransform(hfmTexture.transform); return map; } @@ -624,7 +624,7 @@ void NetworkMaterial::setLightmapMap(const QUrl& url) { } } -NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) : +NetworkMaterial::NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl) : graphics::Material(*material._material), _textures(MapChannel::NUM_MAP_CHANNELS) { diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 5cbe96ea03..2283c355d8 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -45,9 +45,9 @@ public: // Mutable, but must retain structure of vector using NetworkMaterials = std::vector>; - bool isGeometryLoaded() const { return (bool)_fbxGeometry; } + bool isGeometryLoaded() const { return (bool)_hfmGeometry; } - const FBXGeometry& getFBXGeometry() const { return *_fbxGeometry; } + const HFMGeometry& getHFMGeometry() const { return *_hfmGeometry; } const GeometryMeshes& getMeshes() const { return *_meshes; } const std::shared_ptr getShapeMaterial(int shapeID) const; @@ -62,7 +62,7 @@ protected: friend class GeometryMappingResource; // Shared across all geometries, constant throughout lifetime - std::shared_ptr _fbxGeometry; + std::shared_ptr _hfmGeometry; std::shared_ptr _meshes; std::shared_ptr _meshParts; @@ -94,7 +94,7 @@ protected: // Geometries may not hold onto textures while cached - that is for the texture cache // Instead, these methods clear and reset textures from the geometry when caching/loading - bool shouldSetTextures() const { return _fbxGeometry && _materials.empty(); } + bool shouldSetTextures() const { return _hfmGeometry && _materials.empty(); } void setTextures(); void resetTextures(); @@ -165,7 +165,7 @@ public: using MapChannel = graphics::Material::MapChannel; NetworkMaterial() : _textures(MapChannel::NUM_MAP_CHANNELS) {} - NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl); + NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl); NetworkMaterial(const NetworkMaterial& material); void setAlbedoMap(const QUrl& url, bool useAlphaChannel); @@ -201,8 +201,8 @@ protected: private: // Helpers for the ctors - QUrl getTextureUrl(const QUrl& baseUrl, const FBXTexture& fbxTexture); - graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, + QUrl getTextureUrl(const QUrl& baseUrl, const HFMTexture& hfmTexture); + graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture, image::TextureUsage::Type type, MapChannel channel); graphics::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 81a017a46d..31d6cef060 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -32,8 +32,8 @@ bool CauterizedModel::updateGeometry() { bool needsFullUpdate = Model::updateGeometry(); if (_isCauterized && needsFullUpdate) { assert(_cauterizeMeshStates.empty()); - const FBXGeometry& fbxGeometry = getFBXGeometry(); - foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + const HFMGeometry& hfmGeometry = getHFMGeometry(); + foreach (const HFMMesh& mesh, hfmGeometry.meshes) { Model::MeshState state; if (_useDualQuaternionSkinning) { state.clusterDualQuaternions.resize(mesh.clusters.size()); @@ -76,7 +76,7 @@ void CauterizedModel::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); - const FBXGeometry& fbxGeometry = getFBXGeometry(); + const HFMGeometry& hfmGeometry = getHFMGeometry(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -86,7 +86,7 @@ void CauterizedModel::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - initializeBlendshapes(fbxGeometry.meshes[i], i); + initializeBlendshapes(hfmGeometry.meshes[i], i); auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast(ptr); @@ -109,13 +109,13 @@ void CauterizedModel::updateClusterMatrices() { return; } _needsUpdateClusterMatrices = false; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); @@ -145,10 +145,10 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); if (_useDualQuaternionSkinning) { if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 4ebd92bb05..8e2541fdda 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -260,8 +260,8 @@ void ModelMeshPartPayload::initCache(const ModelPointer& model) { _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); _isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX); - const FBXGeometry& geometry = model->getFBXGeometry(); - const FBXMesh& mesh = geometry.meshes.at(_meshIndex); + const HFMGeometry& geometry = model->getHFMGeometry(); + const HFMMesh& mesh = geometry.meshes.at(_meshIndex); _isBlendShaped = !mesh.blendshapes.isEmpty(); _hasTangents = !mesh.tangents.isEmpty(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 53009e8bfa..65b3fef7c0 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -183,7 +183,7 @@ bool Model::shouldInvalidatePayloadShapeKey(int meshIndex) { return true; } - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); const auto& networkMeshes = getGeometry()->getMeshes(); // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // to false to rebuild out mesh groups. @@ -278,7 +278,7 @@ void Model::setRenderItemsNeedUpdate() { void Model::reset() { if (isLoaded()) { - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); _rig.reset(geometry); emit rigReset(); emit rigReady(); @@ -295,13 +295,13 @@ bool Model::updateGeometry() { _needsReload = false; // TODO: should all Models have a valid _rig? - if (_rig.jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { + if (_rig.jointStatesEmpty() && getHFMGeometry().joints.size() > 0) { initJointStates(); assert(_meshStates.empty()); - const FBXGeometry& fbxGeometry = getFBXGeometry(); + const HFMGeometry& hfmGeometry = getHFMGeometry(); int i = 0; - foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + foreach (const HFMMesh& mesh, hfmGeometry.meshes) { MeshState state; state.clusterDualQuaternions.resize(mesh.clusters.size()); state.clusterMatrices.resize(mesh.clusters.size()); @@ -319,7 +319,7 @@ bool Model::updateGeometry() { // virtual void Model::initJointStates() { - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); _rig.initJointStates(geometry, modelOffset); @@ -363,7 +363,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g int bestShapeID = 0; int bestSubMeshIndex = 0; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); if (!_triangleSetsValid) { calculateTriangleSets(geometry); } @@ -506,7 +506,7 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co int bestShapeID = 0; int bestSubMeshIndex = 0; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); if (!_triangleSetsValid) { calculateTriangleSets(geometry); } @@ -641,7 +641,7 @@ bool Model::convexHullContains(glm::vec3 point) { QMutexLocker locker(&_mutex); if (!_triangleSetsValid) { - calculateTriangleSets(getFBXGeometry()); + calculateTriangleSets(getHFMGeometry()); } // If we are inside the models box, then consider the submeshes... @@ -753,14 +753,14 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe } // update triangles for picking { - FBXGeometry geometry; + HFMGeometry geometry; for (const auto& newMesh : meshes) { - FBXMesh mesh; + HFMMesh mesh; mesh._mesh = newMesh.getMeshPointer(); mesh.vertices = buffer_helpers::mesh::attributeToVector(mesh._mesh, gpu::Stream::POSITION); int numParts = (int)newMesh.getMeshPointer()->getNumParts(); for (int partID = 0; partID < numParts; partID++) { - FBXMeshPart part; + HFMMeshPart part; part.triangleIndices = buffer_helpers::bufferToVector(mesh._mesh->getIndexBuffer(), "part.triangleIndices"); mesh.parts << part; } @@ -789,12 +789,12 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { return result; } - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); int numberOfMeshes = geometry.meshes.size(); int shapeID = 0; for (int i = 0; i < numberOfMeshes; i++) { - const FBXMesh& fbxMesh = geometry.meshes.at(i); - if (auto mesh = fbxMesh._mesh) { + const HFMMesh& hfmMesh = geometry.meshes.at(i); + if (auto mesh = hfmMesh._mesh) { result.append(mesh); int numParts = (int)mesh->getNumParts(); @@ -808,7 +808,7 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { return result; } -void Model::calculateTriangleSets(const FBXGeometry& geometry) { +void Model::calculateTriangleSets(const HFMGeometry& geometry) { PROFILE_RANGE(render, __FUNCTION__); int numberOfMeshes = geometry.meshes.size(); @@ -818,14 +818,14 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) { _modelSpaceMeshTriangleSets.resize(numberOfMeshes); for (int i = 0; i < numberOfMeshes; i++) { - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = geometry.meshes.at(i); const int numberOfParts = mesh.parts.size(); auto& meshTriangleSets = _modelSpaceMeshTriangleSets[i]; meshTriangleSets.resize(numberOfParts); for (int j = 0; j < numberOfParts; j++) { - const FBXMeshPart& part = mesh.parts.at(j); + const HFMMeshPart& part = mesh.parts.at(j); auto& partTriangleSet = meshTriangleSets[j]; @@ -1114,7 +1114,7 @@ Extents Model::getBindExtents() const { if (!isActive()) { return Extents(); } - const Extents& bindExtents = getFBXGeometry().bindExtents; + const Extents& bindExtents = getHFMGeometry().bindExtents; Extents scaledExtents = { bindExtents.minimum * _scale, bindExtents.maximum * _scale }; return scaledExtents; } @@ -1128,12 +1128,12 @@ Extents Model::getMeshExtents() const { if (!isActive()) { return Extents(); } - const Extents& extents = getFBXGeometry().meshExtents; + const Extents& extents = getHFMGeometry().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which // is captured in the offset matrix - glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); - glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + glm::vec3 minimum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum * _scale, maximum * _scale }; return scaledExtents; } @@ -1143,12 +1143,12 @@ Extents Model::getUnscaledMeshExtents() const { return Extents(); } - const Extents& extents = getFBXGeometry().meshExtents; + const Extents& extents = getHFMGeometry().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which // is captured in the offset matrix - glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); - glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + glm::vec3 minimum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum, maximum }; return scaledExtents; @@ -1171,11 +1171,11 @@ void Model::setJointTranslation(int index, bool valid, const glm::vec3& translat } int Model::getParentJointIndex(int jointIndex) const { - return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).parentIndex : -1; + return (isActive() && jointIndex != -1) ? getHFMGeometry().joints.at(jointIndex).parentIndex : -1; } int Model::getLastFreeJointIndex(int jointIndex) const { - return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1; + return (isActive() && jointIndex != -1) ? getHFMGeometry().joints.at(jointIndex).freeLineage.last() : -1; } void Model::setTextures(const QVariantMap& textures) { @@ -1275,7 +1275,7 @@ QStringList Model::getJointNames() const { Q_RETURN_ARG(QStringList, result)); return result; } - return isActive() ? getFBXGeometry().getJointNames() : QStringList(); + return isActive() ? getHFMGeometry().getJointNames() : QStringList(); } void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) { @@ -1415,12 +1415,12 @@ void Model::updateClusterMatrices() { } _needsUpdateClusterMatrices = false; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); @@ -1505,7 +1505,7 @@ void Model::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); - auto& fbxGeometry = getFBXGeometry(); + auto& hfmGeometry = getHFMGeometry(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -1515,7 +1515,7 @@ void Model::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - initializeBlendshapes(fbxGeometry.meshes[i], i); + initializeBlendshapes(hfmGeometry.meshes[i], i); _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); auto material = getGeometry()->getShapeMaterial(shapeID); _modelMeshMaterialNames.push_back(material ? material->getName() : ""); @@ -1600,7 +1600,7 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string class CollisionRenderGeometry : public Geometry { public: CollisionRenderGeometry(graphics::MeshPointer mesh) { - _fbxGeometry = std::make_shared(); + _hfmGeometry = std::make_shared(); std::shared_ptr meshes = std::make_shared(); meshes->push_back(mesh); _meshes = meshes; @@ -1656,9 +1656,9 @@ void Blender::run() { if (_model && _model->isLoaded()) { DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); int offset = 0; - auto meshes = _model->getFBXGeometry().meshes; + auto meshes = _model->getHFMGeometry().meshes; int meshIndex = 0; - foreach(const FBXMesh& mesh, meshes) { + foreach(const HFMMesh& mesh, meshes) { auto modelMeshBlendshapeOffsets = _model->_blendshapeOffsets.find(meshIndex++); if (mesh.blendshapes.isEmpty() || modelMeshBlendshapeOffsets == _model->_blendshapeOffsets.end()) { // Not blendshaped or not initialized @@ -1688,7 +1688,7 @@ void Blender::run() { } float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; - const FBXBlendshape& blendshape = mesh.blendshapes.at(i); + const HFMBlendshape& blendshape = mesh.blendshapes.at(i); tbb::parallel_for(tbb::blocked_range(0, blendshape.indices.size()), [&](const tbb::blocked_range& range) { for (auto j = range.begin(); j < range.end(); j++) { @@ -1731,7 +1731,7 @@ bool Model::maybeStartBlender() { return false; } -void Model::initializeBlendshapes(const FBXMesh& mesh, int index) { +void Model::initializeBlendshapes(const HFMMesh& mesh, int index) { if (mesh.blendshapes.empty()) { // mesh doesn't have blendshape, did we allocate one though ? if (_blendshapeOffsets.find(index) != _blendshapeOffsets.end()) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 71809821eb..db5625b3d8 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -185,7 +185,7 @@ public: /// Provided as a convenience, will crash if !isLoaded() // And so that getGeometry() isn't chained everywhere - const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return _renderGeometry->getFBXGeometry(); } + const HFMGeometry& getHFMGeometry() const { assert(isLoaded()); return _renderGeometry->getHFMGeometry(); } bool isActive() const { return isLoaded(); } @@ -450,7 +450,7 @@ protected: bool _overrideModelTransform { false }; bool _triangleSetsValid { false }; - void calculateTriangleSets(const FBXGeometry& geometry); + void calculateTriangleSets(const HFMGeometry& geometry); std::vector> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes virtual void createRenderItemSet(); @@ -506,7 +506,7 @@ protected: bool shouldInvalidatePayloadShapeKey(int meshIndex); - void initializeBlendshapes(const FBXMesh& mesh, int index); + void initializeBlendshapes(const HFMMesh& mesh, int index); private: float _loadingPriority { 0.0f }; diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 90015768d0..77b09caa1d 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -41,14 +41,14 @@ void SoftAttachmentModel::updateClusterMatrices() { _needsUpdateClusterMatrices = false; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); diff --git a/tests-manual/gpu/src/TestFbx.cpp b/tests-manual/gpu/src/TestFbx.cpp index 538bb0a973..ba94fb810c 100644 --- a/tests-manual/gpu/src/TestFbx.cpp +++ b/tests-manual/gpu/src/TestFbx.cpp @@ -100,7 +100,7 @@ bool TestFbx::isReady() const { void TestFbx::parseFbx(const QByteArray& fbxData) { QVariantHash mapping; - FBXGeometry* fbx = readFBX(fbxData, mapping); + HFMGeometry* fbx = readFBX(fbxData, mapping); size_t totalVertexCount = 0; size_t totalIndexCount = 0; size_t totalPartCount = 0; diff --git a/tests-manual/gpu/src/TestFbx.h b/tests-manual/gpu/src/TestFbx.h index 391fff1091..4e22928460 100644 --- a/tests-manual/gpu/src/TestFbx.h +++ b/tests-manual/gpu/src/TestFbx.h @@ -11,7 +11,7 @@ #include -class FBXGeometry; +class HFMGeometry; class TestFbx : public GpuTestBase { size_t _partCount { 0 }; diff --git a/tests/animation/src/AnimInverseKinematicsTests.cpp b/tests/animation/src/AnimInverseKinematicsTests.cpp index f5d3597f56..f51fe12ecb 100644 --- a/tests/animation/src/AnimInverseKinematicsTests.cpp +++ b/tests/animation/src/AnimInverseKinematicsTests.cpp @@ -28,8 +28,8 @@ const glm::quat identity = glm::quat(); const glm::quat quaterTurnAroundZ = glm::angleAxis(0.5f * PI, zAxis); -void makeTestFBXJoints(FBXGeometry& geometry) { - FBXJoint joint; +void makeTestFBXJoints(HFMGeometry& geometry) { + HFMJoint joint; joint.isFree = false; joint.freeLineage.clear(); joint.parentIndex = -1; @@ -79,7 +79,7 @@ void makeTestFBXJoints(FBXGeometry& geometry) { // compute each joint's transform for (int i = 1; i < (int)geometry.joints.size(); ++i) { - FBXJoint& j = geometry.joints[i]; + HFMJoint& j = geometry.joints[i]; int parentIndex = j.parentIndex; // World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1) j.transform = geometry.joints[parentIndex].transform * @@ -96,7 +96,7 @@ void AnimInverseKinematicsTests::testSingleChain() { AnimContext context(false, false, false, glm::mat4(), glm::mat4()); - FBXGeometry geometry; + HFMGeometry geometry; makeTestFBXJoints(geometry); // create a skeleton and doll diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index e9d8243e38..5107931da1 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -54,8 +54,8 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc return; } QByteArray blob = file.readAll(); - std::unique_ptr fbxGeometry(readFBX(blob, QVariantHash())); - std::unique_ptr skeleton(new AnimSkeleton(*fbxGeometry)); + std::unique_ptr geometry(readFBX(blob, QVariantHash())); + std::unique_ptr skeleton(new AnimSkeleton(*geometry)); skeleton->dump(verbose); } diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index a52e948f01..bb2958e11d 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -19,16 +19,16 @@ // FBXReader jumbles the order of the meshes by reading them back out of a hashtable. This will put // them back in the order in which they appeared in the file. -bool FBXGeometryLessThan(const FBXMesh& e1, const FBXMesh& e2) { +bool HFMGeometryLessThan(const HFMMesh& e1, const HFMMesh& e2) { return e1.meshIndex < e2.meshIndex; } -void reSortFBXGeometryMeshes(FBXGeometry& geometry) { - qSort(geometry.meshes.begin(), geometry.meshes.end(), FBXGeometryLessThan); +void reSortHFMGeometryMeshes(HFMGeometry& geometry) { + qSort(geometry.meshes.begin(), geometry.meshes.end(), HFMGeometryLessThan); } // Read all the meshes from provided FBX file -bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { +bool vhacd::VHACDUtil::loadFBX(const QString filename, HFMGeometry& result) { if (_verbose) { qDebug() << "reading FBX file =" << filename << "..."; } @@ -41,7 +41,7 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { } try { QByteArray fbxContents = fbx.readAll(); - FBXGeometry::Pointer geom; + HFMGeometry::Pointer geom; if (filename.toLower().endsWith(".obj")) { bool combineParts = false; geom = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts); @@ -53,7 +53,7 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { } result = *geom; - reSortFBXGeometryMeshes(result); + reSortHFMGeometryMeshes(result); } catch (const QString& error) { qWarning() << "error reading" << filename << ":" << error; return false; @@ -63,7 +63,7 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { } -void getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector& triangleIndices) { +void getTrianglesInMeshPart(const HFMMeshPart &meshPart, std::vector& triangleIndices) { // append triangle indices triangleIndices.reserve(triangleIndices.size() + (size_t)meshPart.triangleIndices.size()); for (auto index : meshPart.triangleIndices) { @@ -88,12 +88,12 @@ void getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector& trian } } -void vhacd::VHACDUtil::fattenMesh(const FBXMesh& mesh, const glm::mat4& geometryOffset, FBXMesh& result) const { +void vhacd::VHACDUtil::fattenMesh(const HFMMesh& mesh, const glm::mat4& geometryOffset, HFMMesh& result) const { // this is used to make meshes generated from a highfield collidable. each triangle // is converted into a tetrahedron and made into its own mesh-part. std::vector triangleIndices; - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { getTrianglesInMeshPart(meshPart, triangleIndices); } @@ -145,7 +145,7 @@ void vhacd::VHACDUtil::fattenMesh(const FBXMesh& mesh, const glm::mat4& geometry int index3 = result.vertices.size(); result.vertices << p3; // add the new point to the result mesh - FBXMeshPart newMeshPart; + HFMMeshPart newMeshPart; setMeshPartDefaults(newMeshPart, "unknown"); newMeshPart.triangleIndices << index0 << index1 << index2; newMeshPart.triangleIndices << index0 << index3 << index1; @@ -155,7 +155,7 @@ void vhacd::VHACDUtil::fattenMesh(const FBXMesh& mesh, const glm::mat4& geometry } } -AABox getAABoxForMeshPart(const FBXMesh& mesh, const FBXMeshPart &meshPart) { +AABox getAABoxForMeshPart(const HFMMesh& mesh, const HFMMeshPart &meshPart) { AABox aaBox; const int TRIANGLE_STRIDE = 3; for (int i = 0; i < meshPart.triangleIndices.size(); i += TRIANGLE_STRIDE) { @@ -242,7 +242,7 @@ bool isClosedManifold(const std::vector& triangleIndices) { return true; } -void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) const { +void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, HFMMesh& resultMesh) const { // Number of hulls for this input meshPart uint32_t numHulls = convexifier->GetNConvexHulls(); if (_verbose) { @@ -256,8 +256,8 @@ void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& res VHACD::IVHACD::ConvexHull hull; convexifier->GetConvexHull(j, hull); - resultMesh.parts.append(FBXMeshPart()); - FBXMeshPart& resultMeshPart = resultMesh.parts.last(); + resultMesh.parts.append(HFMMeshPart()); + HFMMeshPart& resultMeshPart = resultMesh.parts.last(); int hullIndexStart = resultMesh.vertices.size(); resultMesh.vertices.reserve(hullIndexStart + hull.m_nPoints); @@ -288,9 +288,9 @@ float computeDt(uint64_t start) { return (float)(usecTimestampNow() - start) / (float)USECS_PER_SECOND; } -bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, +bool vhacd::VHACDUtil::computeVHACD(HFMGeometry& geometry, VHACD::IVHACD::Parameters params, - FBXGeometry& result, + HFMGeometry& result, float minimumMeshSize, float maximumMeshSize) { if (_verbose) { qDebug() << "meshes =" << geometry.meshes.size(); @@ -298,7 +298,7 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, // count the mesh-parts int numParts = 0; - foreach (const FBXMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, geometry.meshes) { numParts += mesh.parts.size(); } if (_verbose) { @@ -308,15 +308,15 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, VHACD::IVHACD * convexifier = VHACD::CreateVHACD(); result.meshExtents.reset(); - result.meshes.append(FBXMesh()); - FBXMesh &resultMesh = result.meshes.last(); + result.meshes.append(HFMMesh()); + HFMMesh &resultMesh = result.meshes.last(); const uint32_t POINT_STRIDE = 3; const uint32_t TRIANGLE_STRIDE = 3; int meshIndex = 0; int validPartsFound = 0; - foreach (const FBXMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, geometry.meshes) { // find duplicate points int numDupes = 0; @@ -354,7 +354,7 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, int partIndex = 0; std::vector triangleIndices; - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { triangleIndices.clear(); getTrianglesInMeshPart(meshPart, triangleIndices); @@ -421,7 +421,7 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, triangleIndices.clear(); for (auto index : openParts) { - const FBXMeshPart &meshPart = mesh.parts[index]; + const HFMMeshPart &meshPart = mesh.parts[index]; getTrianglesInMeshPart(meshPart, triangleIndices); } diff --git a/tools/vhacd-util/src/VHACDUtil.h b/tools/vhacd-util/src/VHACDUtil.h index 35ec3ef56b..64e86ed7df 100644 --- a/tools/vhacd-util/src/VHACDUtil.h +++ b/tools/vhacd-util/src/VHACDUtil.h @@ -27,16 +27,16 @@ namespace vhacd { public: void setVerbose(bool verbose) { _verbose = verbose; } - bool loadFBX(const QString filename, FBXGeometry& result); + bool loadFBX(const QString filename, HFMGeometry& result); - void fattenMesh(const FBXMesh& mesh, const glm::mat4& gometryOffset, FBXMesh& result) const; + void fattenMesh(const HFMMesh& mesh, const glm::mat4& gometryOffset, HFMMesh& result) const; - bool computeVHACD(FBXGeometry& geometry, + bool computeVHACD(HFMGeometry& geometry, VHACD::IVHACD::Parameters params, - FBXGeometry& result, + HFMGeometry& result, float minimumMeshSize, float maximumMeshSize); - void getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) const; + void getConvexResults(VHACD::IVHACD* convexifier, HFMMesh& resultMesh) const; ~VHACDUtil(); @@ -55,6 +55,6 @@ namespace vhacd { }; } -AABox getAABoxForMeshPart(const FBXMeshPart &meshPart); +AABox getAABoxForMeshPart(const HFMMeshPart &meshPart); #endif //hifi_VHACDUtil_h diff --git a/tools/vhacd-util/src/VHACDUtilApp.cpp b/tools/vhacd-util/src/VHACDUtilApp.cpp index c263dce609..0941198234 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.cpp +++ b/tools/vhacd-util/src/VHACDUtilApp.cpp @@ -36,7 +36,7 @@ QString formatFloat(double n) { } -bool VHACDUtilApp::writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart) { +bool VHACDUtilApp::writeOBJ(QString outFileName, HFMGeometry& geometry, bool outputCentimeters, int whichMeshPart) { QFile file(outFileName); if (!file.open(QIODevice::WriteOnly)) { qWarning() << "unable to write to" << outFileName; @@ -56,9 +56,9 @@ bool VHACDUtilApp::writeOBJ(QString outFileName, FBXGeometry& geometry, bool out int vertexIndexOffset = 0; - foreach (const FBXMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, geometry.meshes) { bool verticesHaveBeenOutput = false; - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { if (whichMeshPart >= 0 && nth != (unsigned int) whichMeshPart) { nth++; continue; @@ -297,7 +297,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } // load the mesh - FBXGeometry fbx; + HFMGeometry fbx; auto begin = std::chrono::high_resolution_clock::now(); if (!vUtil.loadFBX(inputFilename, fbx)){ _returnCode = VHACD_RETURN_CODE_FAILURE_TO_READ; @@ -315,8 +315,8 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : QVector infileExtensions = {"fbx", "obj"}; QString baseFileName = fileNameWithoutExtension(outputFilename, infileExtensions); int count = 0; - foreach (const FBXMesh& mesh, fbx.meshes) { - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMesh& mesh, fbx.meshes) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { QString outputFileName = baseFileName + "-" + QString::number(count) + ".obj"; writeOBJ(outputFileName, fbx, outputCentimeters, count); count++; @@ -358,7 +358,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } begin = std::chrono::high_resolution_clock::now(); - FBXGeometry result; + HFMGeometry result; bool success = vUtil.computeVHACD(fbx, params, result, minimumMeshSize, maximumMeshSize); end = std::chrono::high_resolution_clock::now(); @@ -377,9 +377,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : int totalVertices = 0; int totalTriangles = 0; - foreach (const FBXMesh& mesh, result.meshes) { + foreach (const HFMMesh& mesh, result.meshes) { totalVertices += mesh.vertices.size(); - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { totalTriangles += meshPart.triangleIndices.size() / 3; // each quad was made into two triangles totalTriangles += 2 * meshPart.quadIndices.size() / 4; @@ -398,17 +398,17 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } if (fattenFaces) { - FBXGeometry newFbx; - FBXMesh result; + HFMGeometry newFbx; + HFMMesh result; // count the mesh-parts unsigned int meshCount = 0; - foreach (const FBXMesh& mesh, fbx.meshes) { + foreach (const HFMMesh& mesh, fbx.meshes) { meshCount += mesh.parts.size(); } result.modelTransform = glm::mat4(); // Identity matrix - foreach (const FBXMesh& mesh, fbx.meshes) { + foreach (const HFMMesh& mesh, fbx.meshes) { vUtil.fattenMesh(mesh, fbx.offset, result); } diff --git a/tools/vhacd-util/src/VHACDUtilApp.h b/tools/vhacd-util/src/VHACDUtilApp.h index 0d75275802..3db49456a0 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.h +++ b/tools/vhacd-util/src/VHACDUtilApp.h @@ -28,7 +28,7 @@ public: VHACDUtilApp(int argc, char* argv[]); ~VHACDUtilApp(); - bool writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart = -1); + bool writeOBJ(QString outFileName, HFMGeometry& geometry, bool outputCentimeters, int whichMeshPart = -1); int getReturnCode() const { return _returnCode; } From e5f06690b509480d8b16c6b2180cab63a84b0e35 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 30 Oct 2018 17:31:31 -0700 Subject: [PATCH 047/125] Strip out references to 'my purchases' vs 'my items' code --- .../qml/hifi/commerce/purchases/Purchases.qml | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 7297e046e2..09cea01ccf 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -33,7 +33,6 @@ Rectangle { property bool securityImageResultReceived: false; property bool purchasesReceived: false; property bool punctuationMode: false; - property bool isShowingMyItems: false; property bool isDebuggingFirstUseTutorial: false; property string installedApps; property bool keyboardRaised: false; @@ -105,10 +104,6 @@ Rectangle { } } - onIsShowingMyItemsChanged: { - getPurchases(); - } - Timer { id: notSetUpTimer; interval: 200; @@ -457,7 +452,7 @@ Rectangle { anchors.left: parent.left; anchors.leftMargin: 16; width: paintedWidth; - text: isShowingMyItems ? "My Items" : "Inventory"; + text: "Inventory"; color: hifi.colors.black; size: 22; } @@ -520,6 +515,7 @@ Rectangle { onTextChanged: { purchasesModel.searchFilter = filterBar.text; filterBar.previousText = filterBar.text; + } } } @@ -543,9 +539,8 @@ Rectangle { listModelName: 'purchases'; listView: purchasesContentsList; getPage: function () { - console.debug('getPage', purchasesModel.listModelName, root.isShowingMyItems, filterBar.primaryFilter_filterName, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage); + console.debug('getPage', purchasesModel.listModelName, filterBar.primaryFilter_filterName, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage); Commerce.inventory( - root.isShowingMyItems ? "proofs" : "purchased", filterBar.primaryFilter_filterName, filterBar.text, purchasesModel.currentPageToRetrieve, @@ -596,7 +591,6 @@ Rectangle { upgradeUrl: model.upgrade_url; upgradeTitle: model.upgrade_title; itemType: model.item_type; - isShowingMyItems: root.isShowingMyItems; valid: model.valid; anchors.topMargin: 10; anchors.bottomMargin: 10; @@ -807,7 +801,8 @@ Rectangle { Rectangle { id: updatesAvailableBanner; - visible: root.numUpdatesAvailable > 0 && !root.isShowingMyItems; + visible: root.numUpdatesAvailable > 0 && + filterBar.primaryFilter_filterName !== "proofs"; anchors.bottom: parent.bottom; anchors.left: parent.left; anchors.right: parent.right; @@ -868,9 +863,8 @@ Rectangle { id: noItemsAlertContainer; visible: !purchasesContentsList.visible && root.purchasesReceived && - root.isShowingMyItems && filterBar.text === "" && - filterBar.primaryFilter_displayName === ""; + filterBar.primaryFilter_filterName === "proofs"; anchors.top: filterBarContainer.bottom; anchors.topMargin: 12; anchors.left: parent.left; @@ -918,7 +912,6 @@ Rectangle { id: noPurchasesAlertContainer; visible: !purchasesContentsList.visible && root.purchasesReceived && - !root.isShowingMyItems && filterBar.text === "" && filterBar.primaryFilter_displayName === ""; anchors.top: filterBarContainer.bottom; @@ -1051,7 +1044,9 @@ Rectangle { filterBar.text = message.filterText ? message.filterText : ""; break; case 'purchases_showMyItems': - root.isShowingMyItems = true; + filterBar.primaryFilter_filterName = "proofs"; + filterBar.primaryFilter_displayName = "Proofs"; + filterBar.primaryFilter_index = 6; break; case 'updateConnections': sendAsset.updateConnections(message.connections); From 93cff8dfdabe1691e5212867deb1386fbbeb6e08 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Wed, 31 Oct 2018 08:48:42 -0700 Subject: [PATCH 048/125] Corrected indentation. --- tools/auto-tester/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/auto-tester/CMakeLists.txt b/tools/auto-tester/CMakeLists.txt index 1befa6332e..c8c0a336d2 100644 --- a/tools/auto-tester/CMakeLists.txt +++ b/tools/auto-tester/CMakeLists.txt @@ -57,7 +57,7 @@ if (WIN32) ) # add a custom command to copy the SSL DLLs - add_custom_command( + add_custom_command( TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory "$ENV{VCPKG_ROOT}/installed/x64-windows/bin" "$" From 628ec8ec2be2346865a68b595a28749484e735e1 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 31 Oct 2018 10:09:01 -0700 Subject: [PATCH 049/125] Removed QmlCommerce.cpp, Ledger.cpp changes as requested --- .../qml/hifi/commerce/purchases/Purchases.qml | 11 ++++++++++- interface/src/commerce/Ledger.cpp | 10 +++------- interface/src/commerce/Ledger.h | 2 +- interface/src/commerce/QmlCommerce.cpp | 5 +++-- interface/src/commerce/QmlCommerce.h | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 09cea01ccf..7000b632e2 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -540,8 +540,17 @@ Rectangle { listView: purchasesContentsList; getPage: function () { console.debug('getPage', purchasesModel.listModelName, filterBar.primaryFilter_filterName, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage); + var editionFilter = ""; + var primaryFilter = ""; + + if (filterBar.primaryFilter_filterName === "proofs") { + editionFilter = "proofs"; + } else { + primaryFilter = filterBar.primaryFilter_filterName; + } Commerce.inventory( - filterBar.primaryFilter_filterName, + editionFilter, + primaryFilter, filterBar.text, purchasesModel.currentPageToRetrieve, purchasesModel.itemsPerPage diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 9703f7377f..f4bd7a84b3 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -152,14 +152,10 @@ void Ledger::balance(const QStringList& keys) { keysQuery("balance", "balanceSuccess", "balanceFailure"); } -void Ledger::inventory(const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage) { +void Ledger::inventory(const QString& editionFilter, const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage) { QJsonObject params; - if (typeFilter == "proofs") { - params["edition_filter"] = "proofs"; - } else { - params["type_filter"] = typeFilter; - } - + params["edition_filter"] = editionFilter; + params["type_filter"] = typeFilter; params["title_filter"] = titleFilter; params["page"] = page; params["per_page"] = perPage; diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 7bff9abe9b..427395ee11 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -29,7 +29,7 @@ public: bool receiveAt(const QString& hfc_key, const QString& signing_key, const QByteArray& locker); bool receiveAt(); void balance(const QStringList& keys); - void inventory(const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage); + void inventory(const QString& editionFilter, const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage); void history(const QStringList& keys, const int& pageNumber, const int& itemsPerPage); void account(); void updateLocation(const QString& asset_id, const QString& location, const bool& alsoUpdateSiblings = false, const bool controlledFailure = false); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 369b03d610..ffe89ffc5b 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -151,7 +151,8 @@ void QmlCommerce::balance() { } } -void QmlCommerce::inventory(const QString& typeFilter, +void QmlCommerce::inventory(const QString& editionFilter, + const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage) { @@ -159,7 +160,7 @@ void QmlCommerce::inventory(const QString& typeFilter, auto wallet = DependencyManager::get(); QStringList cachedPublicKeys = wallet->listPublicKeys(); if (!cachedPublicKeys.isEmpty()) { - ledger->inventory(typeFilter, titleFilter, page, perPage); + ledger->inventory(editionFilter, typeFilter, titleFilter, page, perPage); } } diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index a8eb45261d..2e3c0ec24d 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -74,7 +74,7 @@ protected: Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false); Q_INVOKABLE void balance(); - Q_INVOKABLE void inventory(const QString& typeFilter = QString(), const QString& titleFilter = QString(), const int& page = 1, const int& perPage = 20); + Q_INVOKABLE void inventory(const QString& editionFilter = QString(), const QString& typeFilter = QString(), const QString& titleFilter = QString(), const int& page = 1, const int& perPage = 20); Q_INVOKABLE void history(const int& pageNumber, const int& itemsPerPage = 100); Q_INVOKABLE void generateKeyPair(); Q_INVOKABLE void account(); From 0279eef3a4958896b403a562913fc9aa7841dedb Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 31 Oct 2018 10:13:32 -0700 Subject: [PATCH 050/125] improving interstitial loading bar --- scripts/system/interstitialPage.js | 83 ++++++++++++++++-------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index 670d21c7a7..34eee605ae 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -17,7 +17,8 @@ Script.include("/~/system/libraries/globals.js"); var DEBUG = false; var MIN_LOADING_PROGRESS = 3.6; - var TOTAL_LOADING_PROGRESS = 3.8; + var TOTAL_LOADING_PROGRESS = 3.7; + var FINAL_Y_DIMENSIONS = 2.8; var EPSILON = 0.05; var TEXTURE_EPSILON = 0.01; var isVisible = false; @@ -27,6 +28,7 @@ var MAX_LEFT_MARGIN = 1.9; var INNER_CIRCLE_WIDTH = 4.7; var DEFAULT_Z_OFFSET = 5.45; + var LOADING_IMAGE_WIDTH_PIXELS = 1024; var previousCameraMode = Camera.mode; var renderViewTask = Render.getConfig("RenderMainView"); @@ -182,12 +184,13 @@ parentID: anchorOverlay }); - var loadingBarPlacard = Overlays.addOverlay("image3d", { - name: "Loading-Bar-Placard", - localPosition: { x: 0.0, y: -0.99, z: 0.3 }, - url: Script.resourcesPath() + "images/loadingBar_placard.png", + + var loadingBarProgress = Overlays.addOverlay("image3d", { + name: "Loading-Bar-Progress", + localPosition: { x: 0.0, y: -0.86, z: 0.0 }, + url: Script.resourcesPath() + "images/loadingBar_progress.png", alpha: 1, - dimensions: { x: 4, y: 2.8 }, + dimensions: { x: 3.8, y: 2.8 }, visible: isVisible, emissive: true, ignoreRayIntersection: false, @@ -197,12 +200,12 @@ parentID: anchorOverlay }); - var loadingBarProgress = Overlays.addOverlay("image3d", { - name: "Loading-Bar-Progress", - localPosition: { x: 0.0, y: -0.90, z: 0.0 }, - url: Script.resourcesPath() + "images/loadingBar_progress.png", + var loadingBarPlacard = Overlays.addOverlay("image3d", { + name: "Loading-Bar-Placard", + localPosition: { x: 0.0, y: -0.99, z: 0.4 }, + url: Script.resourcesPath() + "images/loadingBar_placard.png", alpha: 1, - dimensions: { x: 3.8, y: 2.8 }, + dimensions: { x: 4, y: 2.8 }, visible: isVisible, emissive: true, ignoreRayIntersection: false, @@ -245,15 +248,7 @@ } function resetValues() { - var properties = { - localPosition: { x: 1.85, y: -0.935, z: 0.0 }, - dimensions: { - x: 0.1, - y: 2.8 - } - }; - - Overlays.editOverlay(loadingBarProgress, properties); + updateProgressBar(0.0); } function startInterstitialPage() { @@ -382,8 +377,8 @@ function updateOverlays(physicsEnabled) { if (isInterstitialOverlaysVisible !== !physicsEnabled && !physicsEnabled === true) { - // visible changed to true. - isInterstitialOverlaysVisible = !physicsEnabled; + // visible changed to true. + isInterstitialOverlaysVisible = !physicsEnabled; } var properties = { @@ -400,7 +395,7 @@ }; var loadingBarProperties = { - dimensions: { x: 0.0, y: 2.8 }, + dimensions: { x: 2.0, y: 2.8 }, visible: !physicsEnabled }; @@ -434,8 +429,8 @@ } if (isInterstitialOverlaysVisible !== !physicsEnabled && !physicsEnabled === false) { - // visible changed to false. - isInterstitialOverlaysVisible = !physicsEnabled; + // visible changed to false. + isInterstitialOverlaysVisible = !physicsEnabled; } } @@ -459,6 +454,27 @@ } } + function updateProgressBar(progress) { + var progressPercentage = progress / TOTAL_LOADING_PROGRESS; + var subImageWidth = progressPercentage * LOADING_IMAGE_WIDTH_PIXELS; + + var properties = { + localPosition: { x: (TOTAL_LOADING_PROGRESS / 2) - (currentProgress / 2), y: -0.86, z: 0.0 }, + dimensions: { + x: currentProgress, + y: FINAL_Y_DIMENSIONS * (subImageWidth / LOADING_IMAGE_WIDTH_PIXELS) + }, + subImage: { + x: 0.0, + y: 0.0, + width: subImageWidth, + height: 90 + } + }; + + Overlays.editOverlay(loadingBarProgress, properties); + } + var MAX_TEXTURE_STABILITY_COUNT = 30; var INTERVAL_PROGRESS = 0.04; function update() { @@ -503,15 +519,8 @@ } currentProgress = lerp(currentProgress, target, INTERVAL_PROGRESS); - var properties = { - localPosition: { x: (1.85 - (currentProgress / 2) - (-0.029 * (currentProgress / TOTAL_LOADING_PROGRESS))), y: -0.935, z: 0.0 }, - dimensions: { - x: currentProgress, - y: 2.8 - } - }; - Overlays.editOverlay(loadingBarProgress, properties); + updateProgressBar(currentProgress); if (errorConnectingToDomain) { updateOverlays(errorConnectingToDomain); @@ -542,17 +551,11 @@ } var whiteColor = { red: 255, green: 255, blue: 255 }; var greyColor = { red: 125, green: 125, blue: 125 }; + Overlays.mouseReleaseOnOverlay.connect(clickedOnOverlay); Overlays.hoverEnterOverlay.connect(onEnterOverlay); - Overlays.hoverLeaveOverlay.connect(onLeaveOverlay); - location.hostChanged.connect(domainChanged); - location.lookupResultsFinished.connect(function() { - Script.setTimeout(function() { - connectionToDomainFailed = !location.isConnected; - }, 1200); - }); Window.redirectErrorStateChanged.connect(toggleInterstitialPage); MyAvatar.sensorToWorldScaleChanged.connect(scaleInterstitialPage); From b5a48d31b6e92d1500a77b88b048d9a01991cfac Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 31 Oct 2018 10:18:04 -0700 Subject: [PATCH 051/125] Fix code-review spacing issue --- interface/resources/qml/controls-uit/FilterBar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/controls-uit/FilterBar.qml b/interface/resources/qml/controls-uit/FilterBar.qml index 71aa1f64ab..3d4e18ed48 100644 --- a/interface/resources/qml/controls-uit/FilterBar.qml +++ b/interface/resources/qml/controls-uit/FilterBar.qml @@ -261,7 +261,7 @@ Item { anchors.fill: parent; model: filterBarModel; delegate: Item { - width: parent.width; + width: parent.width; height: 50; Rectangle { id: dropDownButton; From 92888e9d3e1ff7533b470ca038ea9126027129be Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 31 Oct 2018 18:26:23 +0100 Subject: [PATCH 052/125] tooltip CR changes --- .../system/assets/data/createAppTooltips.json | 24 ------------------- scripts/system/edit.js | 7 ++++++ scripts/system/html/css/edit-style.css | 7 +++--- scripts/system/html/js/createAppTooltip.js | 10 ++++---- scripts/system/html/js/entityProperties.js | 6 ++++- 5 files changed, 20 insertions(+), 34 deletions(-) diff --git a/scripts/system/assets/data/createAppTooltips.json b/scripts/system/assets/data/createAppTooltips.json index 83ddcaa34b..5572779d46 100644 --- a/scripts/system/assets/data/createAppTooltips.json +++ b/scripts/system/assets/data/createAppTooltips.json @@ -196,12 +196,6 @@ "particleRadius": { "tooltip": "The size of each particle." }, - "radiusStart": { - "tooltip": "" - }, - "radiusFinish": { - "tooltip": "" - }, "radiusSpread": { "tooltip": "The spread in size that each particle is given, resulting in a variety of sizes." }, @@ -215,12 +209,6 @@ "alpha": { "tooltip": "The alpha of each particle." }, - "alphaStart": { - "tooltip": "" - }, - "alphaFinish": { - "tooltip": "" - }, "alphaSpread": { "tooltip": "The spread in alpha that each particle is given, resulting in a variety of alphas." }, @@ -233,12 +221,6 @@ "particleSpin": { "tooltip": "The spin of each particle in the system." }, - "spinStart": { - "tooltip": "" - }, - "spinFinish": { - "tooltip": "" - }, "spinSpread": { "tooltip": "The spread in spin that each particle is given, resulting in a variety of spins." }, @@ -248,15 +230,9 @@ "polarStart": { "tooltip": "The angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis." }, - "polarFinish": { - "tooltip": "" - }, "azimuthStart": { "tooltip": "The angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis." }, - "azimuthFinish": { - "tooltip": "" - }, "lightColor": { "tooltip": "The color of the light emitted.", "jsPropertyName": "color" diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6425806771..17fe9e2d40 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2490,6 +2490,13 @@ var PropertiesTool = function (opts) { } }; + HMD.displayModeChanged.connect(function() { + emitScriptEvent({ + type: 'hmdActiveChanged', + hmdActive: HMD.active, + }); + }); + createToolsWindow.webEventReceived.addListener(this, onWebEventReceived); webView.webEventReceived.connect(onWebEventReceived); diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index cf7124d9eb..7d2350e1c8 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1598,7 +1598,7 @@ input.rename-entity { padding-left: 2px; } -.createAppTooltip { +.create-app-tooltip { position: absolute; background: #6a6a6a; border: 1px solid black; @@ -1607,17 +1607,16 @@ input.rename-entity { padding: 5px; } -.createAppTooltip .createAppTooltipDescription { +.create-app-tooltip .create-app-tooltip-description { font-size: 12px; font-style: italic; color: #ffffff; } -.createAppTooltip .createAppTooltipJSAttribute { +.create-app-tooltip .create-app-tooltip-js-attribute { font-family: Raleway-SemiBold; font-size: 11px; color: #000000; bottom: 0; margin-top: 5px; } - diff --git a/scripts/system/html/js/createAppTooltip.js b/scripts/system/html/js/createAppTooltip.js index a42e5efe05..a5c961a7e2 100644 --- a/scripts/system/html/js/createAppTooltip.js +++ b/scripts/system/html/js/createAppTooltip.js @@ -58,15 +58,15 @@ CreateAppTooltip.prototype = { if (!TOOLTIP_DEBUG) { return; } - tooltipData = {tooltip: 'PLEASE SET THIS TOOLTIP'}; + tooltipData = { tooltip: 'PLEASE SET THIS TOOLTIP' }; } let elementRect = element.getBoundingClientRect(); let elTip = document.createElement("div"); - elTip.className = "createAppTooltip"; + elTip.className = "create-app-tooltip"; let elTipDescription = document.createElement("div"); - elTipDescription.className = "createAppTooltipDescription"; + elTipDescription.className = "create-app-tooltip-description"; elTipDescription.innerText = tooltipData.tooltip; elTip.appendChild(elTipDescription); @@ -77,7 +77,7 @@ CreateAppTooltip.prototype = { if (!tooltipData.skipJSProperty) { let elTipJSAttribute = document.createElement("div"); - elTipJSAttribute.className = "createAppTooltipJSAttribute"; + elTipJSAttribute.className = "create-app-tooltip-js-attribute"; elTipJSAttribute.innerText = `JS Attribute: ${jsAttribute}`; elTip.appendChild(elTipJSAttribute); } @@ -93,7 +93,7 @@ CreateAppTooltip.prototype = { // show above when otherwise out of bounds elTip.style.top = elementTop - CREATE_APP_TOOLTIP_OFFSET - elTip.clientHeight; } else { - // show tooltip on below by default + // show tooltip below by default elTip.style.top = desiredTooltipTop; } if ((window.innerWidth + window.pageXOffset) < (desiredTooltipLeft + elTip.clientWidth)) { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 78de0d075a..c7c75a243b 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -3165,8 +3165,13 @@ function loaded() { } else if (data.type === 'tooltipsReply') { createAppTooltip.setIsEnabled(!data.hmdActive); createAppTooltip.setTooltipData(data.tooltips); + } else if (data.type === 'hmdActiveChanged') { + createAppTooltip.setIsEnabled(!data.hmdActive); } }); + + // Request tooltips as soon as we can process a reply: + EventBridge.emitWebEvent(JSON.stringify({ type: 'tooltipsRequest' })); } // Server Script Status @@ -3397,6 +3402,5 @@ function loaded() { setTimeout(function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'propertiesPageReady' })); - EventBridge.emitWebEvent(JSON.stringify({ type: 'tooltipsRequest' })); }, 1000); } From b3539b101b6a5be02d1ef435c0ed930ffb19ce53 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 31 Oct 2018 11:04:30 -0700 Subject: [PATCH 053/125] faster loading bar interpolation once physics is enabled --- scripts/system/interstitialPage.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index 34eee605ae..e6e8d3d6d6 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -19,6 +19,7 @@ var MIN_LOADING_PROGRESS = 3.6; var TOTAL_LOADING_PROGRESS = 3.7; var FINAL_Y_DIMENSIONS = 2.8; + var BEGIN_Y_DIMENSIONS = 0.03; var EPSILON = 0.05; var TEXTURE_EPSILON = 0.01; var isVisible = false; @@ -457,12 +458,13 @@ function updateProgressBar(progress) { var progressPercentage = progress / TOTAL_LOADING_PROGRESS; var subImageWidth = progressPercentage * LOADING_IMAGE_WIDTH_PIXELS; + var subImageWidthPercentage = subImageWidth / LOADING_IMAGE_WIDTH_PIXELS; var properties = { localPosition: { x: (TOTAL_LOADING_PROGRESS / 2) - (currentProgress / 2), y: -0.86, z: 0.0 }, dimensions: { x: currentProgress, - y: FINAL_Y_DIMENSIONS * (subImageWidth / LOADING_IMAGE_WIDTH_PIXELS) + y: (subImageWidthPercentage * (FINAL_Y_DIMENSIONS - BEGIN_Y_DIMENSIONS)) + BEGIN_Y_DIMENSIONS }, subImage: { x: 0.0, @@ -477,6 +479,7 @@ var MAX_TEXTURE_STABILITY_COUNT = 30; var INTERVAL_PROGRESS = 0.04; + var INTERVAL_PROGRESS_PHYSICS_ENABLED = 0.2; function update() { var renderStats = Render.getConfig("Stats"); var physicsEnabled = Window.isPhysicsEnabled(); @@ -518,7 +521,7 @@ target = TOTAL_LOADING_PROGRESS; } - currentProgress = lerp(currentProgress, target, INTERVAL_PROGRESS); + currentProgress = lerp(currentProgress, target, (physicsEnabled ? INTERVAL_PROGRESS_PHYSICS_ENABLED : INTERVAL_PROGRESS)); updateProgressBar(currentProgress); From 8633dc0f663129728423997908347430b7bd4910 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 31 Oct 2018 12:34:20 -0700 Subject: [PATCH 054/125] disable interstitial on android --- libraries/networking/src/DomainHandler.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index ddd23339df..c0c5a4d059 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -231,7 +231,11 @@ private: QString _pendingPath; QTimer _settingsTimer; mutable ReadWriteLockable _interstitialModeSettingLock; - Setting::Handle _enableInterstitialMode{ "enableInterstitialMode", true }; +#ifdef Q_OS_ANDROID + Setting::Handle _enableInterstitialMode{ "enableInterstitialMode", false }; +#else + Setting::Handle _enableInterstitialMode { "enableInterstitialMode", true }; +#endif QSet _domainConnectionRefusals; bool _hasCheckedForAccessToken { false }; From 448be8847ff82b62232b2521a3ce035949d8f32b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 31 Oct 2018 13:23:58 -0700 Subject: [PATCH 055/125] Notifications are working! --- .../qml/hifi/commerce/purchases/Purchases.qml | 1 - .../qml/hifi/commerce/wallet/Wallet.qml | 26 +++- scripts/modules/appUi.js | 144 +++++++++++------- scripts/modules/request.js | 5 +- scripts/system/commerce/wallet.js | 46 +++--- scripts/system/pal.js | 16 +- scripts/system/tablet-goto.js | 16 +- 7 files changed, 155 insertions(+), 99 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index eeb98eeb8c..3f77a17ac0 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -91,7 +91,6 @@ Rectangle { if (result.status !== 'success') { console.log("Failed to get Available Updates", result.data.message); } else { - sendToScript({method: 'purchases_availableUpdatesReceived', numUpdates: result.data.updates.length }); root.numUpdatesAvailable = result.total_entries; } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index a958e62aad..e1e58bf7c7 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -99,6 +99,13 @@ Rectangle { } } + onActiveViewChanged: { + if (activeView === "walletHome") { + walletHomeButtonContainer.messagesWaiting = false; + sendToScript({method: 'clearShouldShowDotHistory'}); + } + } + HifiCommerceCommon.CommerceLightbox { id: lightboxPopup; visible: false; @@ -494,6 +501,7 @@ Rectangle { // "WALLET HOME" tab button Rectangle { id: walletHomeButtonContainer; + property bool messagesWaiting: false; visible: !walletSetup.visible; color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; @@ -514,6 +522,19 @@ Rectangle { color: WalletScriptingInterface.limitedCommerce ? hifi.colors.lightGray50 : ((root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse) ? hifi.colors.white : hifi.colors.blueHighlight); } + Rectangle { + id: recentActivityMessagesWaitingLight; + visible: parent.messagesWaiting; + anchors.right: homeTabIcon.left; + anchors.rightMargin: -4; + anchors.top: homeTabIcon.top; + anchors.topMargin: 16; + height: 10; + width: height; + radius: height/2; + color: "red"; + } + RalewaySemiBold { text: "RECENT ACTIVITY"; // Text size @@ -572,7 +593,7 @@ Rectangle { } Rectangle { - id: messagesWaitingLight; + id: exchangeMoneyMessagesWaitingLight; visible: parent.messagesWaiting; anchors.right: exchangeMoneyTabIcon.left; anchors.rightMargin: -4; @@ -889,6 +910,9 @@ Rectangle { case 'updateWearables': walletInventory.fromScript(message); break; + case 'updateRecentActivityMessageLight': + walletHomeButtonContainer.messagesWaiting = message.messagesWaiting; + break; default: console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); } diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index efb842a9bb..c340dfecd2 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -95,16 +95,16 @@ function AppUi(properties) { activeIcon: isWaiting ? that.activeMessagesButton : that.activeButton }); }; - that.notificationPollTimeout = false; - that.notificationPollTimeoutMs = 60000; - that.notificationPollEndpoint = false; - that.notificationPollStopPaginatingConditionMet = false; + that.notificationPollTimeout = [false]; + that.notificationPollTimeoutMs = [60000]; + that.notificationPollEndpoint = [false]; + that.notificationPollStopPaginatingConditionMet = [false]; that.notificationDataProcessPage = function (data) { return data; }; - that.notificationPollCallback = that.ignore; - that.notificationPollCaresAboutSince = false; - that.notificationInitialCallbackMade = false; + that.notificationPollCallback = [that.ignore]; + that.notificationPollCaresAboutSince = [false]; + that.notificationInitialCallbackMade = [false]; that.notificationDisplayBanner = function (message) { if (!that.isOpen) { Window.displayAnnouncement(message); @@ -149,73 +149,105 @@ function AppUi(properties) { // // START Notification Handling // + + var currentDataPageToRetrieve = []; + var concatenatedServerResponse = []; + for (var i = 0; i < that.notificationPollEndpoint.length; i++) { + currentDataPageToRetrieve[i] = 1; + concatenatedServerResponse[i] = new Array(); + } + + function requestCallback(error, response, optionalParams) { + var indexOfRequest = optionalParams.indexOfRequest; + var urlOfRequest = optionalParams.urlOfRequest; + + if (error || (response.status !== 'success')) { + print("Error: unable to get", urlOfRequest, error || response.status); + that.notificationPollTimeout[indexOfRequest] = Script.setTimeout( + that.notificationPoll(indexOfRequest), that.notificationPollTimeoutMs[indexOfRequest]); + return; + } + + if (!that.notificationPollStopPaginatingConditionMet[indexOfRequest] || + that.notificationPollStopPaginatingConditionMet[indexOfRequest](response)) { + that.notificationPollTimeout[indexOfRequest] = Script.setTimeout(function () { + that.notificationPoll(indexOfRequest); + }, that.notificationPollTimeoutMs[indexOfRequest]); + + var notificationData; + if (concatenatedServerResponse[indexOfRequest].length) { + notificationData = concatenatedServerResponse[indexOfRequest]; + } else { + notificationData = that.notificationDataProcessPage[indexOfRequest](response); + } + console.debug(that.buttonName, that.notificationPollEndpoint[indexOfRequest], + 'notification data for processing:', JSON.stringify(notificationData)); + that.notificationPollCallback[indexOfRequest](notificationData); + that.notificationInitialCallbackMade[indexOfRequest] = true; + currentDataPageToRetrieve[indexOfRequest] = 1; + concatenatedServerResponse[indexOfRequest] = new Array(); + } else { + concatenatedServerResponse[indexOfRequest] = + concatenatedServerResponse[indexOfRequest].concat(that.notificationDataProcessPage[indexOfRequest](response)); + currentDataPageToRetrieve[indexOfRequest]++; + request({ + json: true, + uri: (urlOfRequest + "&page=" + currentDataPageToRetrieve[indexOfRequest]) + }, requestCallback, optionalParams); + } + } + + var METAVERSE_BASE = Account.metaverseServerURL; - var currentDataPageToRetrieve = 1; - var concatenatedServerResponse = new Array(); - that.notificationPoll = function () { - if (!that.notificationPollEndpoint) { + that.notificationPoll = function (i) { + if (!that.notificationPollEndpoint[i]) { return; } // User is "appearing offline" or is offline if (GlobalServices.findableBy === "none" || Account.username === "") { - that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); + that.notificationPollTimeout[i] = Script.setTimeout( + that.notificationPoll(i), that.notificationPollTimeoutMs[i]); return; } - var url = METAVERSE_BASE + that.notificationPollEndpoint; + var url = METAVERSE_BASE + that.notificationPollEndpoint[i]; - var settingsKey = "notifications/" + that.buttonName + "/lastPoll"; + var settingsKey = "notifications/" + that.notificationPollEndpoint[i] + "/lastPoll"; var currentTimestamp = new Date().getTime(); var lastPollTimestamp = Settings.getValue(settingsKey, currentTimestamp); - if (that.notificationPollCaresAboutSince) { - url = url + "&since=" + lastPollTimestamp/1000; + if (that.notificationPollCaresAboutSince[i]) { + url = url + "&since=" + lastPollTimestamp / 1000; } Settings.setValue(settingsKey, currentTimestamp); console.debug(that.buttonName, 'polling for notifications at endpoint', url); - function requestCallback(error, response) { - if (error || (response.status !== 'success')) { - print("Error: unable to get", url, error || response.status); - that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); - return; - } - - if (!that.notificationPollStopPaginatingConditionMet || that.notificationPollStopPaginatingConditionMet(response)) { - that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); - - var notificationData; - if (concatenatedServerResponse.length) { - notificationData = concatenatedServerResponse; - } else { - notificationData = that.notificationDataProcessPage(response); - } - console.debug(that.buttonName, 'notification data for processing:', JSON.stringify(notificationData)); - that.notificationPollCallback(notificationData); - that.notificationInitialCallbackMade = true; - currentDataPageToRetrieve = 1; - concatenatedServerResponse = new Array(); - } else { - concatenatedServerResponse = concatenatedServerResponse.concat(that.notificationDataProcessPage(response)); - currentDataPageToRetrieve++; - request({ json: true, uri: (url + "&page=" + currentDataPageToRetrieve) }, requestCallback); - } - } - - request({ json: true, uri: url }, requestCallback); + request({ + json: true, + uri: url + }, + requestCallback, + { + indexOfRequest: i, + urlOfRequest: url + }); }; // This won't do anything if there isn't a notification endpoint set - that.notificationPoll(); + for (i = 0; i < that.notificationPollEndpoint.length; i++) { + that.notificationPoll(i); + } function restartNotificationPoll() { - that.notificationInitialCallbackMade = false; - if (that.notificationPollTimeout) { - Script.clearTimeout(that.notificationPollTimeout); - that.notificationPollTimeout = false; + for (var j = 0; j < that.notificationPollEndpoint.length; j++) { + that.notificationInitialCallbackMade[j] = false; + if (that.notificationPollTimeout[j]) { + Script.clearTimeout(that.notificationPollTimeout[j]); + that.notificationPollTimeout[j] = false; + } + that.notificationPoll(j); } - that.notificationPoll(); } // // END Notification Handling @@ -322,9 +354,11 @@ function AppUi(properties) { } that.tablet.removeButton(that.button); } - if (that.notificationPollTimeout) { - Script.clearInterval(that.notificationPollTimeout); - that.notificationPollTimeout = false; + for (var i = 0; i < that.notificationPollTimeout.length; i++) { + if (that.notificationPollTimeout[i]) { + Script.clearInterval(that.notificationPollTimeout[i]); + that.notificationPollTimeout[i] = false; + } } }; // Set up the handlers. @@ -333,7 +367,7 @@ function AppUi(properties) { Script.scriptEnding.connect(that.onScriptEnding); GlobalServices.findableByChanged.connect(restartNotificationPoll); GlobalServices.myUsernameChanged.connect(restartNotificationPoll); - if (that.buttonName == Settings.getValue("startUpApp")) { + if (that.buttonName === Settings.getValue("startUpApp")) { Settings.setValue("startUpApp", ""); Script.setTimeout(function () { that.open(); diff --git a/scripts/modules/request.js b/scripts/modules/request.js index d0037f9b43..37f3ac0d7b 100644 --- a/scripts/modules/request.js +++ b/scripts/modules/request.js @@ -18,7 +18,8 @@ module.exports = { // ------------------------------------------------------------------ - request: function (options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request. + // cb(error, responseOfCorrectContentType, optionalCallbackParameter) of url. A subset of npm request. + request: function (options, callback, optionalCallbackParameter) { var httpRequest = new XMLHttpRequest(), key; // QT bug: apparently doesn't handle onload. Workaround using readyState. httpRequest.onreadystatechange = function () { @@ -38,7 +39,7 @@ module.exports = { if (error) { response = { statusCode: httpRequest.status }; } - callback(error, response); + callback(error, response, optionalCallbackParameter); } }; if (typeof options === 'string') { diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 353145035e..5396014866 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -506,10 +506,6 @@ function fromQml(message) { ui.tablet.sendToQml({ method: 'updateWearables', wornWearables: currentlyWornWearables }); break; - case 'purchases_availableUpdatesReceived': - shouldShowDot = message.numUpdates > 0; - ui.messagesWaiting(shouldShowDot && !ui.isOpen); - break; case 'purchases_walletNotSetUp': ui.tablet.sendToQml({ method: 'updateWalletReferrer', @@ -525,6 +521,10 @@ function fromQml(message) { openMarketplace(itemId); } break; + case 'clearShouldShowDotHistory': + shouldShowDotHistory = false; + ui.messagesWaiting(shouldShowDotUpdates || shouldShowDotHistory); + break; case 'http.request': // Handled elsewhere, don't log. break; @@ -541,8 +541,13 @@ function walletOpened() { triggerMapping.enable(); triggerPressMapping.enable(); isWired = true; - shouldShowDot = false; - ui.messagesWaiting(shouldShowDot); + + if (shouldShowDotHistory) { + ui.sendMessage({ + method: 'updateRecentActivityMessageLight', + messagesWaiting: shouldShowDotHistory + }); + } } function walletClosed() { @@ -557,20 +562,20 @@ function notificationDataProcessPageHistory(data) { return data.data.history; } -var shouldShowDot = false; +var shouldShowDotUpdates = false; function notificationPollCallbackUpdates(updatesArray) { - shouldShowDot = shouldShowDot || updatesArray.length > 0; - ui.messagesWaiting(shouldShowDot && !ui.isOpen); + shouldShowDotUpdates = shouldShowDotUpdates || updatesArray.length > 0; + ui.messagesWaiting(shouldShowDotUpdates || shouldShowDotHistory); if (updatesArray.length > 0) { var message; - if (!ui.notificationInitialCallbackMade) { + if (!ui.notificationInitialCallbackMade[0]) { message = updatesArray.length + " of your purchased items " + (updatesArray.length === 1 ? "has an update " : "have updates ") + "available. Open WALLET to update."; ui.notificationDisplayBanner(message); - ui.notificationPollCaresAboutSince = true; + ui.notificationPollCaresAboutSince[0] = true; } else { for (var i = 0; i < updatesArray.length; i++) { message = "Update available for \"" + @@ -581,15 +586,16 @@ function notificationPollCallbackUpdates(updatesArray) { } } } +var shouldShowDotHistory = false; function notificationPollCallbackHistory(historyArray) { if (!ui.isOpen) { var notificationCount = historyArray.length; - shouldShowDot = shouldShowDot || notificationCount > 0; - ui.messagesWaiting(shouldShowDot); + shouldShowDotHistory = shouldShowDotHistory || notificationCount > 0; + ui.messagesWaiting(shouldShowDotUpdates || shouldShowDotHistory); if (notificationCount > 0) { var message; - if (!ui.notificationInitialCallbackMade) { + if (!ui.notificationInitialCallbackMade[1]) { message = "You have " + notificationCount + " unread wallet " + "transaction" + (notificationCount === 1 ? "" : "s") + ". Open WALLET to see all activity."; ui.notificationDisplayBanner(message); @@ -605,8 +611,8 @@ function notificationPollCallbackHistory(historyArray) { } function isReturnedDataEmptyUpdates(data) { - var historyArray = data.data.history; - return historyArray.length === 0; + var updatesArray = data.data.updates; + return updatesArray.length === 0; } function isReturnedDataEmptyHistory(data) { @@ -657,20 +663,12 @@ function startup() { onOpened: walletOpened, onClosed: walletClosed, onMessage: fromQml, -/* Gotta re-add all this stuff once I get it working notificationPollEndpoint: ["/api/v1/commerce/available_updates?per_page=10", "/api/v1/commerce/history?per_page=10"], notificationPollTimeoutMs: [NOTIFICATION_POLL_TIMEOUT, NOTIFICATION_POLL_TIMEOUT], notificationDataProcessPage: [notificationDataProcessPageUpdates, notificationDataProcessPageHistory], notificationPollCallback: [notificationPollCallbackUpdates, notificationPollCallbackHistory], notificationPollStopPaginatingConditionMet: [isReturnedDataEmptyUpdates, isReturnedDataEmptyHistory], notificationPollCaresAboutSince: [false, true] -*/ - notificationPollEndpoint: "/api/v1/commerce/available_updates?per_page=10", - notificationPollTimeoutMs: 300000, - notificationDataProcessPage: notificationDataProcessPageUpdates, - notificationPollCallback: notificationPollCallbackUpdates, - notificationPollStopPaginatingConditionMet: isReturnedDataEmptyUpdates, - notificationPollCaresAboutSince: false }); GlobalServices.myUsernameChanged.connect(onUsernameChanged); installMarketplaceItemTester(); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index a2ebae1a33..341ce9ebc8 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -844,7 +844,7 @@ function notificationPollCallback(connectionsArray) { newOnlineUsers++; storedOnlineUsers[user.username] = user; - if (!ui.isOpen && ui.notificationInitialCallbackMade) { + if (!ui.isOpen && ui.notificationInitialCallbackMade[0]) { message = user.username + " is available in " + user.location.root.name + ". Open PEOPLE to join them."; ui.notificationDisplayBanner(message); @@ -868,7 +868,7 @@ function notificationPollCallback(connectionsArray) { shouldShowDot: shouldShowDot }); - if (newOnlineUsers > 0 && !ui.notificationInitialCallbackMade) { + if (newOnlineUsers > 0 && !ui.notificationInitialCallbackMade[0]) { message = newOnlineUsers + " of your connections " + (newOnlineUsers === 1 ? "is" : "are") + " available online. Open PEOPLE to join them."; ui.notificationDisplayBanner(message); @@ -889,12 +889,12 @@ function startup() { onOpened: palOpened, onClosed: off, onMessage: fromQml, - notificationPollEndpoint: "/api/v1/users?filter=connections&status=online&per_page=10", - notificationPollTimeoutMs: 60000, - notificationDataProcessPage: notificationDataProcessPage, - notificationPollCallback: notificationPollCallback, - notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - notificationPollCaresAboutSince: false + notificationPollEndpoint: ["/api/v1/users?filter=connections&status=online&per_page=10"], + notificationPollTimeoutMs: [60000], + notificationDataProcessPage: [notificationDataProcessPage], + notificationPollCallback: [notificationPollCallback], + notificationPollStopPaginatingConditionMet: [isReturnedDataEmpty], + notificationPollCaresAboutSince: [false] }); Window.domainChanged.connect(clearLocalQMLDataAndClosePAL); Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 6d8ba3a927..94117fd9ea 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -37,7 +37,7 @@ function notificationPollCallback(userStoriesArray) { // pingPong = !pingPong; var totalNewStories = 0; - var shouldNotifyIndividually = !ui.isOpen && ui.notificationInitialCallbackMade; + var shouldNotifyIndividually = !ui.isOpen && ui.notificationInitialCallbackMade[0]; userStoriesArray.forEach(function (story) { if (story.audience !== "for_connections" && story.audience !== "for_feed") { @@ -91,7 +91,7 @@ function notificationPollCallback(userStoriesArray) { shouldShowDot = totalNewStories > 0 || (totalStories > 0 && shouldShowDot); ui.messagesWaiting(shouldShowDot && !ui.isOpen); - if (totalStories > 0 && !ui.isOpen && !ui.notificationInitialCallbackMade) { + if (totalStories > 0 && !ui.isOpen && !ui.notificationInitialCallbackMade[0]) { message = "There " + (totalStories === 1 ? "is " : "are ") + totalStories + " event" + (totalStories === 1 ? "" : "s") + " to know about. " + "Open GOTO to see " + (totalStories === 1 ? "it" : "them") + "."; @@ -122,12 +122,12 @@ function startup() { sortOrder: 8, onOpened: gotoOpened, home: GOTO_QML_SOURCE, - notificationPollEndpoint: endpoint, - notificationPollTimeoutMs: 60000, - notificationDataProcessPage: notificationDataProcessPage, - notificationPollCallback: notificationPollCallback, - notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - notificationPollCaresAboutSince: false + notificationPollEndpoint: [endpoint], + notificationPollTimeoutMs: [60000], + notificationDataProcessPage: [notificationDataProcessPage], + notificationPollCallback: [notificationPollCallback], + notificationPollStopPaginatingConditionMet: [isReturnedDataEmpty], + notificationPollCaresAboutSince: [false] }); } From c4c4d2c98deebd8f5f5165e1a972997476f8a9de Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 31 Oct 2018 13:07:48 -0700 Subject: [PATCH 056/125] possibly fix mac issues --- libraries/gpu/src/gpu/MipGeneration.slh | 2 +- libraries/render-utils/src/debug_deferred_buffer.slf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/MipGeneration.slh b/libraries/gpu/src/gpu/MipGeneration.slh index bc8dd39042..b5d4ab3303 100644 --- a/libraries/gpu/src/gpu/MipGeneration.slh +++ b/libraries/gpu/src/gpu/MipGeneration.slh @@ -13,7 +13,7 @@ <@include gpu/ShaderConstants.h@> -layout(binding=GPU_TEXTURE_MIP_CREATION_INPUT) uniform sampler2D texMap; +LAYOUT(binding=GPU_TEXTURE_MIP_CREATION_INPUT) uniform sampler2D texMap; in vec2 varTexCoord0; diff --git a/libraries/render-utils/src/debug_deferred_buffer.slf b/libraries/render-utils/src/debug_deferred_buffer.slf index c6e3c49e54..ccbe5c491f 100644 --- a/libraries/render-utils/src/debug_deferred_buffer.slf +++ b/libraries/render-utils/src/debug_deferred_buffer.slf @@ -25,7 +25,7 @@ LAYOUT(binding=RENDER_UTILS_TEXTURE_SHADOW) uniform sampler2DArrayShadow shadowM <@include debug_deferred_buffer_shared.slh@> -layout(std140, binding=RENDER_UTILS_BUFFER_DEBUG_DEFERRED_PARAMS) uniform parametersBuffer { +LAYOUT_STD140(binding=RENDER_UTILS_BUFFER_DEBUG_DEFERRED_PARAMS) uniform parametersBuffer { DebugParameters parameters; }; From a1276ba8995dc91ada1f25e491c11207958e5702 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 31 Oct 2018 13:31:29 -0700 Subject: [PATCH 057/125] Don't poll for history notifs if limitedCommerce is true --- scripts/system/commerce/wallet.js | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 5396014866..2ae30b5b35 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -655,6 +655,22 @@ var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; var NOTIFICATION_POLL_TIMEOUT = 300000; var ui; function startup() { + var notificationPollEndpointArray = ["/api/v1/commerce/available_updates?per_page=10"]; + var notificationPollTimeoutMsArray = [NOTIFICATION_POLL_TIMEOUT]; + var notificationDataProcessPageArray = [notificationDataProcessPageUpdates]; + var notificationPollCallbackArray = [notificationPollCallbackUpdates]; + var notificationPollStopPaginatingConditionMetArray = [isReturnedDataEmptyUpdates]; + var notificationPollCaresAboutSinceArray = [false]; + + if (!WalletScriptingInterface.limitedCommerce) { + notificationPollEndpointArray[1] = "/api/v1/commerce/history?per_page=10"; + notificationPollTimeoutMsArray[1] = NOTIFICATION_POLL_TIMEOUT; + notificationDataProcessPageArray[1] = notificationDataProcessPageHistory; + notificationPollCallbackArray[1] = notificationPollCallbackHistory; + notificationPollStopPaginatingConditionMetArray[1] = isReturnedDataEmptyHistory; + notificationPollCaresAboutSinceArray[1] = true; + } + ui = new AppUi({ buttonName: BUTTON_NAME, buttonPrefix: "wallet-", @@ -663,12 +679,12 @@ function startup() { onOpened: walletOpened, onClosed: walletClosed, onMessage: fromQml, - notificationPollEndpoint: ["/api/v1/commerce/available_updates?per_page=10", "/api/v1/commerce/history?per_page=10"], - notificationPollTimeoutMs: [NOTIFICATION_POLL_TIMEOUT, NOTIFICATION_POLL_TIMEOUT], - notificationDataProcessPage: [notificationDataProcessPageUpdates, notificationDataProcessPageHistory], - notificationPollCallback: [notificationPollCallbackUpdates, notificationPollCallbackHistory], - notificationPollStopPaginatingConditionMet: [isReturnedDataEmptyUpdates, isReturnedDataEmptyHistory], - notificationPollCaresAboutSince: [false, true] + notificationPollEndpoint: notificationPollEndpointArray, + notificationPollTimeoutMs: notificationPollTimeoutMsArray, + notificationDataProcessPage: notificationDataProcessPageArray, + notificationPollCallback: notificationPollCallbackArray, + notificationPollStopPaginatingConditionMet: notificationPollStopPaginatingConditionMetArray, + notificationPollCaresAboutSince: notificationPollCaresAboutSinceArray }); GlobalServices.myUsernameChanged.connect(onUsernameChanged); installMarketplaceItemTester(); From 41ec026f8d521d10f10f9f27718ffb65f1462cbd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 29 Oct 2018 16:16:16 -0700 Subject: [PATCH 058/125] cleanup packet times, avoid ambiguous RTT calcs, allow fast re-transmit --- .../networking/src/udt/CongestionControl.h | 2 + libraries/networking/src/udt/Connection.cpp | 22 +- libraries/networking/src/udt/SendQueue.cpp | 1 + libraries/networking/src/udt/TCPVegasCC.cpp | 246 +++++++++++------- libraries/networking/src/udt/TCPVegasCC.h | 19 +- 5 files changed, 177 insertions(+), 113 deletions(-) diff --git a/libraries/networking/src/udt/CongestionControl.h b/libraries/networking/src/udt/CongestionControl.h index 7093e8bd96..bfe7f552d1 100644 --- a/libraries/networking/src/udt/CongestionControl.h +++ b/libraries/networking/src/udt/CongestionControl.h @@ -45,8 +45,10 @@ public: virtual void onTimeout() {} virtual void onPacketSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) {} + virtual void onPacketReSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) {} virtual int estimatedTimeout() const = 0; + protected: void setMSS(int mss) { _mss = mss; } virtual void setInitialSendSequenceNumber(SequenceNumber seqNum) = 0; diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index 24e294881a..4798288a18 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -195,7 +195,7 @@ void Connection::recordSentPackets(int wireSize, int payloadSize, void Connection::recordRetransmission(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) { _stats.record(ConnectionStats::Stats::Retransmission); - _congestionControl->onPacketSent(wireSize, seqNum, timePoint); + _congestionControl->onPacketReSent(wireSize, seqNum, timePoint); } void Connection::sendACK() { @@ -303,7 +303,7 @@ void Connection::processControl(ControlPacketPointer controlPacket) { // where the other end expired our connection. Let's reset. #ifdef UDT_CONNECTION_DEBUG - qCDebug(networking) << "Got HandshakeRequest from" << _destination << ", stopping SendQueue"; + qCDebug(networking) << "Got HandshakeRequest from" << _destination << ", stopping SendQueue"; #endif _hasReceivedHandshakeACK = false; stopSendQueue(); @@ -327,19 +327,19 @@ void Connection::processACK(ControlPacketPointer controlPacket) { return; } - if (ack <= _lastReceivedACK) { + if (ack < _lastReceivedACK) { // this is an out of order ACK, bail - // or - // processing an already received ACK, bail return; } - - _lastReceivedACK = ack; - - // ACK the send queue so it knows what was received - getSendQueue().ack(ack); - + if (ack > _lastReceivedACK) { + // this is not a repeated ACK, so update our member and tell the send queue + _lastReceivedACK = ack; + + // ACK the send queue so it knows what was received + getSendQueue().ack(ack); + } + // give this ACK to the congestion control and update the send queue parameters updateCongestionControlAndSendQueue([this, ack, &controlPacket] { if (_congestionControl->onACK(ack, controlPacket->getReceiveTime())) { diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index b1dfb9a8cf..9cba4970ac 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -481,6 +481,7 @@ bool SendQueue::isInactive(bool attemptedToSendPacket) { auto cvStatus = _emptyCondition.wait_for(locker, EMPTY_QUEUES_INACTIVE_TIMEOUT); if (cvStatus == std::cv_status::timeout && (_packets.isEmpty() || isFlowWindowFull()) && _naks.isEmpty()) { + #ifdef UDT_CONNECTION_DEBUG qCDebug(networking) << "SendQueue to" << _destination << "has been empty for" << EMPTY_QUEUES_INACTIVE_TIMEOUT.count() diff --git a/libraries/networking/src/udt/TCPVegasCC.cpp b/libraries/networking/src/udt/TCPVegasCC.cpp index 4842e5a204..f2119237c2 100644 --- a/libraries/networking/src/udt/TCPVegasCC.cpp +++ b/libraries/networking/src/udt/TCPVegasCC.cpp @@ -27,112 +27,106 @@ TCPVegasCC::TCPVegasCC() { _baseRTT = std::numeric_limits::max(); } +bool TCPVegasCC::calculateRTT(p_high_resolution_clock::time_point sendTime, p_high_resolution_clock::time_point receiveTime) { + // calculate the RTT (receive time - time ACK sent) + int lastRTT = duration_cast(receiveTime - sendTime).count(); + + const int MAX_RTT_SAMPLE_MICROSECONDS = 10000000; + + if (lastRTT < 0) { + Q_ASSERT_X(false, __FUNCTION__, "calculated an RTT that is not > 0"); + return false; + } else if (lastRTT == 0) { + // we do not allow a zero microsecond RTT (as per the UNIX kernel implementation of TCP Vegas) + lastRTT = 1; + } else if (lastRTT > MAX_RTT_SAMPLE_MICROSECONDS) { + // we cap the lastRTT to MAX_RTT_SAMPLE_MICROSECONDS to avoid overflows in window size calculations + lastRTT = MAX_RTT_SAMPLE_MICROSECONDS; + } + + if (_ewmaRTT == -1) { + // first RTT sample - set _ewmaRTT to the value and set the variance to half the value + _ewmaRTT = lastRTT; + _rttVariance = lastRTT / 2; + } else { + // This updates the RTT using exponential weighted moving average + // This is the Jacobson's forumla for RTT estimation + // http://www.mathcs.emory.edu/~cheung/Courses/455/Syllabus/7-transport/Jacobson-88.pdf + + // Estimated RTT = (1 - x)(estimatedRTT) + (x)(sampleRTT) + // (where x = 0.125 via Jacobson) + + // Deviation = (1 - x)(deviation) + x |sampleRTT - estimatedRTT| + // (where x = 0.25 via Jacobson) + + static const int RTT_ESTIMATION_ALPHA = 8; + static const int RTT_ESTIMATION_VARIANCE_ALPHA = 4; + + _ewmaRTT = (_ewmaRTT * (RTT_ESTIMATION_ALPHA - 1) + lastRTT) / RTT_ESTIMATION_ALPHA; + _rttVariance = (_rttVariance * (RTT_ESTIMATION_VARIANCE_ALPHA- 1) + + abs(lastRTT - _ewmaRTT)) / RTT_ESTIMATION_VARIANCE_ALPHA; + } + + // keep track of the lowest RTT during connection + _baseRTT = std::min(_baseRTT, lastRTT); + + // find the min RTT during the last RTT + _currentMinRTT = std::min(_currentMinRTT, lastRTT); + + // add 1 to the number of RTT samples collected during this RTT window + ++_numRTTs; + + return true; +} + bool TCPVegasCC::onACK(SequenceNumber ack, p_high_resolution_clock::time_point receiveTime) { - auto it = _sentPacketTimes.find(ack); auto previousAck = _lastACK; _lastACK = ack; - if (it != _sentPacketTimes.end()) { + bool wasDuplicateACK = (ack == previousAck); - // calculate the RTT (receive time - time ACK sent) - int lastRTT = duration_cast(receiveTime - it->second).count(); + auto it = std::find_if(_sentPacketDatas.begin(), _sentPacketDatas.end(), [ack](SentPacketData& packetTime){ + return packetTime.sequenceNumber == ack; + }); - const int MAX_RTT_SAMPLE_MICROSECONDS = 10000000; + if (!wasDuplicateACK && it != _sentPacketDatas.end()) { + // check if we can unambigiously calculate an RTT from this ACK - if (lastRTT < 0) { - Q_ASSERT_X(false, __FUNCTION__, "calculated an RTT that is not > 0"); + // for that to be the case, + // any of the packets this ACK covers (from the current ACK back to our previous ACK) + // must not have been re-sent + bool canBeUsedForRTT = std::none_of(_sentPacketDatas.begin(), _sentPacketDatas.end(), + [ack, previousAck](SentPacketData& sentPacketData) + { + return sentPacketData.sequenceNumber > previousAck + && sentPacketData.sequenceNumber <= ack + && sentPacketData.wasResent; + }); + + auto sendTime = it->timePoint; + + // remove all sent packet times up to this sequence number + it = _sentPacketDatas.erase(_sentPacketDatas.begin(), it + 1); + + // if we can use this ACK for an RTT calculation then do so + // returning false if we calculate an invalid RTT + if (canBeUsedForRTT && !calculateRTT(sendTime, receiveTime)) { return false; - } else if (lastRTT == 0) { - // we do not allow a zero microsecond RTT (as per the UNIX kernel implementation of TCP Vegas) - lastRTT = 1; - } else if (lastRTT > MAX_RTT_SAMPLE_MICROSECONDS) { - // we cap the lastRTT to MAX_RTT_SAMPLE_MICROSECONDS to avoid overflows in window size calculations - lastRTT = MAX_RTT_SAMPLE_MICROSECONDS; } + } - if (_ewmaRTT == -1) { - // first RTT sample - set _ewmaRTT to the value and set the variance to half the value - _ewmaRTT = lastRTT; - _rttVariance = lastRTT / 2; - } else { - // This updates the RTT using exponential weighted moving average - // This is the Jacobson's forumla for RTT estimation - // http://www.mathcs.emory.edu/~cheung/Courses/455/Syllabus/7-transport/Jacobson-88.pdf - - // Estimated RTT = (1 - x)(estimatedRTT) + (x)(sampleRTT) - // (where x = 0.125 via Jacobson) - - // Deviation = (1 - x)(deviation) + x |sampleRTT - estimatedRTT| - // (where x = 0.25 via Jacobson) - - static const int RTT_ESTIMATION_ALPHA = 8; - static const int RTT_ESTIMATION_VARIANCE_ALPHA = 4; - - _ewmaRTT = (_ewmaRTT * (RTT_ESTIMATION_ALPHA - 1) + lastRTT) / RTT_ESTIMATION_ALPHA; - _rttVariance = (_rttVariance * (RTT_ESTIMATION_VARIANCE_ALPHA- 1) - + abs(lastRTT - _ewmaRTT)) / RTT_ESTIMATION_VARIANCE_ALPHA; - } - - // add 1 to the number of ACKs during this RTT - ++_numACKs; - - // keep track of the lowest RTT during connection - _baseRTT = std::min(_baseRTT, lastRTT); - - // find the min RTT during the last RTT - _currentMinRTT = std::min(_currentMinRTT, lastRTT); - - auto sinceLastAdjustment = duration_cast(p_high_resolution_clock::now() - _lastAdjustmentTime).count(); - if (sinceLastAdjustment >= _ewmaRTT) { - performCongestionAvoidance(ack); - } - - // remove this sent packet time from the hash - _sentPacketTimes.erase(it); + auto sinceLastAdjustment = duration_cast(p_high_resolution_clock::now() - _lastAdjustmentTime).count(); + if (sinceLastAdjustment >= _ewmaRTT) { + performCongestionAvoidance(ack); } ++_numACKSinceFastRetransmit; // perform the fast re-transmit check if this is a duplicate ACK or if this is the first or second ACK // after a previous fast re-transmit - if (ack == previousAck || _numACKSinceFastRetransmit < 3) { - // we may need to re-send ackNum + 1 if it has been more than our estimated timeout since it was sent - - auto it = _sentPacketTimes.find(ack + 1); - if (it != _sentPacketTimes.end()) { - - auto now = p_high_resolution_clock::now(); - auto sinceSend = duration_cast(now - it->second).count(); - - if (sinceSend >= estimatedTimeout()) { - // break out of slow start, we've decided this is loss - _slowStart = false; - - // reset the fast re-transmit counter - _numACKSinceFastRetransmit = 0; - - // return true so the caller knows we needed a fast re-transmit - return true; - } - } - - // if this is the 3rd duplicate ACK, we fallback to Reno's fast re-transmit - static const int RENO_FAST_RETRANSMIT_DUPLICATE_COUNT = 3; - - ++_duplicateACKCount; - - if (ack == previousAck && _duplicateACKCount == RENO_FAST_RETRANSMIT_DUPLICATE_COUNT) { - // break out of slow start, we just hit loss - _slowStart = false; - - // reset our fast re-transmit counters - _numACKSinceFastRetransmit = 0; - _duplicateACKCount = 0; - - // return true so the caller knows we needed a fast re-transmit - return true; - } + if (wasDuplicateACK || _numACKSinceFastRetransmit < 3) { + return needsFastRetransmit(ack, wasDuplicateACK); } else { _duplicateACKCount = 0; } @@ -141,6 +135,49 @@ bool TCPVegasCC::onACK(SequenceNumber ack, p_high_resolution_clock::time_point r return false; } +bool TCPVegasCC::needsFastRetransmit(SequenceNumber ack, bool wasDuplicateACK) { + // we may need to re-send ackNum + 1 if it has been more than our estimated timeout since it was sent + + auto nextIt = std::find_if(_sentPacketDatas.begin(), _sentPacketDatas.end(), [ack](SentPacketData& packetTime){ + return packetTime.sequenceNumber == ack + 1; + }); + + if (nextIt != _sentPacketDatas.end()) { + auto now = p_high_resolution_clock::now(); + auto sinceSend = duration_cast(now - nextIt->timePoint).count(); + + if (sinceSend >= estimatedTimeout()) { + // break out of slow start, we've decided this is loss + _slowStart = false; + + // reset the fast re-transmit counter + _numACKSinceFastRetransmit = 0; + + // return true so the caller knows we needed a fast re-transmit + return true; + } + } + + // if this is the 3rd duplicate ACK, we fallback to Reno's fast re-transmit + static const int RENO_FAST_RETRANSMIT_DUPLICATE_COUNT = 3; + + ++_duplicateACKCount; + + if (wasDuplicateACK && _duplicateACKCount == RENO_FAST_RETRANSMIT_DUPLICATE_COUNT) { + // break out of slow start, we just hit loss + _slowStart = false; + + // reset our fast re-transmit counters + _numACKSinceFastRetransmit = 0; + _duplicateACKCount = 0; + + // return true so the caller knows we needed a fast re-transmit + return true; + } + + return false; +} + void TCPVegasCC::performCongestionAvoidance(udt::SequenceNumber ack) { static int VEGAS_ALPHA_SEGMENTS = 4; static int VEGAS_BETA_SEGMENTS = 6; @@ -158,7 +195,7 @@ void TCPVegasCC::performCongestionAvoidance(udt::SequenceNumber ack) { int64_t windowSizeDiff = (int64_t) _congestionWindowSize * (rtt - _baseRTT) / _baseRTT; - if (_numACKs <= 2) { + if (_numRTTs <= 2) { performRenoCongestionAvoidance(ack); } else { if (_slowStart) { @@ -209,7 +246,7 @@ void TCPVegasCC::performCongestionAvoidance(udt::SequenceNumber ack) { _currentMinRTT = std::numeric_limits::max(); // reset our count of collected RTT samples - _numACKs = 0; + _numRTTs = 0; } @@ -230,29 +267,29 @@ void TCPVegasCC::performRenoCongestionAvoidance(SequenceNumber ack) { return; } - int numAcked = _numACKs; + int numRTTCollected = _numRTTs; if (_slowStart) { // while in slow start we grow the congestion window by the number of ACKed packets // allowing it to grow as high as the slow start threshold - int congestionWindow = _congestionWindowSize + numAcked; + int congestionWindow = _congestionWindowSize + numRTTCollected; if (congestionWindow > udt::MAX_PACKETS_IN_FLIGHT) { // we're done with slow start, set the congestion window to the slow start threshold _congestionWindowSize = udt::MAX_PACKETS_IN_FLIGHT; // figure out how many left over ACKs we should apply using the regular reno congestion avoidance - numAcked = congestionWindow - udt::MAX_PACKETS_IN_FLIGHT; + numRTTCollected = congestionWindow - udt::MAX_PACKETS_IN_FLIGHT; } else { _congestionWindowSize = congestionWindow; - numAcked = 0; + numRTTCollected = 0; } } // grab the size of the window prior to reno additive increase int preAIWindowSize = _congestionWindowSize; - if (numAcked > 0) { + if (numRTTCollected > 0) { // Once we are out of slow start, we use additive increase to grow the window slowly. // We grow the congestion window by a single packet everytime the entire congestion window is sent. @@ -263,7 +300,7 @@ void TCPVegasCC::performRenoCongestionAvoidance(SequenceNumber ack) { } // increase the window size by (1 / window size) for every ACK received - _ackAICount += numAcked; + _ackAICount += numRTTCollected; if (_ackAICount >= preAIWindowSize) { // when _ackAICount % preAIWindowSize == 0 then _ackAICount is 0 // when _ackAICount % preAIWindowSize != 0 then _ackAICount is _ackAICount - (_ackAICount % preAIWindowSize) @@ -277,8 +314,19 @@ void TCPVegasCC::performRenoCongestionAvoidance(SequenceNumber ack) { } void TCPVegasCC::onPacketSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) { - if (_sentPacketTimes.find(seqNum) == _sentPacketTimes.end()) { - _sentPacketTimes[seqNum] = timePoint; + _sentPacketDatas.emplace_back(seqNum, timePoint); +} + +void TCPVegasCC::onPacketReSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) { + // look for our information for this sent packet + auto it = std::find_if(_sentPacketDatas.begin(), _sentPacketDatas.end(), [seqNum](SentPacketData& sentPacketInfo){ + return sentPacketInfo.sequenceNumber == seqNum; + }); + + // if we found information for this packet (it hasn't been erased because it hasn't yet been ACKed) + // then mark it as re-sent so we know it cannot be used for RTT calculations + if (it != _sentPacketDatas.end()) { + it->wasResent = true; } } diff --git a/libraries/networking/src/udt/TCPVegasCC.h b/libraries/networking/src/udt/TCPVegasCC.h index bb14728d4b..1d83c4c992 100644 --- a/libraries/networking/src/udt/TCPVegasCC.h +++ b/libraries/networking/src/udt/TCPVegasCC.h @@ -30,6 +30,7 @@ public: virtual void onTimeout() override {}; virtual void onPacketSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) override; + virtual void onPacketReSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) override; virtual int estimatedTimeout() const override; @@ -37,11 +38,23 @@ protected: virtual void performCongestionAvoidance(SequenceNumber ack); virtual void setInitialSendSequenceNumber(SequenceNumber seqNum) override { _lastACK = seqNum - 1; } private: + bool calculateRTT(p_high_resolution_clock::time_point sendTime, p_high_resolution_clock::time_point receiveTime); + bool needsFastRetransmit(SequenceNumber ack, bool wasDuplicateACK); + bool isCongestionWindowLimited(); void performRenoCongestionAvoidance(SequenceNumber ack); - using PacketTimeList = std::map; - PacketTimeList _sentPacketTimes; // Map of sequence numbers to sent time + struct SentPacketData { + SentPacketData(SequenceNumber seqNum, p_high_resolution_clock::time_point tPoint) + : sequenceNumber(seqNum), timePoint(tPoint) {}; + + SequenceNumber sequenceNumber; + p_high_resolution_clock::time_point timePoint; + bool wasResent { false }; + }; + + using PacketTimeList = std::vector; + PacketTimeList _sentPacketDatas; // association of sequence numbers to sent time, for RTT calc p_high_resolution_clock::time_point _lastAdjustmentTime; // Time of last congestion control adjustment @@ -56,7 +69,7 @@ private: int _ewmaRTT { -1 }; // Exponential weighted moving average RTT int _rttVariance { 0 }; // Variance in collected RTT values - int _numACKs { 0 }; // Number of ACKs received during the last RTT (since last performed congestion avoidance) + int _numRTTs { 0 }; // Number of RTTs calculated during the last RTT (since last performed congestion avoidance) int _ackAICount { 0 }; // Counter for number of ACKs received for Reno additive increase int _duplicateACKCount { 0 }; // Counter for duplicate ACKs received From 34fb849536daa4a3a30d0a387946e114e9aea526 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 31 Oct 2018 13:53:18 -0700 Subject: [PATCH 059/125] Cleanup FIXME --- .../qml/hifi/commerce/purchases/Purchases.qml | 2 +- scripts/system/html/js/marketplacesInject.js | 18 +----------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 108a57d1f5..41453c9790 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -161,7 +161,7 @@ Rectangle { id: titleBarContainer; z: 997; visible: false; - height: 100; // HRS FIXME: get rid of the header and associated code entirely? + height: 100; // Size width: parent.width; // Anchors diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index fd228e2596..eda37c06b7 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -251,7 +251,6 @@ } cost = $(this).closest('.col-xs-3').find('.item-cost').text(); var costInt = parseInt(cost, 10); - var disable = limitedCommerce && (costInt > 0); $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); $(this).closest('.col-xs-3').attr("class", 'col-xs-6'); @@ -260,23 +259,11 @@ priceElement.css({ "padding": "3px 5px", "height": "40px", - "background": disable ? "grey" : "linear-gradient(#00b4ef, #0093C5)", + "background": "linear-gradient(#00b4ef, #0093C5)", "color": "#FFF", "font-weight": "600", "line-height": "34px" }); - - if (parseInt(cost) > 0) { - if (disable) { - priceElement.html('N/A'); // In case the following fails - $(this).parent().parent().parent().parent().parent().css({"display": "none"}); // HRS FIXME, oh and do I have to set display non-none in the other branch? - } else { - priceElement.css({ "width": "auto" }); - priceElement.html(' ' + cost); - priceElement.css({ "min-width": priceElement.width() + 30 }); - } - } }); // change pricing to GET/BUY on button hover @@ -395,9 +382,6 @@ var cost = $('.item-cost').text(); var costInt = parseInt(cost, 10); var availability = $.trim($('.item-availability').text()); - if (limitedCommerce && (costInt > 0)) { - availability = ''; - } if (availability === 'available') { purchaseButton.css({ "background": "linear-gradient(#00b4ef, #0093C5)", From ce0ad48a28de45c9bba040fb94eb5675a27223a0 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 31 Oct 2018 13:58:26 -0700 Subject: [PATCH 060/125] tabless wallet --- .../resources/qml/hifi/commerce/purchases/Purchases.qml | 2 +- interface/resources/qml/hifi/commerce/wallet/Wallet.qml | 8 ++++---- scripts/system/commerce/wallet.js | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 7000b632e2..d9ece98a84 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -452,7 +452,7 @@ Rectangle { anchors.left: parent.left; anchors.leftMargin: 16; width: paintedWidth; - text: "Inventory"; + text: "Items"; color: hifi.colors.black; size: 22; } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index a958e62aad..d63b9c722e 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -135,7 +135,7 @@ Rectangle { // Title Bar text RalewaySemiBold { id: titleBarText; - text: "ASSETS"; + text: "INVENTORY"; // Text size size: hifi.fontSizes.overlayTitle; // Anchors @@ -376,7 +376,7 @@ Rectangle { id: walletInventory; visible: root.activeView === "walletInventory"; anchors.top: titleBarContainer.bottom; - anchors.bottom: tabButtonsContainer.top; + anchors.bottom: !WalletScriptingInterface.limitedCommerce ? tabButtonsContainer.top : parent.bottom; anchors.left: parent.left; anchors.right: parent.right; Connections { @@ -475,7 +475,7 @@ Rectangle { // Item { id: tabButtonsContainer; - visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendAssetStep"; + visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendAssetStep" && !WalletScriptingInterface.limitedCommerce; property int numTabs: 5; // Size width: root.width; @@ -585,7 +585,7 @@ Rectangle { } RalewaySemiBold { - text: "INVENTORY"; + text: "ITEMS"; // Text size size: 16; // Anchors diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 353145035e..a8eca5f60d 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -644,14 +644,13 @@ function uninstallMarketplaceItemTester() { } } -var BUTTON_NAME = "ASSETS"; +var BUTTON_NAME = "INVENTORY"; var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; var NOTIFICATION_POLL_TIMEOUT = 300000; var ui; function startup() { ui = new AppUi({ buttonName: BUTTON_NAME, - buttonPrefix: "wallet-", sortOrder: 10, home: WALLET_QML_SOURCE, onOpened: walletOpened, From 0b7ddca5f66058e5df4fe7d356362f1358dcfcc7 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 31 Oct 2018 14:03:20 -0700 Subject: [PATCH 061/125] Change naming for straggler names in model loading debug dumps --- libraries/fbx/src/GLTFReader.cpp | 60 ++++++++++++++++---------------- libraries/fbx/src/GLTFReader.h | 2 +- libraries/fbx/src/OBJReader.cpp | 18 +++++----- libraries/fbx/src/OBJReader.h | 2 +- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index 05534b5264..7ee13c5cdf 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -929,7 +929,7 @@ HFMGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping buildGeometry(geometry, url); - //fbxDebugDump(geometry); + //hfmDebugDump(geometry); return geometryPtr; } @@ -1181,37 +1181,37 @@ void GLTFReader::retriangulate(const QVector& inIndices, const QVector Date: Wed, 31 Oct 2018 14:15:30 -0700 Subject: [PATCH 062/125] Change local variable in TestFbx for clarity --- tests-manual/gpu/src/TestFbx.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests-manual/gpu/src/TestFbx.cpp b/tests-manual/gpu/src/TestFbx.cpp index ba94fb810c..9890e4fbe7 100644 --- a/tests-manual/gpu/src/TestFbx.cpp +++ b/tests-manual/gpu/src/TestFbx.cpp @@ -100,12 +100,12 @@ bool TestFbx::isReady() const { void TestFbx::parseFbx(const QByteArray& fbxData) { QVariantHash mapping; - HFMGeometry* fbx = readFBX(fbxData, mapping); + HFMGeometry* geometry = readFBX(fbxData, mapping); size_t totalVertexCount = 0; size_t totalIndexCount = 0; size_t totalPartCount = 0; size_t highestIndex = 0; - for (const auto& mesh : fbx->meshes) { + for (const auto& mesh : geometry->meshes) { size_t vertexCount = mesh.vertices.size(); totalVertexCount += mesh.vertices.size(); highestIndex = std::max(highestIndex, vertexCount); @@ -123,7 +123,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { std::vector parts; parts.reserve(totalPartCount); _partCount = totalPartCount; - for (const auto& mesh : fbx->meshes) { + for (const auto& mesh : geometry->meshes) { baseVertex = vertices.size(); vec3 color; @@ -133,7 +133,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { partIndirect.firstIndex = (uint)indices.size(); partIndirect.baseInstance = (uint)parts.size(); _partTransforms.push_back(mesh.modelTransform); - auto material = fbx->materials[part.materialID]; + auto material = geometry->materials[part.materialID]; color = material.diffuseColor; for (auto index : part.quadTrianglesIndices) { indices.push_back(index); @@ -163,7 +163,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { _vertexBuffer->append(vertices); _indexBuffer->append(indices); _indirectBuffer->append(parts); - delete fbx; + delete geometry; } void TestFbx::renderTest(size_t testId, RenderArgs* args) { From 41a0d093896cc4c08b103a92d8473bc689f598d2 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 31 Oct 2018 15:31:51 -0700 Subject: [PATCH 063/125] First pass --- .../qml/LoginDialog/LinkAccountBody.qml | 14 +- .../hifi/commerce/common/CommerceLightbox.qml | 8 +- .../qml/hifi/commerce/wallet/Security.qml | 246 -------------- .../qml/hifi/dialogs/security/Security.qml | 306 ++++++++++++++++++ .../security}/SecurityImageChange.qml | 32 +- .../security}/SecurityImageModel.qml | 8 +- .../security}/SecurityImageSelection.qml | 16 +- .../hifi/dialogs/security/SecurityWrapper.qml | 30 ++ .../wallet => dialogs/security}/images/01.jpg | Bin .../wallet => dialogs/security}/images/02.jpg | Bin .../wallet => dialogs/security}/images/03.jpg | Bin .../wallet => dialogs/security}/images/04.jpg | Bin .../wallet => dialogs/security}/images/05.jpg | Bin .../wallet => dialogs/security}/images/06.jpg | Bin .../wallet => dialogs/security}/images/07.jpg | Bin .../wallet => dialogs/security}/images/08.jpg | Bin .../wallet => dialogs/security}/images/09.jpg | Bin .../wallet => dialogs/security}/images/10.jpg | Bin .../wallet => dialogs/security}/images/11.jpg | Bin .../wallet => dialogs/security}/images/12.jpg | Bin .../wallet => dialogs/security}/images/13.jpg | Bin .../wallet => dialogs/security}/images/14.jpg | Bin .../wallet => dialogs/security}/images/15.jpg | Bin .../wallet => dialogs/security}/images/16.jpg | Bin .../wallet => dialogs/security}/images/17.jpg | Bin .../wallet => dialogs/security}/images/18.jpg | Bin .../wallet => dialogs/security}/images/19.jpg | Bin .../wallet => dialogs/security}/images/20.jpg | Bin .../wallet => dialogs/security}/images/21.jpg | Bin .../wallet => dialogs/security}/images/22.jpg | Bin .../wallet => dialogs/security}/images/23.jpg | Bin .../wallet => dialogs/security}/images/24.jpg | Bin .../wallet => dialogs/security}/images/25.jpg | Bin .../wallet => dialogs/security}/images/26.jpg | Bin .../wallet => dialogs/security}/images/27.jpg | Bin .../wallet => dialogs/security}/images/28.jpg | Bin .../wallet => dialogs/security}/images/29.jpg | Bin .../wallet => dialogs/security}/images/30.jpg | Bin .../wallet => dialogs/security}/images/31.jpg | Bin .../wallet => dialogs/security}/images/32.jpg | Bin .../wallet => dialogs/security}/images/33.jpg | Bin .../wallet => dialogs/security}/images/34.jpg | Bin .../security}/images/lowerKeyboard.png | Bin .../security}/images/wallet-bg.jpg | Bin .../security}/images/wallet-tip-bg.png | Bin interface/src/Application.cpp | 12 +- interface/src/Menu.cpp | 7 + scripts/system/commerce/wallet.js | 6 +- 48 files changed, 388 insertions(+), 297 deletions(-) delete mode 100644 interface/resources/qml/hifi/commerce/wallet/Security.qml create mode 100644 interface/resources/qml/hifi/dialogs/security/Security.qml rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/SecurityImageChange.qml (89%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/SecurityImageModel.qml (89%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/SecurityImageSelection.qml (88%) create mode 100644 interface/resources/qml/hifi/dialogs/security/SecurityWrapper.qml rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/01.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/02.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/03.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/04.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/05.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/06.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/07.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/08.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/09.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/10.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/11.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/12.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/13.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/14.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/15.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/16.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/17.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/18.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/19.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/20.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/21.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/22.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/23.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/24.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/25.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/26.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/27.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/28.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/29.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/30.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/31.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/32.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/33.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/34.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/lowerKeyboard.png (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/wallet-bg.jpg (100%) rename interface/resources/qml/hifi/{commerce/wallet => dialogs/security}/images/wallet-tip-bg.png (100%) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 48cf124127..d5d89cd0b4 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -136,7 +136,7 @@ Item { TextField { id: usernameField - text: Settings.getValue("wallet/savedUsername", ""); + text: Settings.getValue("keepMeLoggedIn/savedUsername", ""); width: parent.width focus: true placeholderText: "Username or Email" @@ -165,7 +165,7 @@ Item { root.text = ""; } Component.onCompleted: { - var savedUsername = Settings.getValue("wallet/savedUsername", ""); + var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", ""); usernameField.text = savedUsername === "Unknown user" ? "" : savedUsername; } } @@ -263,21 +263,21 @@ Item { CheckBox { id: autoLogoutCheckbox - checked: !Settings.getValue("wallet/autoLogout", true) + checked: Settings.getValue("keepMeLoggedIn", false) text: "Keep me signed in" boxSize: 20; labelFontSize: 15 color: hifi.colors.black onCheckedChanged: { - Settings.setValue("wallet/autoLogout", !checked); + Settings.setValue("keepMeLoggedIn", checked); if (checked) { - Settings.setValue("wallet/savedUsername", Account.username); + Settings.setValue("keepMeLoggedIn/savedUsername", Account.username); } else { - Settings.setValue("wallet/savedUsername", ""); + Settings.setValue("keepMeLoggedIn/savedUsername", ""); } } Component.onDestruction: { - Settings.setValue("wallet/autoLogout", !checked); + Settings.setValue("keepMeLoggedIn", checked); } } diff --git a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml index 9d9216c461..5ddfa98923 100644 --- a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml +++ b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml @@ -14,9 +14,9 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit -import "../../../controls" as HifiControls +import "qrc:////qml//styles-uit" +import "qrc:////qml//controls-uit" as HifiControlsUit +import "qrc:////qml//controls" as HifiControls // references XXX from root context @@ -40,6 +40,8 @@ Rectangle { anchors.fill: parent; color: Qt.rgba(0, 0, 0, 0.5); z: 999; + + HifiConstants { id: hifi; } onVisibleChanged: { if (!visible) { diff --git a/interface/resources/qml/hifi/commerce/wallet/Security.qml b/interface/resources/qml/hifi/commerce/wallet/Security.qml deleted file mode 100644 index 14ac696ef7..0000000000 --- a/interface/resources/qml/hifi/commerce/wallet/Security.qml +++ /dev/null @@ -1,246 +0,0 @@ -// -// Security.qml -// qml/hifi/commerce/wallet -// -// Security -// -// Created by Zach Fox on 2017-08-18 -// Copyright 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 -// - -import Hifi 1.0 as Hifi -import QtQuick 2.5 -import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit -import "../../../controls" as HifiControls - -// references XXX from root context - -Item { - HifiConstants { id: hifi; } - - id: root; - property string keyFilePath; - - Connections { - target: Commerce; - - onKeyFilePathIfExistsResult: { - root.keyFilePath = path; - } - } - - // Username Text - RalewayRegular { - id: usernameText; - text: Account.username; - // Text size - size: 24; - // Style - color: hifi.colors.white; - elide: Text.ElideRight; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.leftMargin: 20; - width: parent.width/2; - height: 80; - } - - Item { - id: securityContainer; - anchors.top: usernameText.bottom; - anchors.topMargin: 20; - anchors.left: parent.left; - anchors.right: parent.right; - anchors.bottom: parent.bottom; - - RalewaySemiBold { - id: securityText; - text: "Security"; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - anchors.leftMargin: 20; - anchors.right: parent.right; - height: 30; - // Text size - size: 18; - // Style - color: hifi.colors.blueHighlight; - } - - Rectangle { - id: securityTextSeparator; - // Size - width: parent.width; - height: 1; - // Anchors - anchors.left: parent.left; - anchors.right: parent.right; - anchors.top: securityText.bottom; - anchors.topMargin: 8; - // Style - color: hifi.colors.faintGray; - } - - Item { - id: changeSecurityImageContainer; - anchors.top: securityTextSeparator.bottom; - anchors.topMargin: 8; - anchors.left: parent.left; - anchors.leftMargin: 40; - anchors.right: parent.right; - anchors.rightMargin: 55; - height: 75; - - HiFiGlyphs { - id: changeSecurityImageImage; - text: hifi.glyphs.securityImage; - // Size - size: 80; - // Anchors - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: parent.left; - // Style - color: hifi.colors.white; - } - - RalewaySemiBold { - text: "Security Pic"; - // Anchors - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: changeSecurityImageImage.right; - anchors.leftMargin: 30; - width: 50; - // Text size - size: 18; - // Style - color: hifi.colors.white; - } - - // "Change Security Pic" button - HifiControlsUit.Button { - id: changeSecurityImageButton; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.right: parent.right; - anchors.verticalCenter: parent.verticalCenter; - width: 140; - height: 40; - text: "Change"; - onClicked: { - sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'}); - } - } - } - - Item { - id: autoLogoutContainer; - anchors.top: changeSecurityImageContainer.bottom; - anchors.topMargin: 8; - anchors.left: parent.left; - anchors.leftMargin: 40; - anchors.right: parent.right; - anchors.rightMargin: 55; - height: 75; - - HiFiGlyphs { - id: autoLogoutImage; - text: hifi.glyphs.walletKey; - // Size - size: 80; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 20; - anchors.left: parent.left; - // Style - color: hifi.colors.white; - } - - HifiControlsUit.CheckBox { - id: autoLogoutCheckbox; - checked: Settings.getValue("wallet/autoLogout", false); - text: "Automatically Log Out when Exiting Interface" - // Anchors - anchors.verticalCenter: autoLogoutImage.verticalCenter; - anchors.left: autoLogoutImage.right; - anchors.leftMargin: 20; - anchors.right: autoLogoutHelp.left; - anchors.rightMargin: 12; - boxSize: 28; - labelFontSize: 18; - color: hifi.colors.white; - onCheckedChanged: { - Settings.setValue("wallet/autoLogout", checked); - if (checked) { - Settings.setValue("wallet/savedUsername", Account.username); - } else { - Settings.setValue("wallet/savedUsername", ""); - } - } - } - - RalewaySemiBold { - id: autoLogoutHelp; - text: '[?]'; - // Anchors - anchors.verticalCenter: autoLogoutImage.verticalCenter; - anchors.right: parent.right; - width: 30; - height: 30; - // Text size - size: 18; - // Style - color: hifi.colors.blueHighlight; - - MouseArea { - anchors.fill: parent; - hoverEnabled: true; - onEntered: { - parent.color = hifi.colors.blueAccent; - } - onExited: { - parent.color = hifi.colors.blueHighlight; - } - onClicked: { - sendSignalToWallet({method: 'walletSecurity_autoLogoutHelp'}); - } - } - } - } - } - - // - // FUNCTION DEFINITIONS START - // - // - // Function Name: fromScript() - // - // Relevant Variables: - // None - // - // Arguments: - // message: The message sent from the JavaScript. - // Messages are in format "{method, params}", like json-rpc. - // - // Description: - // Called when a message is received from a script. - // - function fromScript(message) { - switch (message.method) { - default: - console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); - } - } - signal sendSignalToWallet(var msg); - // - // FUNCTION DEFINITIONS END - // -} diff --git a/interface/resources/qml/hifi/dialogs/security/Security.qml b/interface/resources/qml/hifi/dialogs/security/Security.qml new file mode 100644 index 0000000000..d1dc2a9e11 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/security/Security.qml @@ -0,0 +1,306 @@ +// +// Security.qml +// qml\hifi\dialogs\security +// +// Security +// +// Created by Zach Fox on 2018-10-31 +// Copyright 2018 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 +// + +import Hifi 1.0 as Hifi +import QtQuick 2.5 +import QtGraphicalEffects 1.0 +import "qrc:////qml//styles-uit" as HifiStylesUit +import "qrc:////qml//controls-uit" as HifiControlsUit +import "qrc:////qml//controls" as HifiControls +import "qrc:////qml//hifi//commerce//common" as HifiCommerceCommon + +// references XXX from root context + +Rectangle { + HifiStylesUit.HifiConstants { id: hifi; } + + id: root; + color: hifi.colors.darkGray; + + property string title: "Security Settings"; + property bool walletSetUp; + + Connections { + target: Commerce; + + onWalletStatusResult: { + if (walletStatus === 5) { + Commerce.getSecurityImage(); + root.walletSetUp = true; + } else { + root.walletSetUp = false; + } + } + + onSecurityImageResult: { + if (exists) { + currentSecurityPicture.source = ""; + currentSecurityPicture.source = "image://security/securityImage"; + } + } + } + + HifiCommerceCommon.CommerceLightbox { + id: lightboxPopup; + visible: false; + anchors.fill: parent; + } + + // Username Text + HifiStylesUit.RalewayRegular { + id: usernameText; + text: Account.username === "" ? Account.username : "Please Log In"; + // Text size + size: 24; + // Style + color: hifi.colors.white; + elide: Text.ElideRight; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; + } + + Item { + id: pleaseLogInContainer; + visible: Account.username === ""; + anchors.top: usernameText.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + + HifiStylesUit.RalewayRegular { + text: "Please log in for security settings." + // Text size + size: 24; + // Style + color: hifi.colors.white; + // Anchors + anchors.bottom: openLoginButton.top; + anchors.left: parent.left; + anchors.right: parent.right; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + height: 80; + } + + HifiControlsUit.Button { + id: openLoginButton; + color: hifi.buttons.white; + colorScheme: hifi.colorSchemes.dark; + anchors.centerIn: parent; + width: 140; + height: 40; + text: "Change"; + onClicked: { + DialogsManager.showLoginDialog(); + } + } + } + + Item { + id: securitySettingsContainer; + visible: !pleaseLogInContainer.visible; + anchors.top: usernameText.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + + Item { + id: accountContainer; + anchors.top: securitySettingsContainer.top; + anchors.left: parent.left; + anchors.right: parent.right; + height: childrenRect.height; + + Rectangle { + id: accountHeaderContainer; + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + height: 80; + color: hifi.colors.baseGrayHighlight; + + HifiStylesUit.RalewaySemiBold { + text: "Account"; + anchors.fill: parent; + anchors.leftMargin: 20; + } + } + + Item { + id: keepMeLoggedInContainer; + anchors.top: accountHeaderContainer.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 80; + + HifiControlsUit.CheckBox { + id: autoLogoutCheckbox; + checked: Settings.getValue("keepMeLoggedIn", false); + text: "Keep Me Logged In" + // Anchors + anchors.verticalCenter: parent.verticalCenter; + anchors.left: parent.left; + anchors.leftMargin: 20; + boxSize: 28; + labelFontSize: 18; + color: hifi.colors.white; + onCheckedChanged: { + Settings.setValue("keepMeLoggedIn", checked); + if (checked) { + Settings.setValue("keepMeLoggedIn/savedUsername", Account.username); + } else { + Settings.setValue("keepMeLoggedIn/savedUsername", ""); + } + } + } + + HifiStylesUit.RalewaySemiBold { + id: autoLogoutHelp; + text: '[?]'; + // Anchors + anchors.verticalCenter: parent.verticalCenter; + anchors.right: autoLogoutCheckbox.right; + width: 30; + height: 30; + // Text size + size: 18; + // Style + color: hifi.colors.blueHighlight; + + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.color = hifi.colors.blueAccent; + } + onExited: { + parent.color = hifi.colors.blueHighlight; + } + onClicked: { + lightboxPopup.titleText = "Keep Me Logged In"; + lightboxPopup.bodyText = "If you choose to stay logged in, ensure that this is a trusted device.\n\n" + + "Also, remember that logging out may not disconnect you from a domain."; + lightboxPopup.button1text = "OK"; + lightboxPopup.button1method = function() { + lightboxPopup.visible = false; + } + lightboxPopup.visible = true; + } + } + } + } + } + + Item { + id: walletContainer; + anchors.top: accountContainer.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: childrenRect.height; + + Rectangle { + id: walletHeaderContainer; + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + height: 80; + color: hifi.colors.baseGrayHighlight; + + HifiStylesUit.RalewaySemiBold { + text: "Wallet"; + anchors.fill: parent; + anchors.leftMargin: 20; + } + } + + Item { + id: walletSecurityPictureContainer; + visible: root.walletSetUp; + anchors.top: walletHeaderContainer.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 80; + + Image { + id: currentSecurityPicture; + source: ""; + visible: true; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.verticalCenter: parent.verticalCenter; + height: 40; + width: height; + mipmap: true; + cache: false; + } + + HifiStylesUit.RalewayRegular { + id: securityPictureText; + text: "Wallet Security Picture"; + // Anchors + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.left: currentSecurityPicture.right; + anchors.leftMargin: 12; + width: paintedWidth; + // Text size + size: 18; + // Style + color: hifi.colors.white; + } + + // "Change Security Pic" button + HifiControlsUit.Button { + id: changeSecurityImageButton; + color: hifi.buttons.white; + colorScheme: hifi.colorSchemes.dark; + anchors.left: securityPictureText.right; + anchors.verticalCenter: parent.verticalCenter; + width: 140; + height: 40; + text: "Change"; + onClicked: { + + } + } + } + + Item { + id: walletNotSetUpContainer; + visible: !root.walletSetUp; + anchors.top: walletHeaderContainer.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 80; + + HifiStylesUit.RalewayRegular { + text: "Your wallet is not set up.\n" + + "Open the WALLET app to get started."; + // Anchors + anchors.fill: parent; + // Text size + size: 18; + // Style + color: hifi.colors.white; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + } + } + } + } +} diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml b/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml similarity index 89% rename from interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml rename to interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml index 01df18352b..d953a764fd 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml +++ b/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml @@ -1,11 +1,11 @@ // // SecurityImageChange.qml -// qml/hifi/commerce/wallet +// qml\hifi\dialogs\security // -// SecurityImageChange +// Security // -// Created by Zach Fox on 2017-08-18 -// Copyright 2017 High Fidelity, Inc. +// Created by Zach Fox on 2018-10-31 +// Copyright 2018 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 @@ -13,9 +13,9 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit -import "../../../controls" as HifiControls +import "qrc:////qml//styles-uit" as HifiStylesUit +import "qrc:////qml//controls-uit" as HifiControlsUit +import "qrc:////qml//controls" as HifiControls // references XXX from root context @@ -33,7 +33,7 @@ Item { securityImageChangePageSecurityImage.source = "image://security/securityImage"; if (exists) { // Success submitting new security image if (root.justSubmitted) { - sendSignalToWallet({method: "walletSecurity_changeSecurityImageSuccess"}); + sendSignalToParent({method: "walletSecurity_changeSecurityImageSuccess"}); root.justSubmitted = false; } } else if (root.justSubmitted) { @@ -83,10 +83,10 @@ Item { verticalAlignment: Text.AlignBottom; color: hifi.colors.white; } - // "Security image" text below pic + // "Security image" text below image RalewayRegular { id: securityImageText; - text: "SECURITY PIC"; + text: "SECURITY IMAGE"; // Text size size: 12; // Anchors @@ -118,7 +118,7 @@ Item { // "Change Security Image" text RalewaySemiBold { id: securityImageTitle; - text: "Change Security Pic:"; + text: "Change Security Image:"; // Text size size: 18; anchors.top: parent.top; @@ -139,12 +139,6 @@ Item { anchors.right: parent.right; anchors.rightMargin: 16; height: 300; - - Connections { - onSendSignalToWallet: { - sendSignalToWallet(msg); - } - } } // Navigation Bar @@ -169,7 +163,7 @@ Item { width: 150; text: "Cancel" onClicked: { - sendSignalToWallet({method: "walletSecurity_changeSecurityImageCancelled"}); + sendSignalToParent({method: "walletSecurity_changeSecurityImageCancelled"}); } } @@ -197,7 +191,7 @@ Item { // SECURITY IMAGE SELECTION END // - signal sendSignalToWallet(var msg); + signal sendSignalToParent(var msg); function initModel() { securityImageSelection.initModel(); diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml b/interface/resources/qml/hifi/dialogs/security/SecurityImageModel.qml similarity index 89% rename from interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml rename to interface/resources/qml/hifi/dialogs/security/SecurityImageModel.qml index b8e9db09ab..946f979c1a 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml +++ b/interface/resources/qml/hifi/dialogs/security/SecurityImageModel.qml @@ -1,11 +1,11 @@ // // SecurityImageModel.qml -// qml/hifi/commerce +// qml\hifi\dialogs\security // -// SecurityImageModel +// Security // -// Created by Zach Fox on 2017-08-17 -// Copyright 2017 High Fidelity, Inc. +// Created by Zach Fox on 2018-10-31 +// Copyright 2018 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 diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml b/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml similarity index 88% rename from interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml rename to interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml index 599c6a1851..5a05a28ba3 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml +++ b/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml @@ -1,11 +1,11 @@ // // SecurityImageSelection.qml -// qml/hifi/commerce/wallet +// qml\hifi\dialogs\security // -// SecurityImageSelection +// Security // -// Created by Zach Fox on 2017-08-17 -// Copyright 2017 High Fidelity, Inc. +// Created by Zach Fox on 2018-10-31 +// Copyright 2018 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 @@ -13,9 +13,9 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit -import "../../../controls" as HifiControls +import "qrc:////qml//styles-uit" as HifiStylesUit +import "qrc:////qml//controls-uit" as HifiControlsUit +import "qrc:////qml//controls" as HifiControls // references XXX from root context @@ -73,8 +73,6 @@ Item { // // FUNCTION DEFINITIONS START // - signal sendSignalToWallet(var msg); - function getImagePathFromImageID(imageID) { return (imageID ? gridModel.getImagePathFromImageID(imageID) : ""); } diff --git a/interface/resources/qml/hifi/dialogs/security/SecurityWrapper.qml b/interface/resources/qml/hifi/dialogs/security/SecurityWrapper.qml new file mode 100644 index 0000000000..7c17818225 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/security/SecurityWrapper.qml @@ -0,0 +1,30 @@ +// +// SecurityWrapper.qml +// qml\hifi\dialogs\security +// +// SecurityWrapper +// +// Created by Zach Fox on 2018-10-31 +// Copyright 2018 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 +// + +import "qrc:////qml//windows" +import "./" + +ScrollingWindow { + id: root; + + resizable: true; + destroyOnHidden: true; + width: 400; + height: 577; + minSize: Qt.vector2d(400, 500); + + Security { id: security; width: root.width } + + objectName: "SecurityDialog"; + title: security.title; +} diff --git a/interface/resources/qml/hifi/commerce/wallet/images/01.jpg b/interface/resources/qml/hifi/dialogs/security/images/01.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/01.jpg rename to interface/resources/qml/hifi/dialogs/security/images/01.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/02.jpg b/interface/resources/qml/hifi/dialogs/security/images/02.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/02.jpg rename to interface/resources/qml/hifi/dialogs/security/images/02.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/03.jpg b/interface/resources/qml/hifi/dialogs/security/images/03.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/03.jpg rename to interface/resources/qml/hifi/dialogs/security/images/03.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/04.jpg b/interface/resources/qml/hifi/dialogs/security/images/04.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/04.jpg rename to interface/resources/qml/hifi/dialogs/security/images/04.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/05.jpg b/interface/resources/qml/hifi/dialogs/security/images/05.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/05.jpg rename to interface/resources/qml/hifi/dialogs/security/images/05.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/06.jpg b/interface/resources/qml/hifi/dialogs/security/images/06.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/06.jpg rename to interface/resources/qml/hifi/dialogs/security/images/06.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/07.jpg b/interface/resources/qml/hifi/dialogs/security/images/07.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/07.jpg rename to interface/resources/qml/hifi/dialogs/security/images/07.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/08.jpg b/interface/resources/qml/hifi/dialogs/security/images/08.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/08.jpg rename to interface/resources/qml/hifi/dialogs/security/images/08.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/09.jpg b/interface/resources/qml/hifi/dialogs/security/images/09.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/09.jpg rename to interface/resources/qml/hifi/dialogs/security/images/09.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/10.jpg b/interface/resources/qml/hifi/dialogs/security/images/10.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/10.jpg rename to interface/resources/qml/hifi/dialogs/security/images/10.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/11.jpg b/interface/resources/qml/hifi/dialogs/security/images/11.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/11.jpg rename to interface/resources/qml/hifi/dialogs/security/images/11.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/12.jpg b/interface/resources/qml/hifi/dialogs/security/images/12.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/12.jpg rename to interface/resources/qml/hifi/dialogs/security/images/12.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/13.jpg b/interface/resources/qml/hifi/dialogs/security/images/13.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/13.jpg rename to interface/resources/qml/hifi/dialogs/security/images/13.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/14.jpg b/interface/resources/qml/hifi/dialogs/security/images/14.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/14.jpg rename to interface/resources/qml/hifi/dialogs/security/images/14.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/15.jpg b/interface/resources/qml/hifi/dialogs/security/images/15.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/15.jpg rename to interface/resources/qml/hifi/dialogs/security/images/15.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/16.jpg b/interface/resources/qml/hifi/dialogs/security/images/16.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/16.jpg rename to interface/resources/qml/hifi/dialogs/security/images/16.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/17.jpg b/interface/resources/qml/hifi/dialogs/security/images/17.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/17.jpg rename to interface/resources/qml/hifi/dialogs/security/images/17.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/18.jpg b/interface/resources/qml/hifi/dialogs/security/images/18.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/18.jpg rename to interface/resources/qml/hifi/dialogs/security/images/18.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/19.jpg b/interface/resources/qml/hifi/dialogs/security/images/19.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/19.jpg rename to interface/resources/qml/hifi/dialogs/security/images/19.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/20.jpg b/interface/resources/qml/hifi/dialogs/security/images/20.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/20.jpg rename to interface/resources/qml/hifi/dialogs/security/images/20.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/21.jpg b/interface/resources/qml/hifi/dialogs/security/images/21.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/21.jpg rename to interface/resources/qml/hifi/dialogs/security/images/21.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/22.jpg b/interface/resources/qml/hifi/dialogs/security/images/22.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/22.jpg rename to interface/resources/qml/hifi/dialogs/security/images/22.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/23.jpg b/interface/resources/qml/hifi/dialogs/security/images/23.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/23.jpg rename to interface/resources/qml/hifi/dialogs/security/images/23.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/24.jpg b/interface/resources/qml/hifi/dialogs/security/images/24.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/24.jpg rename to interface/resources/qml/hifi/dialogs/security/images/24.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/25.jpg b/interface/resources/qml/hifi/dialogs/security/images/25.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/25.jpg rename to interface/resources/qml/hifi/dialogs/security/images/25.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/26.jpg b/interface/resources/qml/hifi/dialogs/security/images/26.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/26.jpg rename to interface/resources/qml/hifi/dialogs/security/images/26.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/27.jpg b/interface/resources/qml/hifi/dialogs/security/images/27.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/27.jpg rename to interface/resources/qml/hifi/dialogs/security/images/27.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/28.jpg b/interface/resources/qml/hifi/dialogs/security/images/28.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/28.jpg rename to interface/resources/qml/hifi/dialogs/security/images/28.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/29.jpg b/interface/resources/qml/hifi/dialogs/security/images/29.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/29.jpg rename to interface/resources/qml/hifi/dialogs/security/images/29.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/30.jpg b/interface/resources/qml/hifi/dialogs/security/images/30.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/30.jpg rename to interface/resources/qml/hifi/dialogs/security/images/30.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/31.jpg b/interface/resources/qml/hifi/dialogs/security/images/31.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/31.jpg rename to interface/resources/qml/hifi/dialogs/security/images/31.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/32.jpg b/interface/resources/qml/hifi/dialogs/security/images/32.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/32.jpg rename to interface/resources/qml/hifi/dialogs/security/images/32.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/33.jpg b/interface/resources/qml/hifi/dialogs/security/images/33.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/33.jpg rename to interface/resources/qml/hifi/dialogs/security/images/33.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/34.jpg b/interface/resources/qml/hifi/dialogs/security/images/34.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/34.jpg rename to interface/resources/qml/hifi/dialogs/security/images/34.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/lowerKeyboard.png b/interface/resources/qml/hifi/dialogs/security/images/lowerKeyboard.png similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/lowerKeyboard.png rename to interface/resources/qml/hifi/dialogs/security/images/lowerKeyboard.png diff --git a/interface/resources/qml/hifi/commerce/wallet/images/wallet-bg.jpg b/interface/resources/qml/hifi/dialogs/security/images/wallet-bg.jpg similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/wallet-bg.jpg rename to interface/resources/qml/hifi/dialogs/security/images/wallet-bg.jpg diff --git a/interface/resources/qml/hifi/commerce/wallet/images/wallet-tip-bg.png b/interface/resources/qml/hifi/dialogs/security/images/wallet-tip-bg.png similarity index 100% rename from interface/resources/qml/hifi/commerce/wallet/images/wallet-tip-bg.png rename to interface/resources/qml/hifi/dialogs/security/images/wallet-tip-bg.png diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7cd91d76a0..1ca71a70d9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -382,7 +382,7 @@ static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin"; static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; -static const QString AUTO_LOGOUT_SETTING_NAME = "wallet/autoLogout"; +static const QString KEEP_ME_LOGGED_IN_SETTING_NAME = "keepMeLoggedIn"; const std::vector> Application::_acceptedExtensions { { SVO_EXTENSION, &Application::importSVOFromURL }, @@ -2567,7 +2567,7 @@ void Application::cleanupBeforeQuit() { } DependencyManager::destroy(); - bool autoLogout = Setting::Handle(AUTO_LOGOUT_SETTING_NAME, false).get(); + bool autoLogout = Setting::Handle(KEEP_ME_LOGGED_IN_SETTING_NAME, false).get(); if (autoLogout) { DependencyManager::get()->removeAccountFromFile(); } @@ -2944,13 +2944,13 @@ void Application::initializeUi() { QUrl{ "hifi/commerce/wallet/PassphraseChange.qml" }, QUrl{ "hifi/commerce/wallet/PassphraseModal.qml" }, QUrl{ "hifi/commerce/wallet/PassphraseSelection.qml" }, - QUrl{ "hifi/commerce/wallet/Security.qml" }, - QUrl{ "hifi/commerce/wallet/SecurityImageChange.qml" }, - QUrl{ "hifi/commerce/wallet/SecurityImageModel.qml" }, - QUrl{ "hifi/commerce/wallet/SecurityImageSelection.qml" }, QUrl{ "hifi/commerce/wallet/Wallet.qml" }, QUrl{ "hifi/commerce/wallet/WalletHome.qml" }, QUrl{ "hifi/commerce/wallet/WalletSetup.qml" }, + QUrl{ "hifi/dialogs/security/Security.qml" }, + QUrl{ "hifi/dialogs/security/SecurityImageChange.qml" }, + QUrl{ "hifi/dialogs/security/SecurityImageModel.qml" }, + QUrl{ "hifi/dialogs/security/SecurityImageSelection.qml" }, }, callback); qmlRegisterType("Hifi", 1, 0, "ResourceImageItem"); qmlRegisterType("Hifi", 1, 0, "Preference"); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 539bdabe7d..e16da6781d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -265,6 +265,13 @@ Menu::Menu() { QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog"); }); + // Settings > Security... + action = addActionToQMenuAndActionHash(settingsMenu, "Security..."); + connect(action, &QAction::triggered, [] { + qApp->showDialog(QString("hifi/dialogs/security/SecurityWrapper.qml"), + QString("hifi/dialogs/security/Security.qml"), "SecurityDialog"); + }); + // Settings > Developer Menu addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menu", 0, false, this, SLOT(toggleDeveloperMenus())); diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 5b91afea33..2d44650d9c 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -376,9 +376,9 @@ function deleteSendMoneyParticleEffect() { } function onUsernameChanged() { - if (Account.username !== Settings.getValue("wallet/savedUsername")) { - Settings.setValue("wallet/autoLogout", false); - Settings.setValue("wallet/savedUsername", ""); + if (Account.username !== Settings.getValue("keepMeLoggedIn/savedUsername")) { + Settings.setValue("keepMeLoggedIn", false); + Settings.setValue("keepMeLoggedIn/savedUsername", ""); } } From e6c3e7e67f31b30613bf1a86dd60ece38731bfb7 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 31 Oct 2018 14:41:49 -0700 Subject: [PATCH 064/125] initial purge of security tab from wallet --- .../qml/hifi/commerce/wallet/Help.qml | 7 +- .../qml/hifi/commerce/wallet/Wallet.qml | 132 +----------------- 2 files changed, 8 insertions(+), 131 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 6d8fc3c33f..6cf00f78eb 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -74,8 +74,7 @@ In your Wallet's Send Money tab, choose from your list of connections, or choose isExpanded: false; question: "What is a Security Pic?" answer: "Your Security Pic acts as an extra layer of Wallet security. \ -When you see your Security Pic, you know that your actions and data are securely making use of your account. \ -

Tap here to change your Security Pic."; +When you see your Security Pic, you know that your actions and data are securely making use of your account."; } ListElement { isExpanded: false; @@ -137,7 +136,7 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta anchors.left: parent.left; width: parent.width; height: questionText.paintedHeight + 50; - + RalewaySemiBold { id: plusMinusButton; text: model.isExpanded ? "-" : "+"; @@ -217,8 +216,6 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta } } else if (link === "#support") { Qt.openUrlExternally("mailto:support@highfidelity.com"); - } else if (link === "#securitypic") { - sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'}); } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index cbb77883df..f2119a1a8f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -232,10 +232,6 @@ Rectangle { root.isPassword = msg.isPasswordField; } else if (msg.method === 'walletSetup_lowerKeyboard') { root.keyboardRaised = false; - } else if (msg.method === 'walletSecurity_changePassphraseCancelled') { - root.activeView = "security"; - } else if (msg.method === 'walletSecurity_changePassphraseSuccess') { - root.activeView = "security"; } else { sendToScript(msg); } @@ -245,27 +241,6 @@ Rectangle { } } } - SecurityImageChange { - id: securityImageChange; - visible: root.activeView === "securityImageChange"; - z: 997; - anchors.top: titleBarContainer.bottom; - anchors.left: parent.left; - anchors.right: parent.right; - anchors.bottom: parent.bottom; - - Connections { - onSendSignalToWallet: { - if (msg.method === 'walletSecurity_changeSecurityImageCancelled') { - root.activeView = "security"; - } else if (msg.method === 'walletSecurity_changeSecurityImageSuccess') { - root.activeView = "security"; - } else { - sendToScript(msg); - } - } - } - } // // TAB CONTENTS START @@ -366,39 +341,6 @@ Rectangle { } } - Security { - id: security; - visible: root.activeView === "security"; - anchors.top: titleBarContainer.bottom; - anchors.bottom: tabButtonsContainer.top; - anchors.left: parent.left; - anchors.right: parent.right; - - Connections { - onSendSignalToWallet: { - if (msg.method === 'walletSecurity_changePassphrase') { - root.activeView = "passphraseChange"; - passphraseChange.clearPassphraseFields(); - passphraseChange.resetSubmitButton(); - } else if (msg.method === 'walletSecurity_changeSecurityImage') { - securityImageChange.initModel(); - root.activeView = "securityImageChange"; - } else if (msg.method === 'walletSecurity_autoLogoutHelp') { - lightboxPopup.titleText = "Automatically Log Out"; - lightboxPopup.bodyText = "By default, after you log in to High Fidelity, you will stay logged in to your High Fidelity " + - "account even after you close and re-open Interface. This means anyone who opens Interface on your computer " + - "could make purchases with your Wallet.\n\n" + - "If you do not want to stay logged in across Interface sessions, check this box."; - lightboxPopup.button1text = "CLOSE"; - lightboxPopup.button1method = function() { - lightboxPopup.visible = false; - } - lightboxPopup.visible = true; - } - } - } - } - Help { id: help; visible: root.activeView === "help"; @@ -407,14 +349,6 @@ Rectangle { anchors.left: parent.left; anchors.right: parent.right; - Connections { - onSendSignalToWallet: { - if (msg.method === 'walletSecurity_changeSecurityImage') { - securityImageChange.initModel(); - root.activeView = "securityImageChange"; - } - } - } } @@ -428,7 +362,7 @@ Rectangle { Item { id: tabButtonsContainer; visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendAssetStep"; - property int numTabs: 5; + property int numTabs: 4; // Size width: root.width; height: 90; @@ -452,7 +386,7 @@ Rectangle { anchors.left: parent.left; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; - + HiFiGlyphs { id: homeTabIcon; text: hifi.glyphs.home2; @@ -506,7 +440,7 @@ Rectangle { anchors.left: walletHomeButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; - + HiFiGlyphs { id: exchangeMoneyTabIcon; text: hifi.glyphs.leftRightArrows; @@ -550,7 +484,7 @@ Rectangle { anchors.left: exchangeMoneyButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; - + HiFiGlyphs { id: sendMoneyTabIcon; text: hifi.glyphs.paperPlane; @@ -596,70 +530,16 @@ Rectangle { } } - // "SECURITY" tab button - Rectangle { - id: securityButtonContainer; - visible: !walletSetup.visible; - color: root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black; - anchors.top: parent.top; - anchors.left: sendMoneyButtonContainer.right; - anchors.bottom: parent.bottom; - width: parent.width / tabButtonsContainer.numTabs; - - HiFiGlyphs { - id: securityTabIcon; - text: hifi.glyphs.lock; - // Size - size: 38; - // Anchors - anchors.horizontalCenter: parent.horizontalCenter; - anchors.top: parent.top; - anchors.topMargin: 2; - // Style - color: root.activeView === "security" || securityTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; - } - - RalewaySemiBold { - text: "SECURITY"; - // Text size - size: 16; - // Anchors - anchors.bottom: parent.bottom; - height: parent.height/2; - anchors.left: parent.left; - anchors.leftMargin: 4; - anchors.right: parent.right; - anchors.rightMargin: 4; - // Style - color: root.activeView === "security" || securityTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; - wrapMode: Text.WordWrap; - // Alignment - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignTop; - } - MouseArea { - id: securityTabMouseArea; - anchors.fill: parent; - hoverEnabled: enabled; - onClicked: { - root.activeView = "security"; - tabButtonsContainer.resetTabButtonColors(); - } - onEntered: parent.color = hifi.colors.blueHighlight; - onExited: parent.color = root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black; - } - } - // "HELP" tab button Rectangle { id: helpButtonContainer; visible: !walletSetup.visible; color: root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: securityButtonContainer.right; + anchors.left: sendMoneyButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; - + HiFiGlyphs { id: helpTabIcon; text: hifi.glyphs.question; From 317f4d28ba5e0ac11d19e1aaa8c141df21df5024 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 31 Oct 2018 15:41:51 -0700 Subject: [PATCH 065/125] always keep a workload view around MyAvatar --- libraries/workload/src/workload/ViewTask.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/libraries/workload/src/workload/ViewTask.cpp b/libraries/workload/src/workload/ViewTask.cpp index 9d0c693149..0db24f19ba 100644 --- a/libraries/workload/src/workload/ViewTask.cpp +++ b/libraries/workload/src/workload/ViewTask.cpp @@ -31,13 +31,25 @@ void SetupViews::run(const WorkloadContextPointer& renderContext, const Input& i auto& outViews = outputs; outViews.clear(); - // Filter the first view centerer on the avatar head if needed if (_views.size() >= 2) { + // when inputs contains two or more views: + // index 0 = view from avatar's head + // index 1 = view from camera + // index 2 and higher = secondary camera and whatever if (data.useAvatarView) { + // for debug purposes we keep the head view and skip that of the camera outViews.push_back(_views[0]); outViews.insert(outViews.end(), _views.begin() + 2, _views.end()); } else { - outViews.insert(outViews.end(), _views.begin() + 1, _views.end()); + // otherwise we use all of the views... + const float MIN_HEAD_CAMERA_SEPARATION_SQUARED = 0.2f; + if (glm::distance2(_views[0].origin, _views[1].origin) < MIN_HEAD_CAMERA_SEPARATION_SQUARED) { + // ... unless the first two are close enough to be considered the same + // in which case we only keep one of them + outViews.insert(outViews.end(), _views.begin() + 1, _views.end()); + } else { + outViews = _views; + } } } else { outViews = _views; @@ -177,7 +189,6 @@ void ControlViews::regulateViews(workload::Views& outViews, const workload::Timi outView.regionBackFronts[workload::Region::R1] = regionBackFronts[workload::Region::R1]; outView.regionBackFronts[workload::Region::R2] = regionBackFronts[workload::Region::R2]; outView.regionBackFronts[workload::Region::R3] = regionBackFronts[workload::Region::R3]; - workload::View::updateRegionsFromBackFronts(outView); } } From d4e9a7056409e7e071af495917d8be91312c5c19 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 31 Oct 2018 16:12:16 -0700 Subject: [PATCH 066/125] Almost there --- .../qml/LoginDialog/LinkAccountBody.qml | 15 ++- .../qml/hifi/commerce/wallet/Wallet.qml | 4 +- .../qml/hifi/commerce/wallet/WalletSetup.qml | 120 ------------------ .../qml/hifi/dialogs/security/Security.qml | 62 +++++++-- .../dialogs/security/SecurityImageChange.qml | 13 +- .../security/SecurityImageSelection.qml | 10 +- .../hifi/dialogs/security/SecurityWrapper.qml | 30 ----- interface/src/Application.cpp | 4 +- interface/src/Menu.cpp | 5 +- interface/src/commerce/Wallet.cpp | 2 +- 10 files changed, 82 insertions(+), 183 deletions(-) delete mode 100644 interface/resources/qml/hifi/dialogs/security/SecurityWrapper.qml diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index d5d89cd0b4..3c0577532a 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -239,7 +239,10 @@ Item { } - Keys.onReturnPressed: linkAccountBody.login() + Keys.onReturnPressed: { + Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text); + linkAccountBody.login(); + } } InfoItem { @@ -264,14 +267,14 @@ Item { CheckBox { id: autoLogoutCheckbox checked: Settings.getValue("keepMeLoggedIn", false) - text: "Keep me signed in" + text: "Keep me logged in" boxSize: 20; labelFontSize: 15 color: hifi.colors.black onCheckedChanged: { Settings.setValue("keepMeLoggedIn", checked); if (checked) { - Settings.setValue("keepMeLoggedIn/savedUsername", Account.username); + Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text); } else { Settings.setValue("keepMeLoggedIn/savedUsername", ""); } @@ -289,7 +292,10 @@ Item { text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Log in") color: hifi.buttons.blue - onClicked: linkAccountBody.login() + onClicked: { + Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text); + linkAccountBody.login(); + } } } @@ -403,6 +409,7 @@ Item { case Qt.Key_Enter: case Qt.Key_Return: event.accepted = true + Settings.setValue("keepMeLoggedIn/savedUsername", usernameField.text); linkAccountBody.login() break } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index f2119a1a8f..d032f060e2 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -142,7 +142,7 @@ Rectangle { Image { id: titleBarSecurityImage; source: ""; - visible: titleBarSecurityImage.source !== "" && !securityImageChange.visible; + visible: titleBarSecurityImage.source !== ""; anchors.right: parent.right; anchors.rightMargin: 6; anchors.top: parent.top; @@ -361,7 +361,7 @@ Rectangle { // Item { id: tabButtonsContainer; - visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendAssetStep"; + visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && sendMoney.currentActiveView !== "sendAssetStep"; property int numTabs: 4; // Size width: root.width; diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index dc6ce45a74..ecd7234400 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -243,7 +243,6 @@ Item { height: 50; text: "Set Up Wallet"; onClicked: { - securityImageSelection.initModel(); root.activeView = "step_2"; } } @@ -267,124 +266,6 @@ Item { // FIRST PAGE END // - // - // SECURITY IMAGE SELECTION START - // - Item { - id: securityImageContainer; - visible: root.activeView === "step_2"; - // Anchors - anchors.top: titleBarContainer.bottom; - anchors.topMargin: 30; - anchors.bottom: parent.bottom; - anchors.left: parent.left; - anchors.leftMargin: 16; - anchors.right: parent.right; - anchors.rightMargin: 16; - - // Text below title bar - RalewayRegular { - id: securityImageTitleHelper; - text: "Choose a Security Pic:"; - // Text size - size: 24; - // Anchors - anchors.top: parent.top; - anchors.left: parent.left; - height: 50; - width: paintedWidth; - // Style - color: hifi.colors.white; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - - SecurityImageSelection { - id: securityImageSelection; - // Anchors - anchors.top: securityImageTitleHelper.bottom; - anchors.left: parent.left; - anchors.right: parent.right; - height: 300; - - Connections { - onSendSignalToWallet: { - sendSignalToWallet(msg); - } - } - } - - // Text below security images - RalewayRegular { - text: "Your security picture shows you that the service asking for your passphrase is authorized. You can change your secure picture at any time."; - // Text size - size: 18; - // Anchors - anchors.top: securityImageSelection.bottom; - anchors.topMargin: 40; - anchors.left: parent.left; - anchors.right: parent.right; - height: paintedHeight; - // Style - color: hifi.colors.white; - wrapMode: Text.WordWrap; - // Alignment - horizontalAlignment: Text.AlignHLeft; - verticalAlignment: Text.AlignVCenter; - } - - // Navigation Bar - Item { - // Size - width: parent.width; - height: 50; - // Anchors: - anchors.left: parent.left; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 50; - - // "Back" button - HifiControlsUit.Button { - color: hifi.buttons.noneBorderlessWhite; - colorScheme: hifi.colorSchemes.dark; - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: parent.left; - anchors.leftMargin: 20; - width: 200; - text: "Back" - onClicked: { - securityImageSelection.resetSelection(); - root.activeView = "step_1"; - } - } - - // "Next" button - HifiControlsUit.Button { - enabled: securityImageSelection.currentIndex !== -1; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.right: parent.right; - anchors.rightMargin: 20; - width: 200; - text: "Next"; - onClicked: { - root.lastPage = "step_2"; - var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex()) - Commerce.chooseSecurityImage(securityImagePath); - root.activeView = "step_3"; - passphraseSelection.clearPassphraseFields(); - } - } - } - } - // - // SECURITY IMAGE SELECTION END - // - // // SECURE PASSPHRASE SELECTION START // @@ -525,7 +406,6 @@ Item { width: 200; text: "Back" onClicked: { - securityImageSelection.resetSelection(); root.lastPage = "step_3"; root.activeView = "step_2"; } diff --git a/interface/resources/qml/hifi/dialogs/security/Security.qml b/interface/resources/qml/hifi/dialogs/security/Security.qml index d1dc2a9e11..3f4d17e838 100644 --- a/interface/resources/qml/hifi/dialogs/security/Security.qml +++ b/interface/resources/qml/hifi/dialogs/security/Security.qml @@ -29,6 +29,17 @@ Rectangle { property string title: "Security Settings"; property bool walletSetUp; + + QtObject { + id: margins + property real paddings: root.width / 20.25 + + property real sizeCheckBox: root.width / 13.5 + property real sizeText: root.width / 2.5 + property real sizeLevel: root.width / 5.8 + property real sizeDesktop: root.width / 5.8 + property real sizeVR: root.width / 13.5 + } Connections { target: Commerce; @@ -50,16 +61,37 @@ Rectangle { } } + Component.onCompleted: { + Commerce.getWalletStatus(); + } + HifiCommerceCommon.CommerceLightbox { + z: 996; id: lightboxPopup; visible: false; anchors.fill: parent; } + SecurityImageChange { + id: securityImageChange; + visible: false; + z: 997; + anchors.top: usernameText.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + + Connections { + onSendSignalToParent: { + securityImageChange.visible = false; + } + } + } + // Username Text HifiStylesUit.RalewayRegular { id: usernameText; - text: Account.username === "" ? Account.username : "Please Log In"; + text: Account.username === "Unknown user" ? "Please Log In" : Account.username; // Text size size: 24; // Style @@ -71,12 +103,12 @@ Rectangle { anchors.leftMargin: 20; anchors.right: parent.right; anchors.rightMargin: 20; - height: 80; + height: 60; } Item { id: pleaseLogInContainer; - visible: Account.username === ""; + visible: Account.username === "Unknown user"; anchors.top: usernameText.bottom; anchors.left: parent.left; anchors.right: parent.right; @@ -94,7 +126,7 @@ Rectangle { anchors.right: parent.right; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; - height: 80; + height: 60; } HifiControlsUit.Button { @@ -104,7 +136,7 @@ Rectangle { anchors.centerIn: parent; width: 140; height: 40; - text: "Change"; + text: "Log In"; onClicked: { DialogsManager.showLoginDialog(); } @@ -131,13 +163,15 @@ Rectangle { anchors.top: parent.top; anchors.left: parent.left; anchors.right: parent.right; - height: 80; + height: 70; color: hifi.colors.baseGrayHighlight; HifiStylesUit.RalewaySemiBold { text: "Account"; anchors.fill: parent; anchors.leftMargin: 20; + color: hifi.colors.white; + size: 18; } } @@ -156,9 +190,11 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter; anchors.left: parent.left; anchors.leftMargin: 20; - boxSize: 28; + boxSize: 24; labelFontSize: 18; + colorScheme: hifi.colorSchemes.dark color: hifi.colors.white; + width: 240; onCheckedChanged: { Settings.setValue("keepMeLoggedIn", checked); if (checked) { @@ -218,13 +254,15 @@ Rectangle { anchors.top: parent.top; anchors.left: parent.left; anchors.right: parent.right; - height: 80; + height: 70; color: hifi.colors.baseGrayHighlight; HifiStylesUit.RalewaySemiBold { text: "Wallet"; anchors.fill: parent; anchors.leftMargin: 20; + color: hifi.colors.white; + size: 18; } } @@ -249,7 +287,7 @@ Rectangle { cache: false; } - HifiStylesUit.RalewayRegular { + HifiStylesUit.RalewaySemiBold { id: securityPictureText; text: "Wallet Security Picture"; // Anchors @@ -270,12 +308,14 @@ Rectangle { color: hifi.buttons.white; colorScheme: hifi.colorSchemes.dark; anchors.left: securityPictureText.right; + anchors.leftMargin: 12; anchors.verticalCenter: parent.verticalCenter; width: 140; height: 40; text: "Change"; onClicked: { - + securityImageChange.visible = true; + securityImageChange.initModel(); } } } @@ -286,7 +326,7 @@ Rectangle { anchors.top: walletHeaderContainer.bottom; anchors.left: parent.left; anchors.right: parent.right; - height: 80; + height: 60; HifiStylesUit.RalewayRegular { text: "Your wallet is not set up.\n" + diff --git a/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml b/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml index d953a764fd..0007b98291 100644 --- a/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml +++ b/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml @@ -19,10 +19,11 @@ import "qrc:////qml//controls" as HifiControls // references XXX from root context -Item { - HifiConstants { id: hifi; } +Rectangle { + HifiStylesUit.HifiConstants { id: hifi; } id: root; + color: hifi.colors.darkGray; property bool justSubmitted: false; Connections { @@ -72,7 +73,7 @@ Item { anchors.bottom: parent.bottom; height: 22; // Lock icon - HiFiGlyphs { + HifiStylesUit.HiFiGlyphs { id: lockIcon; text: hifi.glyphs.lock; anchors.bottom: parent.bottom; @@ -84,9 +85,9 @@ Item { color: hifi.colors.white; } // "Security image" text below image - RalewayRegular { + HifiStylesUit.RalewayRegular { id: securityImageText; - text: "SECURITY IMAGE"; + text: "SECURITY PIC"; // Text size size: 12; // Anchors @@ -116,7 +117,7 @@ Item { anchors.bottom: parent.bottom; // "Change Security Image" text - RalewaySemiBold { + HifiStylesUit.RalewaySemiBold { id: securityImageTitle; text: "Change Security Image:"; // Text size diff --git a/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml b/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml index 5a05a28ba3..f058aad40a 100644 --- a/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml +++ b/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml @@ -20,7 +20,7 @@ import "qrc:////qml//controls" as HifiControls // references XXX from root context Item { - HifiConstants { id: hifi; } + HifiStylesUit.HifiConstants { id: hifi; } id: root; property alias currentIndex: securityImageGrid.currentIndex; @@ -64,10 +64,10 @@ Item { } } highlight: Rectangle { - width: securityImageGrid.cellWidth; - height: securityImageGrid.cellHeight; - color: hifi.colors.blueHighlight; - } + width: securityImageGrid.cellWidth; + height: securityImageGrid.cellHeight; + color: hifi.colors.blueHighlight; + } } // diff --git a/interface/resources/qml/hifi/dialogs/security/SecurityWrapper.qml b/interface/resources/qml/hifi/dialogs/security/SecurityWrapper.qml deleted file mode 100644 index 7c17818225..0000000000 --- a/interface/resources/qml/hifi/dialogs/security/SecurityWrapper.qml +++ /dev/null @@ -1,30 +0,0 @@ -// -// SecurityWrapper.qml -// qml\hifi\dialogs\security -// -// SecurityWrapper -// -// Created by Zach Fox on 2018-10-31 -// Copyright 2018 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 -// - -import "qrc:////qml//windows" -import "./" - -ScrollingWindow { - id: root; - - resizable: true; - destroyOnHidden: true; - width: 400; - height: 577; - minSize: Qt.vector2d(400, 500); - - Security { id: security; width: root.width } - - objectName: "SecurityDialog"; - title: security.title; -} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1ca71a70d9..245e6c0017 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2567,8 +2567,8 @@ void Application::cleanupBeforeQuit() { } DependencyManager::destroy(); - bool autoLogout = Setting::Handle(KEEP_ME_LOGGED_IN_SETTING_NAME, false).get(); - if (autoLogout) { + bool keepMeLoggedIn = Setting::Handle(KEEP_ME_LOGGED_IN_SETTING_NAME, false).get(); + if (!keepMeLoggedIn) { DependencyManager::get()->removeAccountFromFile(); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e16da6781d..3636a2f45a 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -268,8 +268,9 @@ Menu::Menu() { // Settings > Security... action = addActionToQMenuAndActionHash(settingsMenu, "Security..."); connect(action, &QAction::triggered, [] { - qApp->showDialog(QString("hifi/dialogs/security/SecurityWrapper.qml"), - QString("hifi/dialogs/security/Security.qml"), "SecurityDialog"); + auto tablet = dynamic_cast( + DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")); + tablet->loadQMLSource(QString("hifi/dialogs/security/Security.qml")); }); // Settings > Developer Menu diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 5b8417be7c..0e9ad7d79a 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -687,7 +687,7 @@ void Wallet::chooseSecurityImage(const QString& filename) { delete _securityImage; } QString path = PathUtils::resourcesPath(); - path.append("/qml/hifi/commerce/wallet/"); + path.append("/qml/hifi/dialogs/security/"); path.append(filename); // now create a new security image pixmap From 162a08b9935986a46caaf86648191186e1637d95 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 31 Oct 2018 16:14:26 -0700 Subject: [PATCH 067/125] Restore certain images --- .../wallet}/images/lowerKeyboard.png | Bin .../wallet}/images/wallet-bg.jpg | Bin .../wallet}/images/wallet-tip-bg.png | Bin 3 files changed, 0 insertions(+), 0 deletions(-) rename interface/resources/qml/hifi/{dialogs/security => commerce/wallet}/images/lowerKeyboard.png (100%) rename interface/resources/qml/hifi/{dialogs/security => commerce/wallet}/images/wallet-bg.jpg (100%) rename interface/resources/qml/hifi/{dialogs/security => commerce/wallet}/images/wallet-tip-bg.png (100%) diff --git a/interface/resources/qml/hifi/dialogs/security/images/lowerKeyboard.png b/interface/resources/qml/hifi/commerce/wallet/images/lowerKeyboard.png similarity index 100% rename from interface/resources/qml/hifi/dialogs/security/images/lowerKeyboard.png rename to interface/resources/qml/hifi/commerce/wallet/images/lowerKeyboard.png diff --git a/interface/resources/qml/hifi/dialogs/security/images/wallet-bg.jpg b/interface/resources/qml/hifi/commerce/wallet/images/wallet-bg.jpg similarity index 100% rename from interface/resources/qml/hifi/dialogs/security/images/wallet-bg.jpg rename to interface/resources/qml/hifi/commerce/wallet/images/wallet-bg.jpg diff --git a/interface/resources/qml/hifi/dialogs/security/images/wallet-tip-bg.png b/interface/resources/qml/hifi/commerce/wallet/images/wallet-tip-bg.png similarity index 100% rename from interface/resources/qml/hifi/dialogs/security/images/wallet-tip-bg.png rename to interface/resources/qml/hifi/commerce/wallet/images/wallet-tip-bg.png From a1eee351e94e292cc34d0c752f859cef4d3f8af8 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 31 Oct 2018 16:24:57 -0700 Subject: [PATCH 068/125] Change "Proofs" to "My Submissions" in assets filter. --- interface/resources/qml/hifi/commerce/purchases/Purchases.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 41453c9790..ced5450290 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -498,7 +498,7 @@ Rectangle { "filterName": "updated" }, { - "displayName": "Proofs", + "displayName": "My Submissions", "filterName": "proofs" } ] From a4b345bb583eca092e625dd0ef93c6cca6660bb3 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Nov 2018 09:25:46 -0700 Subject: [PATCH 069/125] fix interstitial domain loading due to bad texture url --- libraries/model-networking/src/model-networking/ModelCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index e96815d391..2686f1c9a8 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -551,7 +551,7 @@ graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel) { auto textureCache = DependencyManager::get(); - if (textureCache) { + if (textureCache && !url.isEmpty()) { auto texture = textureCache->getTexture(url, type); _textures[channel].texture = texture; From cfec94f3fffe56e7a184b7674fa9e5abf690970c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 1 Nov 2018 09:28:31 -0700 Subject: [PATCH 070/125] larger threshold btw 1 vs 2 workload views for 3rd person --- libraries/workload/src/workload/ViewTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/workload/src/workload/ViewTask.cpp b/libraries/workload/src/workload/ViewTask.cpp index 0db24f19ba..0a268df9fc 100644 --- a/libraries/workload/src/workload/ViewTask.cpp +++ b/libraries/workload/src/workload/ViewTask.cpp @@ -42,7 +42,7 @@ void SetupViews::run(const WorkloadContextPointer& renderContext, const Input& i outViews.insert(outViews.end(), _views.begin() + 2, _views.end()); } else { // otherwise we use all of the views... - const float MIN_HEAD_CAMERA_SEPARATION_SQUARED = 0.2f; + const float MIN_HEAD_CAMERA_SEPARATION_SQUARED = MIN_VIEW_BACK_FRONTS[0][1] * MIN_VIEW_BACK_FRONTS[0][1]; if (glm::distance2(_views[0].origin, _views[1].origin) < MIN_HEAD_CAMERA_SEPARATION_SQUARED) { // ... unless the first two are close enough to be considered the same // in which case we only keep one of them From d481f081bbf163086a1835e8095b5110e86a1092 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 1 Nov 2018 10:29:38 -0700 Subject: [PATCH 071/125] Fix four bugs; truncate some logs --- .../hifi/commerce/purchases/PurchasedItem.qml | 5 +-- .../qml/hifi/commerce/wallet/Wallet.qml | 6 --- .../qml/hifi/commerce/wallet/WalletHome.qml | 22 ---------- scripts/modules/appUi.js | 41 ++++++++++++------- scripts/system/commerce/wallet.js | 12 ++++++ 5 files changed, 40 insertions(+), 46 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index d13473af22..0828d86eff 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -47,8 +47,7 @@ Item { property string wornEntityID; property string upgradeUrl; property string upgradeTitle; - property bool updateAvailable: root.upgradeUrl !== "" && !root.isShowingMyItems; - property bool isShowingMyItems; + property bool updateAvailable: root.upgradeUrl !== ""; property bool valid; property string originalStatusText; @@ -231,7 +230,7 @@ Item { Loader { id: giftButton; - visible: !root.isShowingMyItems; + visible: root.itemEdition > 0; sourceComponent: contextCardButton; anchors.right: parent.right; anchors.top: parent.top; diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index e1e58bf7c7..e17faef194 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -355,10 +355,6 @@ Rectangle { if (msg.method === 'transactionHistory_usernameLinkClicked') { userInfoViewer.url = msg.usernameLink; userInfoViewer.visible = true; - } else if (msg.method === 'goToPurchases_fromWalletHome') { - root.activeView = "walletInventory"; - walletInventory.isShowingMyItems = false; - tabButtonsContainer.resetTabButtonColors(); } else { sendToScript(msg); } @@ -630,7 +626,6 @@ Rectangle { hoverEnabled: enabled; onClicked: { root.activeView = "walletInventory"; - walletInventory.isShowingMyItems = false; tabButtonsContainer.resetTabButtonColors(); } onEntered: parent.color = hifi.colors.blueHighlight; @@ -961,7 +956,6 @@ Rectangle { Commerce.getWalletStatus(); } else if (msg.referrer === 'purchases') { root.activeView = "walletInventory"; - walletInventory.isShowingMyItems = false; tabButtonsContainer.resetTabButtonColors(); } else if (msg.referrer === 'marketplace cta' || msg.referrer === 'mainPage') { sendToScript({method: 'goToMarketplaceMainPage', itemId: msg.referrer}); diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 246773ff07..d32017189e 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -179,28 +179,6 @@ Item { color: hifi.colors.baseGrayHighlight; } - RalewaySemiBold { - id: myPurchasesLink; - text: 'Inventory'; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 26; - anchors.right: parent.right; - anchors.rightMargin: 20; - width: paintedWidth; - height: 30; - y: 4; - // Text size - size: 18; - // Style - color: hifi.colors.baseGrayHighlight; - horizontalAlignment: Text.AlignRight; - - onLinkActivated: { - sendSignalToWallet({method: 'goToPurchases_fromWalletHome'}); - } - } - HifiModels.PSFListModel { id: transactionHistoryModel; property int lastPendingCount: 0; diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index c340dfecd2..e267293977 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -129,7 +129,9 @@ function AppUi(properties) { } that.isOpen = true; } - } else { // Not us. Should we do something for type Home, Menu, and particularly Closed (meaning tablet hidden? + } else { + // A different screen is now visible, or the tablet has been closed. + // Tablet visibility is controlled separately by `tabletShownChanged()` that.wireEventBridge(false); if (that.isOpen) { that.buttonActive(false); @@ -139,12 +141,12 @@ function AppUi(properties) { that.isOpen = false; } } - // console.debug(that.buttonName + " app reports: Tablet screen changed.\nNew screen type: " + type + - // "\nNew screen URL: " + url + "\nCurrent app open status: " + that.isOpen + "\n"); }; // Overwrite with the given properties: - Object.keys(properties).forEach(function (key) { that[key] = properties[key]; }); + Object.keys(properties).forEach(function (key) { + that[key] = properties[key]; + }); // // START Notification Handling @@ -157,22 +159,20 @@ function AppUi(properties) { concatenatedServerResponse[i] = new Array(); } + var MAX_LOG_LENGTH_CHARACTERS = 300; function requestCallback(error, response, optionalParams) { var indexOfRequest = optionalParams.indexOfRequest; var urlOfRequest = optionalParams.urlOfRequest; if (error || (response.status !== 'success')) { print("Error: unable to get", urlOfRequest, error || response.status); - that.notificationPollTimeout[indexOfRequest] = Script.setTimeout( - that.notificationPoll(indexOfRequest), that.notificationPollTimeoutMs[indexOfRequest]); + startNotificationTimer(indexOfRequest); return; } if (!that.notificationPollStopPaginatingConditionMet[indexOfRequest] || that.notificationPollStopPaginatingConditionMet[indexOfRequest](response)) { - that.notificationPollTimeout[indexOfRequest] = Script.setTimeout(function () { - that.notificationPoll(indexOfRequest); - }, that.notificationPollTimeoutMs[indexOfRequest]); + startNotificationTimer(indexOfRequest); var notificationData; if (concatenatedServerResponse[indexOfRequest].length) { @@ -181,7 +181,8 @@ function AppUi(properties) { notificationData = that.notificationDataProcessPage[indexOfRequest](response); } console.debug(that.buttonName, that.notificationPollEndpoint[indexOfRequest], - 'notification data for processing:', JSON.stringify(notificationData)); + 'truncated notification data for processing:', + JSON.stringify(notificationData).substring(0, MAX_LOG_LENGTH_CHARACTERS)); that.notificationPollCallback[indexOfRequest](notificationData); that.notificationInitialCallbackMade[indexOfRequest] = true; currentDataPageToRetrieve[indexOfRequest] = 1; @@ -199,15 +200,19 @@ function AppUi(properties) { var METAVERSE_BASE = Account.metaverseServerURL; + var MS_IN_SEC = 1000; that.notificationPoll = function (i) { if (!that.notificationPollEndpoint[i]) { return; } - // User is "appearing offline" or is offline - if (GlobalServices.findableBy === "none" || Account.username === "") { - that.notificationPollTimeout[i] = Script.setTimeout( - that.notificationPoll(i), that.notificationPollTimeoutMs[i]); + // User is "appearing offline" or is not logged in + if (GlobalServices.findableBy === "none" || Account.username === "Unknown user") { + // The notification polling will restart when the user changes their availability + // or when they log in, so it's not necessary to restart a timer here. + console.debug(that.buttonName + " Notifications: User is appearing offline or not logged in. " + + that.buttonName + " will poll for notifications when user logs in and has their availability " + + "set to not appear offline."); return; } @@ -217,7 +222,7 @@ function AppUi(properties) { var currentTimestamp = new Date().getTime(); var lastPollTimestamp = Settings.getValue(settingsKey, currentTimestamp); if (that.notificationPollCaresAboutSince[i]) { - url = url + "&since=" + lastPollTimestamp / 1000; + url = url + "&since=" + lastPollTimestamp / MS_IN_SEC; } Settings.setValue(settingsKey, currentTimestamp); @@ -239,6 +244,12 @@ function AppUi(properties) { that.notificationPoll(i); } + function startNotificationTimer(indexOfRequest) { + that.notificationPollTimeout[indexOfRequest] = Script.setTimeout(function () { + that.notificationPoll(indexOfRequest); + }, that.notificationPollTimeoutMs[indexOfRequest]); + } + function restartNotificationPoll() { for (var j = 0; j < that.notificationPollEndpoint.length; j++) { that.notificationInitialCallbackMade[j] = false; diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 2ae30b5b35..bc7bccd94f 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -401,6 +401,14 @@ function openMarketplace(optionalItemOrUrl) { ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL); } +function setCertificateInfo(itemCertificateId) { + ui.tablet.sendToQml({ + method: 'inspectionCertificate_setCertificateId', + entityId: "", + certificateId: itemCertificateId + }); +} + // Function Name: fromQml() // // Description: @@ -521,6 +529,9 @@ function fromQml(message) { openMarketplace(itemId); } break; + case 'purchases_itemCertificateClicked': + setCertificateInfo(message.itemCertificateId); + break; case 'clearShouldShowDotHistory': shouldShowDotHistory = false; ui.messagesWaiting(shouldShowDotUpdates || shouldShowDotHistory); @@ -698,6 +709,7 @@ function off() { Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); triggerMapping.disable(); triggerPressMapping.disable(); + isWired = false; } if (isUpdateOverlaysWired) { From 4a56f769e5b133e2ce8c67cf87274939a732ffcb Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 1 Nov 2018 11:04:59 -0700 Subject: [PATCH 072/125] Re-add some code I shouldn't have removed --- scripts/system/html/js/marketplacesInject.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index eda37c06b7..db433c5058 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -264,6 +264,13 @@ "font-weight": "600", "line-height": "34px" }); + + if (parseInt(cost) > 0) { + priceElement.css({ "width": "auto" }); + priceElement.html(' ' + cost); + priceElement.css({ "min-width": priceElement.width() + 30 }); + } }); // change pricing to GET/BUY on button hover @@ -382,6 +389,9 @@ var cost = $('.item-cost').text(); var costInt = parseInt(cost, 10); var availability = $.trim($('.item-availability').text()); + if (limitedCommerce && (costInt > 0)) { + availability = ''; + } if (availability === 'available') { purchaseButton.css({ "background": "linear-gradient(#00b4ef, #0093C5)", From 3e2d6c341b6b2cfcf142664d99b3400bff01f8e5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 1 Nov 2018 11:45:25 -0700 Subject: [PATCH 073/125] placeholder svgs --- .../tablet-icons/{wallet-a-msg.svg => inventory-a-msg.svg} | 0 interface/resources/icons/tablet-icons/inventory-a.svg | 3 +++ .../tablet-icons/{wallet-i-msg.svg => inventory-i-msg.svg} | 0 .../icons/tablet-icons/{wallet-i.svg => inventory-i.svg} | 0 interface/resources/icons/tablet-icons/wallet-a.svg | 1 - 5 files changed, 3 insertions(+), 1 deletion(-) rename interface/resources/icons/tablet-icons/{wallet-a-msg.svg => inventory-a-msg.svg} (100%) create mode 100644 interface/resources/icons/tablet-icons/inventory-a.svg rename interface/resources/icons/tablet-icons/{wallet-i-msg.svg => inventory-i-msg.svg} (100%) rename interface/resources/icons/tablet-icons/{wallet-i.svg => inventory-i.svg} (100%) delete mode 100644 interface/resources/icons/tablet-icons/wallet-a.svg diff --git a/interface/resources/icons/tablet-icons/wallet-a-msg.svg b/interface/resources/icons/tablet-icons/inventory-a-msg.svg similarity index 100% rename from interface/resources/icons/tablet-icons/wallet-a-msg.svg rename to interface/resources/icons/tablet-icons/inventory-a-msg.svg diff --git a/interface/resources/icons/tablet-icons/inventory-a.svg b/interface/resources/icons/tablet-icons/inventory-a.svg new file mode 100644 index 0000000000..8b6f34eaa3 --- /dev/null +++ b/interface/resources/icons/tablet-icons/inventory-a.svg @@ -0,0 +1,3 @@ + + + diff --git a/interface/resources/icons/tablet-icons/wallet-i-msg.svg b/interface/resources/icons/tablet-icons/inventory-i-msg.svg similarity index 100% rename from interface/resources/icons/tablet-icons/wallet-i-msg.svg rename to interface/resources/icons/tablet-icons/inventory-i-msg.svg diff --git a/interface/resources/icons/tablet-icons/wallet-i.svg b/interface/resources/icons/tablet-icons/inventory-i.svg similarity index 100% rename from interface/resources/icons/tablet-icons/wallet-i.svg rename to interface/resources/icons/tablet-icons/inventory-i.svg diff --git a/interface/resources/icons/tablet-icons/wallet-a.svg b/interface/resources/icons/tablet-icons/wallet-a.svg deleted file mode 100644 index 50ea64848f..0000000000 --- a/interface/resources/icons/tablet-icons/wallet-a.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 01b29185ca50c7cf2ce6d966193a99c9afcdbf7b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 1 Nov 2018 09:22:56 -0700 Subject: [PATCH 074/125] Update the default edit.js collidesWith to include myAvatar --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6425806771..c3f8f1f4e7 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -278,7 +278,7 @@ const DEFAULT_ENTITY_PROPERTIES = { All: { description: "", rotation: { x: 0, y: 0, z: 0, w: 1 }, - collidesWith: "static,dynamic,kinematic,otherAvatar", + collidesWith: "static,dynamic,kinematic,otherAvatar,myAvatar", collisionSoundURL: "", cloneable: false, ignoreIK: true, From d6477993a160e7f8c667849a645c523260c0173b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 1 Nov 2018 13:08:00 -0700 Subject: [PATCH 075/125] Make it work better with the tablet --- interface/src/Menu.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3636a2f45a..16e8af5683 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -268,9 +268,13 @@ Menu::Menu() { // Settings > Security... action = addActionToQMenuAndActionHash(settingsMenu, "Security..."); connect(action, &QAction::triggered, [] { - auto tablet = dynamic_cast( - DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")); - tablet->loadQMLSource(QString("hifi/dialogs/security/Security.qml")); + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + auto hmd = DependencyManager::get(); + tablet->pushOntoStack("hifi/dialogs/security/Security.qml"); + + if (!hmd->getShouldShowTablet()) { + hmd->toggleShouldShowTablet(); + } }); // Settings > Developer Menu From 0a15e94fe4de1088806af17489859066c7856171 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 1 Nov 2018 13:27:31 -0700 Subject: [PATCH 076/125] New colors --- .../resources/qml/hifi/dialogs/security/Security.qml | 8 +++----- .../qml/hifi/dialogs/security/SecurityImageChange.qml | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/security/Security.qml b/interface/resources/qml/hifi/dialogs/security/Security.qml index 3f4d17e838..8baff0ac13 100644 --- a/interface/resources/qml/hifi/dialogs/security/Security.qml +++ b/interface/resources/qml/hifi/dialogs/security/Security.qml @@ -19,13 +19,11 @@ import "qrc:////qml//controls-uit" as HifiControlsUit import "qrc:////qml//controls" as HifiControls import "qrc:////qml//hifi//commerce//common" as HifiCommerceCommon -// references XXX from root context - Rectangle { HifiStylesUit.HifiConstants { id: hifi; } id: root; - color: hifi.colors.darkGray; + color: hifi.colors.baseGray; property string title: "Security Settings"; property bool walletSetUp; @@ -163,7 +161,7 @@ Rectangle { anchors.top: parent.top; anchors.left: parent.left; anchors.right: parent.right; - height: 70; + height: 55; color: hifi.colors.baseGrayHighlight; HifiStylesUit.RalewaySemiBold { @@ -254,7 +252,7 @@ Rectangle { anchors.top: parent.top; anchors.left: parent.left; anchors.right: parent.right; - height: 70; + height: 55; color: hifi.colors.baseGrayHighlight; HifiStylesUit.RalewaySemiBold { diff --git a/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml b/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml index 0007b98291..96d75e340b 100644 --- a/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml +++ b/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml @@ -23,7 +23,7 @@ Rectangle { HifiStylesUit.HifiConstants { id: hifi; } id: root; - color: hifi.colors.darkGray; + color: hifi.colors.baseGray; property bool justSubmitted: false; Connections { From 4593b33435ea1a73d7c011e0ac1e33f692cf876f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 1 Nov 2018 14:23:21 -0700 Subject: [PATCH 077/125] Fix new models not obeying dynamic checkbox --- scripts/system/edit.js | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6425806771..43b82c8615 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -484,23 +484,28 @@ var toolBar = (function () { originalProperties[key] = newProperties[key]; } } - function createNewEntity(properties) { - var dimensions = properties.dimensions ? properties.dimensions : DEFAULT_DIMENSIONS; + function createNewEntity(requestedProperties) { + var dimensions = requestedProperties.dimensions ? requestedProperties.dimensions : DEFAULT_DIMENSIONS; var position = getPositionToCreateEntity(); var entityID = null; + var properties = {}; + applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.All); - var type = properties.type; + var type = requestedProperties.type; if (type == "Box" || type == "Sphere") { applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.Shape); } else if (type == "Image") { - properties.type = "Model"; + requestedProperties.type = "Model"; applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.Image); } else { applyProperties(properties, DEFAULT_ENTITY_PROPERTIES[type]); } + // We apply the requested properties first so that they take priority over any default properties. + applyProperties(properties, requestedProperties); + if (position !== null && position !== undefined) { var direction; @@ -845,41 +850,18 @@ var toolBar = (function () { addButton("newCubeButton", function () { createNewEntity({ type: "Box", - dimensions: DEFAULT_DIMENSIONS, - color: { - red: 255, - green: 0, - blue: 0 - } }); }); addButton("newSphereButton", function () { createNewEntity({ type: "Sphere", - dimensions: DEFAULT_DIMENSIONS, - color: { - red: 255, - green: 0, - blue: 0 - } }); }); addButton("newLightButton", function () { createNewEntity({ type: "Light", - isSpotlight: false, - color: { - red: 150, - green: 150, - blue: 150 - }, - constantAttenuation: 1, - linearAttenuation: 0, - quadraticAttenuation: 0, - exponent: 0, - cutoff: 180 // in degrees }); }); From bccb3f1de9ea68e6d722d77924be490854ca04e6 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 1 Nov 2018 14:53:10 -0700 Subject: [PATCH 078/125] Prepare for further work --- scripts/defaultScripts.js | 3 +-- scripts/system/tts/TTS.js | 28 ---------------------------- scripts/system/tts/tts-a.svg | 9 --------- scripts/system/tts/tts-i.svg | 9 --------- 4 files changed, 1 insertion(+), 48 deletions(-) delete mode 100644 scripts/system/tts/TTS.js delete mode 100644 scripts/system/tts/tts-a.svg delete mode 100644 scripts/system/tts/tts-i.svg diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 5ed74fd833..5df1b3e511 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -32,8 +32,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/firstPersonHMD.js", "system/tablet-ui/tabletUI.js", "system/emote.js", - "system/miniTablet.js", - "system/tts/TTS.js" + "system/miniTablet.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", diff --git a/scripts/system/tts/TTS.js b/scripts/system/tts/TTS.js deleted file mode 100644 index 36259cfda0..0000000000 --- a/scripts/system/tts/TTS.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -/*jslint vars:true, plusplus:true, forin:true*/ -/*global Tablet, Script, */ -/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ -// -// TTS.js -// -// Created by Zach Fox on 2018-10-10 -// Copyright 2018 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 -// - -(function () { // BEGIN LOCAL_SCOPE -var AppUi = Script.require('appUi'); - -var ui; -function startup() { - ui = new AppUi({ - buttonName: "TTS", - //home: Script.resolvePath("TTS.qml") - home: "hifi/tts/TTS.qml", - graphicsDirectory: Script.resolvePath("./") // speech by Danil Polshin from the Noun Project - }); -} -startup(); -}()); // END LOCAL_SCOPE diff --git a/scripts/system/tts/tts-a.svg b/scripts/system/tts/tts-a.svg deleted file mode 100644 index 9dac3a2d53..0000000000 --- a/scripts/system/tts/tts-a.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/scripts/system/tts/tts-i.svg b/scripts/system/tts/tts-i.svg deleted file mode 100644 index 1c52ec3193..0000000000 --- a/scripts/system/tts/tts-i.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - From 8256bfbc554b06d2d752f55403f2239729801d6c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 1 Nov 2018 15:20:50 -0700 Subject: [PATCH 079/125] icons --- .../icons/tablet-icons/inventory-a-msg.svg | 10 ++--- .../icons/tablet-icons/inventory-i-msg.svg | 18 ++------- .../icons/tablet-icons/inventory-i.svg | 17 ++------- .../qml/hifi/commerce/wallet/Wallet.qml | 38 +++++++++++-------- .../commerce/wallet/images/items-tab-a.svg | 8 ++++ 5 files changed, 40 insertions(+), 51 deletions(-) create mode 100644 interface/resources/qml/hifi/commerce/wallet/images/items-tab-a.svg diff --git a/interface/resources/icons/tablet-icons/inventory-a-msg.svg b/interface/resources/icons/tablet-icons/inventory-a-msg.svg index d51c3e99a2..794bd1e414 100644 --- a/interface/resources/icons/tablet-icons/inventory-a-msg.svg +++ b/interface/resources/icons/tablet-icons/inventory-a-msg.svg @@ -1,6 +1,4 @@ - - - - \ No newline at end of file + + + + diff --git a/interface/resources/icons/tablet-icons/inventory-i-msg.svg b/interface/resources/icons/tablet-icons/inventory-i-msg.svg index 676f97a966..35d4fb54ae 100644 --- a/interface/resources/icons/tablet-icons/inventory-i-msg.svg +++ b/interface/resources/icons/tablet-icons/inventory-i-msg.svg @@ -1,16 +1,4 @@ - - - - - - - - + + + diff --git a/interface/resources/icons/tablet-icons/inventory-i.svg b/interface/resources/icons/tablet-icons/inventory-i.svg index 4e27e41b44..071fabce88 100644 --- a/interface/resources/icons/tablet-icons/inventory-i.svg +++ b/interface/resources/icons/tablet-icons/inventory-i.svg @@ -1,14 +1,3 @@ - - - - - - - - + + + diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 4124200535..6383677b84 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -126,16 +126,19 @@ Rectangle { anchors.top: parent.top; // Wallet icon - HiFiGlyphs { + Image { id: walletIcon; - text: hifi.glyphs.wallet; - // Size - size: parent.height * 0.8; - // Anchors + source: "../../../../icons/tablet-icons/inventory-a.svg"; + height: parent.height * 0.5; + width: walletIcon.height; anchors.left: parent.left; anchors.leftMargin: 8; anchors.verticalCenter: parent.verticalCenter; - // Style + visible: false; // When we use a white .svg instead of a glyph with color property, we set to invisible and use the following ColorOverlay. + } + ColorOverlay { + anchors.fill: walletIcon; + source: walletIcon; color: hifi.colors.blueHighlight; } @@ -148,7 +151,7 @@ Rectangle { // Anchors anchors.top: parent.top; anchors.left: walletIcon.right; - anchors.leftMargin: 4; + anchors.leftMargin: 6; anchors.bottom: parent.bottom; width: paintedWidth; // Style @@ -575,16 +578,19 @@ Rectangle { anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; - HiFiGlyphs { + Image { id: exchangeMoneyTabIcon; - text: hifi.glyphs.home2; - // Size - size: 50; - // Anchors + source: "images/items-tab-a.svg"; + height: 25; + width: exchangeMoneyTabIcon.height; anchors.horizontalCenter: parent.horizontalCenter; anchors.top: parent.top; - anchors.topMargin: -2; - // Style + anchors.topMargin: 10; + visible: false; // When we use a white .svg instead of a glyph with color property, we set to invisible and use the following ColorOverlay. + } + ColorOverlay { + anchors.fill: exchangeMoneyTabIcon; + source: exchangeMoneyTabIcon; color: root.activeView === "walletInventory" || inventoryTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; } @@ -592,9 +598,9 @@ Rectangle { id: exchangeMoneyMessagesWaitingLight; visible: parent.messagesWaiting; anchors.right: exchangeMoneyTabIcon.left; - anchors.rightMargin: -4; + anchors.rightMargin: 10; anchors.top: exchangeMoneyTabIcon.top; - anchors.topMargin: 16; + anchors.topMargin: 4; height: 10; width: height; radius: height/2; diff --git a/interface/resources/qml/hifi/commerce/wallet/images/items-tab-a.svg b/interface/resources/qml/hifi/commerce/wallet/images/items-tab-a.svg new file mode 100644 index 0000000000..7474f4ed57 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/images/items-tab-a.svg @@ -0,0 +1,8 @@ + + + + + + + + From 1f8dde67568f80e70cc888200535f2442cc1792c Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Thu, 1 Nov 2018 15:37:16 -0700 Subject: [PATCH 080/125] Transaction string was assuming items given to the marketplace/highfidelity were Gifts and was reporting them as such. Items given to the marketplace are in exchange for updates, and the memo indicates such. --- interface/src/commerce/Ledger.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 67303f2a9b..60cd96c0ca 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -219,7 +219,11 @@ QString transactionString(const QJsonObject& valueObject) { if (!message.isEmpty()) { result += QString("
with memo: \"%1\"").arg(message); } - } else if (sentMoney <= 0 && receivedMoney <= 0 && (sentCerts > 0 || receivedCerts > 0) && !KNOWN_USERS.contains(valueObject["sender_name"].toString())) { + } else if (sentMoney <= 0 && receivedMoney <= 0 && + (sentCerts > 0 || receivedCerts > 0) && + !KNOWN_USERS.contains(valueObject["sender_name"].toString()) && + !KNOWN_USERS.contains(valueObject["recipient_name"].toString()) + ) { // this is a non-HFC asset transfer. if (sentCerts > 0) { QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString()); From 20cd1df22cf1f48bbf3c27a7347518c1022afae7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 1 Nov 2018 16:48:08 -0700 Subject: [PATCH 081/125] Lotsa changes --- interface/resources/qml/hifi/tts/TTS.qml | 12 +++- interface/src/Application.cpp | 4 -- .../src/scripting/TTSScriptingInterface.cpp | 55 +++++++------------ .../src/scripting/TTSScriptingInterface.h | 15 ++--- libraries/audio-client/src/AudioClient.cpp | 41 -------------- libraries/audio-client/src/AudioClient.h | 5 -- 6 files changed, 36 insertions(+), 96 deletions(-) diff --git a/interface/resources/qml/hifi/tts/TTS.qml b/interface/resources/qml/hifi/tts/TTS.qml index 114efd0cca..d9507f6084 100644 --- a/interface/resources/qml/hifi/tts/TTS.qml +++ b/interface/resources/qml/hifi/tts/TTS.qml @@ -202,7 +202,6 @@ Rectangle { TextArea { id: messageToSpeak; - placeholderText: "Message to Speak"; font.family: "Fira Sans SemiBold"; font.pixelSize: 20; // Anchors @@ -229,6 +228,17 @@ Rectangle { event.accepted = true; } } + + HifiStylesUit.FiraSansRegular { + text: "Input Text to Speak..."; + size: 20; + anchors.fill: parent; + anchors.topMargin: 4; + anchors.leftMargin: 4; + color: hifi.colors.lightGrayText; + visible: !parent.activeFocus && messageToSpeak.text === ""; + verticalAlignment: Text.AlignTop; + } } HifiControlsUit.Button { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dfb4ef1c32..967a31135c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1187,10 +1187,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [&audioIO](recording::Frame::ConstPointer frame) { audioIO->handleRecordedAudioInput(frame->data); }); - - auto TTS = DependencyManager::get().data(); - connect(TTS, &TTSScriptingInterface::ttsSampleCreated, audioIO, &AudioClient::handleTTSAudioInput); - connect(TTS, &TTSScriptingInterface::clearTTSBuffer, audioIO, &AudioClient::clearTTSBuffer); connect(audioIO, &AudioClient::inputReceived, [](const QByteArray& audio) { static auto recorder = DependencyManager::get(); diff --git a/interface/src/scripting/TTSScriptingInterface.cpp b/interface/src/scripting/TTSScriptingInterface.cpp index f51a638471..6a5b72ea5f 100644 --- a/interface/src/scripting/TTSScriptingInterface.cpp +++ b/interface/src/scripting/TTSScriptingInterface.cpp @@ -37,6 +37,9 @@ TTSScriptingInterface::TTSScriptingInterface() { if (FAILED(hr)) { qDebug() << "Can't set default voice."; } + + _lastSoundAudioInjectorUpdateTimer.setSingleShot(true); + connect(&_lastSoundAudioInjectorUpdateTimer, &QTimer::timeout, this, &TTSScriptingInterface::updateLastSoundAudioInjector); #endif } @@ -58,38 +61,22 @@ private: }; #endif -void TTSScriptingInterface::testTone(const bool& alsoInject) { - QByteArray byteArray(480000, 0); - _lastSoundByteArray.resize(0); - _lastSoundByteArray.resize(480000); - - int32_t a = 0; - int16_t* samples = reinterpret_cast(byteArray.data()); - for (a = 0; a < 240000; a++) { - int16_t temp = (glm::sin(glm::radians((float)a))) * 32768; - samples[a] = temp; - } - emit ttsSampleCreated(_lastSoundByteArray, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * 50, 96); - - if (alsoInject) { +const std::chrono::milliseconds INJECTOR_INTERVAL_MS = std::chrono::milliseconds(100); +void TTSScriptingInterface::updateLastSoundAudioInjector() { + if (_lastSoundAudioInjector) { AudioInjectorOptions options; options.position = DependencyManager::get()->getMyAvatarPosition(); - - _lastSoundAudioInjector = AudioInjector::playSound(_lastSoundByteArray, options); + _lastSoundAudioInjector->setOptions(options); + _lastSoundAudioInjectorUpdateTimer.start(INJECTOR_INTERVAL_MS); } } -void TTSScriptingInterface::speakText(const QString& textToSpeak, - const int& newChunkSize, - const int& timerInterval, - const int& sampleRate, - const int& bitsPerSample, - const bool& alsoInject) { +void TTSScriptingInterface::speakText(const QString& textToSpeak) { #ifdef WIN32 WAVEFORMATEX fmt; fmt.wFormatTag = WAVE_FORMAT_PCM; - fmt.nSamplesPerSec = sampleRate; - fmt.wBitsPerSample = bitsPerSample; + fmt.nSamplesPerSec = 24000; + fmt.wBitsPerSample = 16; fmt.nChannels = 1; fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; @@ -156,16 +143,17 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak, _lastSoundByteArray.resize(0); _lastSoundByteArray.append(buf1, dwSize); - // Commented out because this doesn't work completely :) - // Obviously, commenting this out isn't fit for production, but it's fine for a test PR - //emit ttsSampleCreated(_lastSoundByteArray, newChunkSize, timerInterval); + AudioInjectorOptions options; + options.position = DependencyManager::get()->getMyAvatarPosition(); - if (alsoInject) { - AudioInjectorOptions options; - options.position = DependencyManager::get()->getMyAvatarPosition(); - - _lastSoundAudioInjector = AudioInjector::playSound(_lastSoundByteArray, options); + if (_lastSoundAudioInjector) { + _lastSoundAudioInjector->stop(); + _lastSoundAudioInjectorUpdateTimer.stop(); } + + _lastSoundAudioInjector = AudioInjector::playSoundAndDelete(_lastSoundByteArray, options); + + _lastSoundAudioInjectorUpdateTimer.start(INJECTOR_INTERVAL_MS); #else qDebug() << "Text-to-Speech isn't currently supported on non-Windows platforms."; #endif @@ -174,7 +162,6 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak, void TTSScriptingInterface::stopLastSpeech() { if (_lastSoundAudioInjector) { _lastSoundAudioInjector->stop(); + _lastSoundAudioInjector = NULL; } - - emit clearTTSBuffer(); } diff --git a/interface/src/scripting/TTSScriptingInterface.h b/interface/src/scripting/TTSScriptingInterface.h index 7e4f3afa9d..0f1e723885 100644 --- a/interface/src/scripting/TTSScriptingInterface.h +++ b/interface/src/scripting/TTSScriptingInterface.h @@ -12,6 +12,7 @@ #define hifi_SpeechScriptingInterface_h #include +#include #include #ifdef WIN32 #pragma warning(disable : 4996) @@ -31,19 +32,9 @@ public: TTSScriptingInterface(); ~TTSScriptingInterface(); - Q_INVOKABLE void testTone(const bool& alsoInject = false); - Q_INVOKABLE void speakText(const QString& textToSpeak, - const int& newChunkSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * 50), - const int& timerInterval = 96, - const int& sampleRate = 24000, - const int& bitsPerSample = 16, - const bool& alsoInject = false); + Q_INVOKABLE void speakText(const QString& textToSpeak); Q_INVOKABLE void stopLastSpeech(); -signals: - void ttsSampleCreated(QByteArray outputArray, const int& newChunkSize, const int& timerInterval); - void clearTTSBuffer(); - private: #ifdef WIN32 class CComAutoInit { @@ -90,6 +81,8 @@ private: QByteArray _lastSoundByteArray; AudioInjectorPointer _lastSoundAudioInjector; + QTimer _lastSoundAudioInjectorUpdateTimer; + void updateLastSoundAudioInjector(); }; #endif // hifi_SpeechScriptingInterface_h diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 1ce6c15951..b7557681a5 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -245,8 +245,6 @@ AudioClient::AudioClient() : packetReceiver.registerListener(PacketType::NoisyMute, this, "handleNoisyMutePacket"); packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); - - connect(&_TTSTimer, &QTimer::timeout, this, &AudioClient::processTTSBuffer); } AudioClient::~AudioClient() { @@ -1202,45 +1200,6 @@ int rawToWav(const char* rawData, const int& rawLength, const char* wavfn, long return 0; } -void AudioClient::processTTSBuffer() { - Lock lock(_TTSMutex); - if (_TTSAudioBuffer.size() > 0) { - QByteArray part; - part.append(_TTSAudioBuffer.data(), _TTSChunkSize); - _TTSAudioBuffer.remove(0, _TTSChunkSize); - handleAudioInput(part); - } else { - _isProcessingTTS = false; - _TTSTimer.stop(); - } -} - -void AudioClient::handleTTSAudioInput(const QByteArray& audio, const int& newChunkSize, const int& timerInterval) { - _TTSChunkSize = newChunkSize; - _TTSAudioBuffer.append(audio); - - handleLocalEchoAndReverb(_TTSAudioBuffer, 48000, 1); - - //QString filename = QString::number(usecTimestampNow()); - //QString path = PathUtils::getAppDataPath() + "Audio/" + filename + "-before.wav"; - //rawToWav(_TTSAudioBuffer.data(), _TTSAudioBuffer.size(), path.toLocal8Bit(), 24000, 1); - - //QByteArray temp; - - _isProcessingTTS = true; - _TTSTimer.start(timerInterval); - - //filename = QString::number(usecTimestampNow()); - //path = PathUtils::getAppDataPath() + "Audio/" + filename + "-after.wav"; - //rawToWav(temp.data(), temp.size(), path.toLocal8Bit(), 12000, 1); -} - -void AudioClient::clearTTSBuffer() { - _TTSAudioBuffer.resize(0); - _isProcessingTTS = false; - _TTSTimer.stop(); -} - void AudioClient::prepareLocalAudioInjectors(std::unique_ptr localAudioLock) { bool doSynchronously = localAudioLock.operator bool(); if (!localAudioLock) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 9b50d3eccb..788b764903 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -197,11 +197,6 @@ public slots: void checkInputTimeout(); void handleDummyAudioInput(); void handleRecordedAudioInput(const QByteArray& audio); - void handleTTSAudioInput(const QByteArray& audio, - const int& newChunkSize, - const int& timerInterval); - void clearTTSBuffer(); - void processTTSBuffer(); void reset(); void audioMixerKilled(); From cbca77b12fa3f87d70dfc22de92a97b875c77257 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 1 Nov 2018 17:17:48 -0700 Subject: [PATCH 082/125] Rename HFMGeometry to HFMModel and adjust related variables --- .../src/avatars/ScriptableAvatar.cpp | 6 +- interface/src/ModelPackager.cpp | 34 +-- interface/src/ModelPackager.h | 6 +- interface/src/ModelPropertiesDialog.cpp | 20 +- interface/src/ModelPropertiesDialog.h | 4 +- interface/src/avatar/MyAvatar.cpp | 14 +- interface/src/avatar/MySkeletonModel.cpp | 14 +- interface/src/raypick/CollisionPick.cpp | 14 +- interface/src/ui/overlays/ModelOverlay.cpp | 14 +- libraries/animation/src/AnimClip.cpp | 10 +- libraries/animation/src/AnimSkeleton.cpp | 6 +- libraries/animation/src/AnimSkeleton.h | 2 +- libraries/animation/src/AnimationCache.cpp | 24 +- libraries/animation/src/AnimationCache.h | 8 +- libraries/animation/src/Rig.cpp | 68 ++--- libraries/animation/src/Rig.h | 6 +- .../src/avatars-renderer/Avatar.cpp | 6 +- .../src/avatars-renderer/SkeletonModel.cpp | 46 ++-- .../src/avatars-renderer/SkeletonModel.h | 4 +- libraries/baking/src/FBXBaker.cpp | 6 +- libraries/baking/src/FBXBaker.h | 2 +- libraries/baking/src/OBJBaker.cpp | 20 +- libraries/baking/src/OBJBaker.h | 4 +- .../src/RenderableModelEntityItem.cpp | 24 +- libraries/fbx/src/FBX.h | 14 +- libraries/fbx/src/FBXReader.cpp | 234 +++++++++--------- libraries/fbx/src/FBXReader.h | 12 +- libraries/fbx/src/GLTFReader.cpp | 112 ++++----- libraries/fbx/src/GLTFReader.h | 8 +- libraries/fbx/src/OBJReader.cpp | 70 +++--- libraries/fbx/src/OBJReader.h | 6 +- .../src/model-networking/ModelCache.cpp | 40 +-- .../src/model-networking/ModelCache.h | 8 +- .../render-utils/src/CauterizedModel.cpp | 20 +- .../render-utils/src/MeshPartPayload.cpp | 4 +- libraries/render-utils/src/Model.cpp | 90 +++---- libraries/render-utils/src/Model.h | 8 +- .../render-utils/src/SoftAttachmentModel.cpp | 6 +- tests-manual/gpu/src/TestFbx.cpp | 10 +- tests-manual/gpu/src/TestFbx.h | 2 +- .../src/AnimInverseKinematicsTests.cpp | 24 +- tools/skeleton-dump/src/SkeletonDumpApp.cpp | 2 +- tools/vhacd-util/src/VHACDUtil.cpp | 34 +-- tools/vhacd-util/src/VHACDUtil.h | 8 +- tools/vhacd-util/src/VHACDUtilApp.cpp | 10 +- tools/vhacd-util/src/VHACDUtilApp.h | 2 +- 46 files changed, 543 insertions(+), 543 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 385f94d9f3..51038a782f 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -84,7 +84,7 @@ void ScriptableAvatar::update(float deltatime) { // Run animation if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) { if (!_animSkeleton) { - _animSkeleton = std::make_shared(_bind->getGeometry()); + _animSkeleton = std::make_shared(_bind->getHFMModel()); } float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { @@ -93,7 +93,7 @@ void ScriptableAvatar::update(float deltatime) { } _animationDetails.currentFrame = currentFrame; - const QVector& modelJoints = _bind->getGeometry().joints; + const QVector& modelJoints = _bind->getHFMModel().joints; QStringList animationJointNames = _animation->getJointNames(); const int nJoints = modelJoints.size(); @@ -113,7 +113,7 @@ void ScriptableAvatar::update(float deltatime) { const QString& name = animationJointNames[i]; // As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than // trusting the .fst (which is sometimes not updated to match changes to .fbx). - int mapping = _bind->getGeometry().getJointIndex(name); + int mapping = _bind->getHFMModel().getJointIndex(name); if (mapping != -1 && !_maskedJoints.contains(name)) { AnimPose floorPose = composeAnimPose(modelJoints[mapping], floorFrame.rotations[i], floorFrame.translations[i] * UNIT_SCALE); diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 0b2846006b..f3a69bbad2 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -109,10 +109,10 @@ bool ModelPackager::loadModel() { qCDebug(interfaceapp) << "Reading FBX file : " << _fbxInfo.filePath(); QByteArray fbxContents = fbx.readAll(); - _geometry.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath())); + _model.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath())); // make sure we have some basic mappings - populateBasicMapping(_mapping, _fbxInfo.filePath(), *_geometry); + populateBasicMapping(_mapping, _fbxInfo.filePath(), *_model); } catch (const QString& error) { qCDebug(interfaceapp) << "Error reading " << _fbxInfo.filePath() << ": " << error; return false; @@ -122,7 +122,7 @@ bool ModelPackager::loadModel() { bool ModelPackager::editProperties() { // open the dialog to configure the rest - ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), *_geometry); + ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), *_model); if (properties.exec() == QDialog::Rejected) { return false; } @@ -235,18 +235,18 @@ bool ModelPackager::zipModel() { return true; } -void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const HFMGeometry& geometry) { +void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& model) { bool isBodyType = _modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL; // mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file - bool likelyMixamoFile = geometry.applicationName == "mixamo.com" || - (geometry.blendshapeChannelNames.contains("BrowsDown_Right") && - geometry.blendshapeChannelNames.contains("MouthOpen") && - geometry.blendshapeChannelNames.contains("Blink_Left") && - geometry.blendshapeChannelNames.contains("Blink_Right") && - geometry.blendshapeChannelNames.contains("Squint_Right")); + bool likelyMixamoFile = model.applicationName == "mixamo.com" || + (model.blendshapeChannelNames.contains("BrowsDown_Right") && + model.blendshapeChannelNames.contains("MouthOpen") && + model.blendshapeChannelNames.contains("Blink_Left") && + model.blendshapeChannelNames.contains("Blink_Right") && + model.blendshapeChannelNames.contains("Squint_Right")); if (!mapping.contains(NAME_FIELD)) { mapping.insert(NAME_FIELD, QFileInfo(filename).baseName()); @@ -268,15 +268,15 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename } QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); if (!joints.contains("jointEyeLeft")) { - joints.insert("jointEyeLeft", geometry.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" : - (geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye")); + joints.insert("jointEyeLeft", model.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" : + (model.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye")); } if (!joints.contains("jointEyeRight")) { - joints.insert("jointEyeRight", geometry.jointIndices.contains("jointEyeRight") ? "jointEyeRight" : - geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye"); + joints.insert("jointEyeRight", model.jointIndices.contains("jointEyeRight") ? "jointEyeRight" : + model.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye"); } if (!joints.contains("jointNeck")) { - joints.insert("jointNeck", geometry.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck"); + joints.insert("jointNeck", model.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck"); } if (isBodyType) { @@ -296,7 +296,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename if (!joints.contains("jointHead")) { const char* topName = likelyMixamoFile ? "HeadTop_End" : "HeadEnd"; - joints.insert("jointHead", geometry.jointIndices.contains(topName) ? topName : "Head"); + joints.insert("jointHead", model.jointIndices.contains(topName) ? topName : "Head"); } mapping.insert(JOINT_FIELD, joints); @@ -370,7 +370,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename void ModelPackager::listTextures() { _textures.clear(); - foreach (const HFMMaterial mat, _geometry->materials) { + foreach (const HFMMaterial mat, _model->materials) { if (!mat.albedoTexture.filename.isEmpty() && mat.albedoTexture.content.isEmpty() && !_textures.contains(mat.albedoTexture.filename)) { _textures << mat.albedoTexture.filename; diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index b68d9e746d..2ec629a363 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -19,7 +19,7 @@ #include "ui/ModelsBrowser.h" -class HFMGeometry; +class HFMModel; class ModelPackager : public QObject { public: @@ -32,7 +32,7 @@ private: bool editProperties(); bool zipModel(); - void populateBasicMapping(QVariantHash& mapping, QString filename, const HFMGeometry& geometry); + void populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& model); void listTextures(); bool copyTextures(const QString& oldDir, const QDir& newDir); @@ -44,7 +44,7 @@ private: QString _scriptDir; QVariantHash _mapping; - std::unique_ptr _geometry; + std::unique_ptr _model; QStringList _textures; QStringList _scripts; }; diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index dcda85d117..0b1638a5bc 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -27,11 +27,11 @@ ModelPropertiesDialog::ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const HFMGeometry& geometry) : + const QString& basePath, const HFMModel& model) : _modelType(modelType), _originalMapping(originalMapping), _basePath(basePath), -_geometry(geometry) +_model(model) { setWindowTitle("Set Model Properties"); @@ -108,8 +108,8 @@ QVariantHash ModelPropertiesDialog::getMapping() const { // update the joint indices QVariantHash jointIndices; - for (int i = 0; i < _geometry.joints.size(); i++) { - jointIndices.insert(_geometry.joints.at(i).name, QString::number(i)); + for (int i = 0; i < _model.joints.size(); i++) { + jointIndices.insert(_model.joints.at(i).name, QString::number(i)); } mapping.insert(JOINT_INDEX_FIELD, jointIndices); @@ -118,10 +118,10 @@ QVariantHash ModelPropertiesDialog::getMapping() const { if (_modelType == FSTReader::ATTACHMENT_MODEL) { glm::vec3 pivot; if (_pivotAboutCenter->isChecked()) { - pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f; + pivot = (_model.meshExtents.minimum + _model.meshExtents.maximum) * 0.5f; } else if (_pivotJoint->currentIndex() != 0) { - pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform); + pivot = extractTranslation(_model.joints.at(_pivotJoint->currentIndex() - 1).transform); } mapping.insert(TRANSLATION_X_FIELD, -pivot.x * (float)_scale->value() + (float)_translationX->value()); mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * (float)_scale->value() + (float)_translationY->value()); @@ -191,7 +191,7 @@ void ModelPropertiesDialog::reset() { } foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) { QString jointName = joint.toString(); - if (_geometry.jointIndices.contains(jointName)) { + if (_model.jointIndices.contains(jointName)) { createNewFreeJoint(jointName); } } @@ -249,8 +249,8 @@ QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const { if (withNone) { box->addItem("(none)"); } - foreach (const HFMJoint& joint, _geometry.joints) { - if (joint.isSkeletonJoint || !_geometry.hasSkeletonJoints) { + foreach (const HFMJoint& joint, _model.joints) { + if (joint.isSkeletonJoint || !_model.hasSkeletonJoints) { box->addItem(joint.name); } } @@ -266,7 +266,7 @@ QDoubleSpinBox* ModelPropertiesDialog::createTranslationBox() const { } void ModelPropertiesDialog::insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const { - if (_geometry.jointIndices.contains(name)) { + if (_model.jointIndices.contains(name)) { joints.insert(joint, name); } else { joints.remove(joint); diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h index d1a020bec3..b979768a2d 100644 --- a/interface/src/ModelPropertiesDialog.h +++ b/interface/src/ModelPropertiesDialog.h @@ -30,7 +30,7 @@ class ModelPropertiesDialog : public QDialog { public: ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const HFMGeometry& geometry); + const QString& basePath, const HFMModel& model); QVariantHash getMapping() const; @@ -50,7 +50,7 @@ private: FSTReader::ModelType _modelType; QVariantHash _originalMapping; QString _basePath; - HFMGeometry _geometry; + HFMModel _model; QLineEdit* _name = nullptr; QPushButton* _textureDirectory = nullptr; QPushButton* _scriptDirectory = nullptr; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b3a66f70b8..8d47591d40 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -155,8 +155,8 @@ MyAvatar::MyAvatar(QThread* thread) : }); connect(_skeletonModel.get(), &Model::rigReady, this, [this]() { if (_shouldLoadScripts) { - auto geometry = getSkeletonModel()->getHFMGeometry(); - qApp->loadAvatarScripts(geometry.scripts); + auto hfmModel = getSkeletonModel()->getHFMModel(); + qApp->loadAvatarScripts(hfmModel.scripts); _shouldLoadScripts = false; } // Load and convert old attachments to avatar entities @@ -2429,10 +2429,10 @@ void MyAvatar::attachmentDataToEntityProperties(const AttachmentData& data, Enti void MyAvatar::initHeadBones() { int neckJointIndex = -1; if (_skeletonModel->isLoaded()) { - neckJointIndex = _skeletonModel->getHFMGeometry().neckJointIndex; + neckJointIndex = _skeletonModel->getHFMModel().neckJointIndex; } if (neckJointIndex == -1) { - neckJointIndex = (_skeletonModel->getHFMGeometry().headJointIndex - 1); + neckJointIndex = (_skeletonModel->getHFMModel().headJointIndex - 1); if (neckJointIndex < 0) { // return if the head is not even there. can't cauterize!! return; @@ -2592,11 +2592,11 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { if (_skeletonModel && _skeletonModel->isLoaded()) { const Rig& rig = _skeletonModel->getRig(); - const HFMGeometry& geometry = _skeletonModel->getHFMGeometry(); + const HFMModel& hfmModel = _skeletonModel->getHFMModel(); for (int i = 0; i < rig.getJointStateCount(); i++) { AnimPose jointPose; rig.getAbsoluteJointPoseInRigFrame(i, jointPose); - const HFMJointShapeInfo& shapeInfo = geometry.joints[i].shapeInfo; + const HFMJointShapeInfo& shapeInfo = hfmModel.joints[i].shapeInfo; const AnimPose pose = rigToWorldPose * jointPose; for (size_t j = 0; j < shapeInfo.debugLines.size() / 2; j++) { glm::vec3 pointA = pose.xformPoint(shapeInfo.debugLines[2 * j]); @@ -4012,7 +4012,7 @@ float MyAvatar::getSitStandStateChange() const { } QVector MyAvatar::getScriptUrls() { - QVector scripts = _skeletonModel->isLoaded() ? _skeletonModel->getHFMGeometry().scripts : QVector(); + QVector scripts = _skeletonModel->isLoaded() ? _skeletonModel->getHFMModel().scripts : QVector(); return scripts; } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 3ec40d372b..2a21f78b21 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -90,7 +90,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { // Called within Model::simulate call, below. void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - const HFMGeometry& geometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); Head* head = _owningAvatar->getHead(); @@ -268,19 +268,19 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // pass detailed torso k-dops to rig. int hipsJoint = _rig.indexOfJoint("Hips"); if (hipsJoint >= 0) { - params.hipsShapeInfo = geometry.joints[hipsJoint].shapeInfo; + params.hipsShapeInfo = hfmModel.joints[hipsJoint].shapeInfo; } int spineJoint = _rig.indexOfJoint("Spine"); if (spineJoint >= 0) { - params.spineShapeInfo = geometry.joints[spineJoint].shapeInfo; + params.spineShapeInfo = hfmModel.joints[spineJoint].shapeInfo; } int spine1Joint = _rig.indexOfJoint("Spine1"); if (spine1Joint >= 0) { - params.spine1ShapeInfo = geometry.joints[spine1Joint].shapeInfo; + params.spine1ShapeInfo = hfmModel.joints[spine1Joint].shapeInfo; } int spine2Joint = _rig.indexOfJoint("Spine2"); if (spine2Joint >= 0) { - params.spine2ShapeInfo = geometry.joints[spine2Joint].shapeInfo; + params.spine2ShapeInfo = hfmModel.joints[spine2Joint].shapeInfo; } _rig.updateFromControllerParameters(params, deltaTime); @@ -300,8 +300,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.eyeSaccade = head->getSaccade(); eyeParams.modelRotation = getRotation(); eyeParams.modelTranslation = getTranslation(); - eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; - eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; + eyeParams.leftEyeJointIndex = hfmModel.leftEyeJointIndex; + eyeParams.rightEyeJointIndex = hfmModel.rightEyeJointIndex; _rig.updateFromEyeParameters(eyeParams); diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index e8a53aa9b6..2b75946e28 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -131,7 +131,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // should never fall in here when collision model not fully loaded // TODO: assert that all geometries exist and are loaded //assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); - const HFMGeometry& collisionGeometry = resource->getHFMGeometry(); + const HFMModel& collisionModel = resource->getHFMModel(); ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); @@ -139,7 +139,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case. - foreach (const HFMMesh& mesh, collisionGeometry.meshes) { + foreach (const HFMMesh& mesh, collisionModel.meshes) { // each meshPart is a convex hull foreach (const HFMMeshPart &meshPart, mesh.parts) { pointCollection.push_back(QVector()); @@ -206,7 +206,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // to the visual model and apply them to the collision model (without regard for the // collision model's extents). - glm::vec3 scaleToFit = dimensions / resource->getHFMGeometry().getUnscaledMeshExtents().size(); + glm::vec3 scaleToFit = dimensions / resource->getHFMModel().getUnscaledMeshExtents().size(); // multiply each point by scale for (int32_t i = 0; i < pointCollection.size(); i++) { for (int32_t j = 0; j < pointCollection[i].size(); j++) { @@ -216,11 +216,11 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha } shapeInfo.setParams(type, dimensions, resource->getURL().toString()); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { - const HFMGeometry& hfmGeometry = resource->getHFMGeometry(); - int numHFMMeshes = hfmGeometry.meshes.size(); + const HFMModel& hfmModel = resource->getHFMModel(); + int numHFMMeshes = hfmModel.meshes.size(); int totalNumVertices = 0; for (int i = 0; i < numHFMMeshes; i++) { - const HFMMesh& mesh = hfmGeometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); totalNumVertices += mesh.vertices.size(); } const int32_t MAX_VERTICES_PER_STATIC_MESH = 1e6; @@ -230,7 +230,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha return; } - auto& meshes = resource->getHFMGeometry().meshes; + auto& meshes = resource->getHFMModel().meshes; int32_t numMeshes = (int32_t)(meshes.size()); const int MAX_ALLOWED_MESH_COUNT = 1000; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 1b66ff08ad..190d9c3895 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -446,7 +446,7 @@ QVariant ModelOverlay::getProperty(const QString& property) { if (property == "jointNames") { if (_model && _model->isActive()) { - // note: going through Rig because Model::getJointNames() (which proxies to HFMGeometry) was always empty + // note: going through Rig because Model::getJointNames() (which proxies to HFMModel) was always empty const Rig* rig = &(_model->getRig()); return mapJoints([rig](int jointIndex) -> QString { return rig->nameOfJoint(jointIndex); @@ -605,11 +605,11 @@ void ModelOverlay::animate() { return; } - QStringList animationJointNames = _animation->getGeometry().getJointNames(); - auto& hfmJoints = _animation->getGeometry().joints; + QStringList animationJointNames = _animation->getHFMModel().getJointNames(); + auto& hfmJoints = _animation->getHFMModel().joints; - auto& originalHFMJoints = _model->getHFMGeometry().joints; - auto& originalFbxIndices = _model->getHFMGeometry().jointIndices; + auto& originalHFMJoints = _model->getHFMModel().joints; + auto& originalHFMIndices = _model->getHFMModel().jointIndices; const QVector& rotations = frames[_lastKnownCurrentFrame].rotations; const QVector& translations = frames[_lastKnownCurrentFrame].translations; @@ -628,9 +628,9 @@ void ModelOverlay::animate() { } else if (index < animationJointNames.size()) { QString jointName = hfmJoints[index].name; - if (originalFbxIndices.contains(jointName)) { + if (originalHFMIndices.contains(jointName)) { // Making sure the joint names exist in the original model the animation is trying to apply onto. If they do, then remap and get its translation. - int remappedIndex = originalFbxIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. + int remappedIndex = originalHFMIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. translationMat = glm::translate(originalHFMJoints[remappedIndex].translation); } } diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index d630218165..7380ca8a13 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -101,8 +101,8 @@ void AnimClip::copyFromNetworkAnim() { // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. - const HFMGeometry& geom = _networkAnim->getGeometry(); - AnimSkeleton animSkeleton(geom); + const HFMModel& model = _networkAnim->getHFMModel(); + AnimSkeleton animSkeleton(model); const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); std::vector jointMap; @@ -115,12 +115,12 @@ void AnimClip::copyFromNetworkAnim() { jointMap.push_back(skeletonJoint); } - const int frameCount = geom.animationFrames.size(); + const int frameCount = model.animationFrames.size(); _anim.resize(frameCount); for (int frame = 0; frame < frameCount; frame++) { - const HFMAnimationFrame& hfmAnimFrame = geom.animationFrames[frame]; + const HFMAnimationFrame& hfmAnimFrame = model.animationFrames[frame]; // init all joints in animation to default pose // this will give us a resonable result for bones in the model skeleton but not in the animation. @@ -150,7 +150,7 @@ void AnimClip::copyFromNetworkAnim() { // adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. - const glm::vec3& hfmZeroTrans = geom.animationFrames[0].translations[animJoint]; + const glm::vec3& hfmZeroTrans = model.animationFrames[0].translations[animJoint]; const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); float boneLengthScale = 1.0f; const float EPSILON = 0.0001f; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index fc4114ac7b..bc179739a0 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -16,11 +16,11 @@ #include "AnimationLogging.h" -AnimSkeleton::AnimSkeleton(const HFMGeometry& geometry) { +AnimSkeleton::AnimSkeleton(const HFMModel& model) { // convert to std::vector of joints std::vector joints; - joints.reserve(geometry.joints.size()); - for (auto& joint : geometry.joints) { + joints.reserve(model.joints.size()); + for (auto& joint : model.joints) { joints.push_back(joint); } buildSkeletonFromJoints(joints); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 1717d75985..80353523d1 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -23,7 +23,7 @@ public: using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; - explicit AnimSkeleton(const HFMGeometry& geometry); + explicit AnimSkeleton(const HFMModel& model); explicit AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; const QString& getJointName(int jointIndex) const; diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index b5b27c3a24..06dfc0262a 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -69,14 +69,14 @@ void AnimationReader::run() { if (urlValid) { // Parse the FBX directly from the QNetworkReply - HFMGeometry::Pointer fbxgeo; + HFMModel::Pointer hfmModel; if (_url.path().toLower().endsWith(".fbx")) { - fbxgeo.reset(readFBX(_data, QVariantHash(), _url.path())); + hfmModel.reset(readFBX(_data, QVariantHash(), _url.path())); } else { QString errorStr("usupported format"); emit onError(299, errorStr); } - emit onSuccess(fbxgeo); + emit onSuccess(hfmModel); } else { throw QString("url is invalid"); } @@ -88,7 +88,7 @@ void AnimationReader::run() { } bool Animation::isLoaded() const { - return _loaded && _geometry; + return _loaded && _hfmModel; } QStringList Animation::getJointNames() const { @@ -99,8 +99,8 @@ QStringList Animation::getJointNames() const { return result; } QStringList names; - if (_geometry) { - foreach (const HFMJoint& joint, _geometry->joints) { + if (_hfmModel) { + foreach (const HFMJoint& joint, _hfmModel->joints) { names.append(joint.name); } } @@ -114,30 +114,30 @@ QVector Animation::getFrames() const { Q_RETURN_ARG(QVector, result)); return result; } - if (_geometry) { - return _geometry->animationFrames; + if (_hfmModel) { + return _hfmModel->animationFrames; } else { return QVector(); } } const QVector& Animation::getFramesReference() const { - return _geometry->animationFrames; + return _hfmModel->animationFrames; } void Animation::downloadFinished(const QByteArray& data) { // parse the animation/fbx file on a background thread. AnimationReader* animationReader = new AnimationReader(_url, data); - connect(animationReader, SIGNAL(onSuccess(HFMGeometry::Pointer)), SLOT(animationParseSuccess(HFMGeometry::Pointer))); + connect(animationReader, SIGNAL(onSuccess(HFMModel::Pointer)), SLOT(animationParseSuccess(HFMModel::Pointer))); connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString))); QThreadPool::globalInstance()->start(animationReader); } -void Animation::animationParseSuccess(HFMGeometry::Pointer geometry) { +void Animation::animationParseSuccess(HFMModel::Pointer hfmModel) { qCDebug(animation) << "Animation parse success" << _url.toDisplayString(); - _geometry = geometry; + _hfmModel = hfmModel; finishedLoading(true); } diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index 302f23a4e7..4423e8f18d 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -66,7 +66,7 @@ public: QString getType() const override { return "Animation"; } - const HFMGeometry& getGeometry() const { return *_geometry; } + const HFMModel& getHFMModel() const { return *_hfmModel; } virtual bool isLoaded() const override; @@ -88,12 +88,12 @@ protected: virtual void downloadFinished(const QByteArray& data) override; protected slots: - void animationParseSuccess(HFMGeometry::Pointer geometry); + void animationParseSuccess(HFMModel::Pointer hfmModel); void animationParseError(int error, QString str); private: - HFMGeometry::Pointer _geometry; + HFMModel::Pointer _hfmModel; }; /// Reads geometry in a worker thread. @@ -105,7 +105,7 @@ public: virtual void run() override; signals: - void onSuccess(HFMGeometry::Pointer geometry); + void onSuccess(HFMModel::Pointer hfmModel); void onError(int error, QString str); private: diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2641be92da..c2f909dd24 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -260,14 +260,14 @@ void Rig::destroyAnimGraph() { _rightEyeJointChildren.clear(); } -void Rig::initJointStates(const HFMGeometry& geometry, const glm::mat4& modelOffset) { - _geometryOffset = AnimPose(geometry.offset); +void Rig::initJointStates(const HFMModel& hfmModel, const glm::mat4& modelOffset) { + _geometryOffset = AnimPose(hfmModel.offset); _invGeometryOffset = _geometryOffset.inverse(); - _geometryToRigTransform = modelOffset * geometry.offset; + _geometryToRigTransform = modelOffset * hfmModel.offset; _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); setModelOffset(modelOffset); - _animSkeleton = std::make_shared(geometry); + _animSkeleton = std::make_shared(hfmModel); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -293,24 +293,24 @@ void Rig::initJointStates(const HFMGeometry& geometry, const glm::mat4& modelOff buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); - _rootJointIndex = geometry.rootJointIndex; - _leftEyeJointIndex = geometry.leftEyeJointIndex; - _rightEyeJointIndex = geometry.rightEyeJointIndex; - _leftHandJointIndex = geometry.leftHandJointIndex; - _leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1; - _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? geometry.joints.at(_leftElbowJointIndex).parentIndex : -1; - _rightHandJointIndex = geometry.rightHandJointIndex; - _rightElbowJointIndex = _rightHandJointIndex >= 0 ? geometry.joints.at(_rightHandJointIndex).parentIndex : -1; - _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? geometry.joints.at(_rightElbowJointIndex).parentIndex : -1; + _rootJointIndex = hfmModel.rootJointIndex; + _leftEyeJointIndex = hfmModel.leftEyeJointIndex; + _rightEyeJointIndex = hfmModel.rightEyeJointIndex; + _leftHandJointIndex = hfmModel.leftHandJointIndex; + _leftElbowJointIndex = _leftHandJointIndex >= 0 ? hfmModel.joints.at(_leftHandJointIndex).parentIndex : -1; + _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? hfmModel.joints.at(_leftElbowJointIndex).parentIndex : -1; + _rightHandJointIndex = hfmModel.rightHandJointIndex; + _rightElbowJointIndex = _rightHandJointIndex >= 0 ? hfmModel.joints.at(_rightHandJointIndex).parentIndex : -1; + _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? hfmModel.joints.at(_rightElbowJointIndex).parentIndex : -1; - _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.leftEyeJointIndex); - _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.rightEyeJointIndex); + _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.leftEyeJointIndex); + _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.rightEyeJointIndex); } -void Rig::reset(const HFMGeometry& geometry) { - _geometryOffset = AnimPose(geometry.offset); +void Rig::reset(const HFMModel& hfmModel) { + _geometryOffset = AnimPose(hfmModel.offset); _invGeometryOffset = _geometryOffset.inverse(); - _animSkeleton = std::make_shared(geometry); + _animSkeleton = std::make_shared(hfmModel); _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -338,18 +338,18 @@ void Rig::reset(const HFMGeometry& geometry) { buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); - _rootJointIndex = geometry.rootJointIndex; - _leftEyeJointIndex = geometry.leftEyeJointIndex; - _rightEyeJointIndex = geometry.rightEyeJointIndex; - _leftHandJointIndex = geometry.leftHandJointIndex; - _leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1; - _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? geometry.joints.at(_leftElbowJointIndex).parentIndex : -1; - _rightHandJointIndex = geometry.rightHandJointIndex; - _rightElbowJointIndex = _rightHandJointIndex >= 0 ? geometry.joints.at(_rightHandJointIndex).parentIndex : -1; - _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? geometry.joints.at(_rightElbowJointIndex).parentIndex : -1; + _rootJointIndex = hfmModel.rootJointIndex; + _leftEyeJointIndex = hfmModel.leftEyeJointIndex; + _rightEyeJointIndex = hfmModel.rightEyeJointIndex; + _leftHandJointIndex = hfmModel.leftHandJointIndex; + _leftElbowJointIndex = _leftHandJointIndex >= 0 ? hfmModel.joints.at(_leftHandJointIndex).parentIndex : -1; + _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? hfmModel.joints.at(_leftElbowJointIndex).parentIndex : -1; + _rightHandJointIndex = hfmModel.rightHandJointIndex; + _rightElbowJointIndex = _rightHandJointIndex >= 0 ? hfmModel.joints.at(_rightHandJointIndex).parentIndex : -1; + _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? hfmModel.joints.at(_rightElbowJointIndex).parentIndex : -1; - _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.leftEyeJointIndex); - _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.rightEyeJointIndex); + _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.leftEyeJointIndex); + _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.rightEyeJointIndex); if (!_animGraphURL.isEmpty()) { _animNode.reset(); @@ -1938,7 +1938,7 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { data.rotationIsDefaultPose = isEqual(data.rotation, defaultAbsRot); // translations are in relative frame but scaled so that they are in meters, - // instead of geometry units. + // instead of model units. glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans(); data.translation = _geometryOffset.scale() * (!_sendNetworkNode ? _internalPoseSet._relativePoses[i].trans() : _networkPoseSet._relativePoses[i].trans()); data.translationIsDefaultPose = isEqual(data.translation, defaultRelTrans); @@ -1963,7 +1963,7 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { return; } - // make a vector of rotations in absolute-geometry-frame + // make a vector of rotations in absolute-model-frame std::vector rotations; rotations.reserve(numJoints); const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); @@ -1972,7 +1972,7 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { if (data.rotationIsDefaultPose) { rotations.push_back(absoluteDefaultPoses[i].rot()); } else { - // JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame + // JointData rotations are in absolute rig-frame so we rotate them to absolute model-frame rotations.push_back(rigToGeometryRot * data.rotation); } } @@ -2008,7 +2008,7 @@ void Rig::computeExternalPoses(const glm::mat4& modelOffsetMat) { } void Rig::computeAvatarBoundingCapsule( - const HFMGeometry& geometry, + const HFMModel& hfmModel, float& radiusOut, float& heightOut, glm::vec3& localOffsetOut) const { @@ -2041,7 +2041,7 @@ void Rig::computeAvatarBoundingCapsule( // from the head to the hips when computing the rest of the bounding capsule. int index = indexOfJoint("Head"); while (index != -1) { - const HFMJointShapeInfo& shapeInfo = geometry.joints.at(index).shapeInfo; + const HFMJointShapeInfo& shapeInfo = hfmModel.joints.at(index).shapeInfo; AnimPose pose = _animSkeleton->getAbsoluteDefaultPose(index); if (shapeInfo.points.size() > 0) { for (auto& point : shapeInfo.points) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 61e8672972..345f335f88 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -122,8 +122,8 @@ public: void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreRoleAnimation(const QString& role); - void initJointStates(const HFMGeometry& geometry, const glm::mat4& modelOffset); - void reset(const HFMGeometry& geometry); + void initJointStates(const HFMModel& hfmModel, const glm::mat4& modelOffset); + void reset(const HFMModel& hfmModel); bool jointStatesEmpty(); int getJointStateCount() const; int indexOfJoint(const QString& jointName) const; @@ -210,7 +210,7 @@ public: void copyJointsFromJointData(const QVector& jointDataVec); void computeExternalPoses(const glm::mat4& modelOffsetMat); - void computeAvatarBoundingCapsule(const HFMGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; + void computeAvatarBoundingCapsule(const HFMModel& hfmModel, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; void setEnableInverseKinematics(bool enable); void setEnableAnimations(bool enable); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index b8448e389d..78f994f462 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1311,7 +1311,7 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { case CAMERA_MATRIX_INDEX: { glm::quat rotation; if (_skeletonModel && _skeletonModel->isActive()) { - int headJointIndex = _skeletonModel->getHFMGeometry().headJointIndex; + int headJointIndex = _skeletonModel->getHFMModel().headJointIndex; if (headJointIndex >= 0) { _skeletonModel->getAbsoluteJointRotationInRigFrame(headJointIndex, rotation); } @@ -1360,7 +1360,7 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { case CAMERA_MATRIX_INDEX: { glm::vec3 translation; if (_skeletonModel && _skeletonModel->isActive()) { - int headJointIndex = _skeletonModel->getHFMGeometry().headJointIndex; + int headJointIndex = _skeletonModel->getHFMModel().headJointIndex; if (headJointIndex >= 0) { _skeletonModel->getAbsoluteJointTranslationInRigFrame(headJointIndex, translation); } @@ -1416,7 +1416,7 @@ void Avatar::withValidJointIndicesCache(std::function const& worker) con if (!_modelJointsCached) { _modelJointIndicesCache.clear(); if (_skeletonModel && _skeletonModel->isActive()) { - _modelJointIndicesCache = _skeletonModel->getHFMGeometry().jointIndices; + _modelJointIndicesCache = _skeletonModel->getHFMModel().jointIndices; _modelJointsCached = true; } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index a41cff528b..13ee5854bf 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -54,9 +54,9 @@ void SkeletonModel::setTextures(const QVariantMap& textures) { } void SkeletonModel::initJointStates() { - const HFMGeometry& geometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig.initJointStates(geometry, modelOffset); + _rig.initJointStates(hfmModel, modelOffset); { // initialize _jointData with proper values for default joints @@ -66,7 +66,7 @@ void SkeletonModel::initJointStates() { } // Determine the default eye position for avatar scale = 1.0 - int headJointIndex = geometry.headJointIndex; + int headJointIndex = hfmModel.headJointIndex; if (0 > headJointIndex || headJointIndex >= _rig.getJointStateCount()) { qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig.getJointStateCount(); } @@ -74,7 +74,7 @@ void SkeletonModel::initJointStates() { getEyeModelPositions(leftEyePosition, rightEyePosition); glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.0f; - int rootJointIndex = geometry.rootJointIndex; + int rootJointIndex = hfmModel.rootJointIndex; glm::vec3 rootModelPosition; getJointPosition(rootJointIndex, rootModelPosition); @@ -96,7 +96,7 @@ void SkeletonModel::initJointStates() { // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { assert(!_owningAvatar->isMyAvatar()); - const HFMGeometry& geometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); Head* head = _owningAvatar->getHead(); @@ -124,7 +124,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // If the head is not positioned, updateEyeJoints won't get the math right glm::quat headOrientation; - _rig.getJointRotation(geometry.headJointIndex, headOrientation); + _rig.getJointRotation(hfmModel.headJointIndex, headOrientation); glm::vec3 eulers = safeEulerAngles(headOrientation); head->setBasePitch(glm::degrees(-eulers.x)); head->setBaseYaw(glm::degrees(eulers.y)); @@ -135,8 +135,8 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.eyeSaccade = glm::vec3(0.0f); eyeParams.modelRotation = getRotation(); eyeParams.modelTranslation = getTranslation(); - eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; - eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; + eyeParams.leftEyeJointIndex = hfmModel.leftEyeJointIndex; + eyeParams.rightEyeJointIndex = hfmModel.rightEyeJointIndex; _rig.updateFromEyeParameters(eyeParams); } @@ -259,45 +259,45 @@ bool SkeletonModel::getRightShoulderPosition(glm::vec3& position) const { } bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const { - return isActive() && getJointPositionInWorldFrame(getHFMGeometry().headJointIndex, headPosition); + return isActive() && getJointPositionInWorldFrame(getHFMModel().headJointIndex, headPosition); } bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPositionInWorldFrame(getHFMGeometry().neckJointIndex, neckPosition); + return isActive() && getJointPositionInWorldFrame(getHFMModel().neckJointIndex, neckPosition); } bool SkeletonModel::getLocalNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPosition(getHFMGeometry().neckJointIndex, neckPosition); + return isActive() && getJointPosition(getHFMModel().neckJointIndex, neckPosition); } bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { return false; } - const HFMGeometry& geometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); - if (getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) && - getJointPosition(geometry.rightEyeJointIndex, secondEyePosition)) { + if (getJointPosition(hfmModel.leftEyeJointIndex, firstEyePosition) && + getJointPosition(hfmModel.rightEyeJointIndex, secondEyePosition)) { return true; } // no eye joints; try to estimate based on head/neck joints glm::vec3 neckPosition, headPosition; - if (getJointPosition(geometry.neckJointIndex, neckPosition) && - getJointPosition(geometry.headJointIndex, headPosition)) { + if (getJointPosition(hfmModel.neckJointIndex, neckPosition) && + getJointPosition(hfmModel.headJointIndex, headPosition)) { const float EYE_PROPORTION = 0.6f; glm::vec3 baseEyePosition = glm::mix(neckPosition, headPosition, EYE_PROPORTION); glm::quat headRotation; - getJointRotation(geometry.headJointIndex, headRotation); + getJointRotation(hfmModel.headJointIndex, headRotation); const float EYES_FORWARD = 0.25f; const float EYE_SEPARATION = 0.1f; float headHeight = glm::distance(neckPosition, headPosition); firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; return true; - } else if (getJointPosition(geometry.headJointIndex, headPosition)) { + } else if (getJointPosition(hfmModel.headJointIndex, headPosition)) { glm::vec3 baseEyePosition = headPosition; glm::quat headRotation; - getJointRotation(geometry.headJointIndex, headRotation); + getJointRotation(hfmModel.headJointIndex, headRotation); const float EYES_FORWARD_HEAD_ONLY = 0.30f; const float EYE_SEPARATION = 0.1f; firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); @@ -330,15 +330,15 @@ void SkeletonModel::computeBoundingShape() { return; } - const HFMGeometry& geometry = getHFMGeometry(); - if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) { + const HFMModel& model = getHFMModel(); + if (model.joints.isEmpty() || model.rootJointIndex == -1) { // rootJointIndex == -1 if the avatar model has no skeleton return; } float radius, height; glm::vec3 offset; - _rig.computeAvatarBoundingCapsule(geometry, radius, height, offset); + _rig.computeAvatarBoundingCapsule(model, radius, height, offset); float invScale = 1.0f / _owningAvatar->getModelScale(); _boundingCapsuleRadius = invScale * radius; _boundingCapsuleHeight = invScale * height; @@ -369,7 +369,7 @@ void SkeletonModel::renderBoundingCollisionShapes(RenderArgs* args, gpu::Batch& } bool SkeletonModel::hasSkeleton() { - return isActive() ? getHFMGeometry().rootJointIndex != -1 : false; + return isActive() ? getHFMModel().rootJointIndex != -1 : false; } void SkeletonModel::onInvalidate() { diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index 6c533a5941..c53cf8d333 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -41,10 +41,10 @@ public: void updateAttitude(const glm::quat& orientation); /// Returns the index of the left hand joint, or -1 if not found. - int getLeftHandJointIndex() const { return isActive() ? getHFMGeometry().leftHandJointIndex : -1; } + int getLeftHandJointIndex() const { return isActive() ? getHFMModel().leftHandJointIndex : -1; } /// Returns the index of the right hand joint, or -1 if not found. - int getRightHandJointIndex() const { return isActive() ? getHFMGeometry().rightHandJointIndex : -1; } + int getRightHandJointIndex() const { return isActive() ? getHFMModel().rightHandJointIndex : -1; } bool getLeftGrabPosition(glm::vec3& position) const; bool getRightGrabPosition(glm::vec3& position) const; diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 0b76c275d4..9898651268 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -206,7 +206,7 @@ void FBXBaker::importScene() { } #endif - _geometry = reader.extractHFMGeometry({}, _modelURL.toString()); + _model = reader.extractHFMModel({}, _modelURL.toString()); _textureContentMap = reader._textureContent; } @@ -231,7 +231,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { for (FBXNode& objectChild : rootChild.children) { if (objectChild.name == "Geometry") { - // TODO Pull this out of _geometry instead so we don't have to reprocess it + // TODO Pull this out of _model instead so we don't have to reprocess it auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex, false); // Callback to get MaterialID @@ -293,7 +293,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { QHash textureTypes; // enumerate the materials in the extracted geometry so we can determine the texture type for each texture ID - for (const auto& material : _geometry->materials) { + for (const auto& material : _model->materials) { if (material.normalTexture.isBumpmap) { textureTypes[material.normalTexture.id] = BUMP_TEXTURE; } else { diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 8edaf91c79..8dfde12b31 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -53,7 +53,7 @@ private: void rewriteAndBakeSceneModels(); void rewriteAndBakeSceneTextures(); - HFMGeometry* _geometry; + HFMModel* _model; QHash _textureNameMatchCount; QHash _remappedTexturePaths; diff --git a/libraries/baking/src/OBJBaker.cpp b/libraries/baking/src/OBJBaker.cpp index e9130e3fbd..67bd2d5d62 100644 --- a/libraries/baking/src/OBJBaker.cpp +++ b/libraries/baking/src/OBJBaker.cpp @@ -153,7 +153,7 @@ void OBJBaker::bakeOBJ() { checkIfTexturesFinished(); } -void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMGeometry& geometry) { +void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMModel& model) { // Generating FBX Header Node FBXNode headerNode; headerNode.name = FBX_HEADER_EXTENSION; @@ -199,7 +199,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMGeometry& geometry) { // Compress the mesh information and store in dracoNode bool hasDeformers = false; // No concept of deformers for an OBJ FBXNode dracoNode; - compressMesh(geometry.meshes[0], hasDeformers, dracoNode); + compressMesh(model.meshes[0], hasDeformers, dracoNode); geometryNode.children.append(dracoNode); // Generating Object node's child - Model node @@ -214,17 +214,17 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMGeometry& geometry) { objectNode.children = { geometryNode, modelNode }; // Generating Objects node's child - Material node - auto& meshParts = geometry.meshes[0].parts; + auto& meshParts = model.meshes[0].parts; for (auto& meshPart : meshParts) { FBXNode materialNode; materialNode.name = MATERIAL_NODE_NAME; - if (geometry.materials.size() == 1) { + if (model.materials.size() == 1) { // case when no material information is provided, OBJReader considers it as a single default material - for (auto& materialID : geometry.materials.keys()) { - setMaterialNodeProperties(materialNode, materialID, geometry); + for (auto& materialID : model.materials.keys()) { + setMaterialNodeProperties(materialNode, materialID, model); } } else { - setMaterialNodeProperties(materialNode, meshPart.materialID, geometry); + setMaterialNodeProperties(materialNode, meshPart.materialID, model); } objectNode.children.append(materialNode); @@ -235,7 +235,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMGeometry& geometry) { auto size = meshParts.size(); for (int i = 0; i < size; i++) { QString material = meshParts[i].materialID; - HFMMaterial currentMaterial = geometry.materials[material]; + HFMMaterial currentMaterial = model.materials[material]; if (!currentMaterial.albedoTexture.filename.isEmpty() || !currentMaterial.specularTexture.filename.isEmpty()) { auto textureID = nextNodeID(); _mapTextureMaterial.emplace_back(textureID, i); @@ -325,12 +325,12 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMGeometry& geometry) { } // Set properties for material nodes -void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMGeometry& geometry) { +void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMModel& model) { auto materialID = nextNodeID(); _materialIDs.push_back(materialID); materialNode.properties = { materialID, material, MESH }; - HFMMaterial currentMaterial = geometry.materials[material]; + HFMMaterial currentMaterial = model.materials[material]; // Setting the hierarchy: Material -> Properties70 -> P -> Properties FBXNode properties70Node; diff --git a/libraries/baking/src/OBJBaker.h b/libraries/baking/src/OBJBaker.h index 875a500129..f889730ffa 100644 --- a/libraries/baking/src/OBJBaker.h +++ b/libraries/baking/src/OBJBaker.h @@ -39,8 +39,8 @@ private slots: private: void loadOBJ(); - void createFBXNodeTree(FBXNode& rootNode, HFMGeometry& geometry); - void setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMGeometry& geometry); + void createFBXNodeTree(FBXNode& rootNode, HFMModel& model); + void setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMModel& model); NodeID nextNodeID() { return _nodeID++; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index c36f60600f..f60bf20e3d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -268,7 +268,7 @@ EntityItemProperties RenderableModelEntityItem::getProperties(const EntityProper if (model->isLoaded()) { // TODO: improve naturalDimensions in the future, // for now we've added this hack for setting natural dimensions of models - Extents meshExtents = model->getHFMGeometry().getUnscaledMeshExtents(); + Extents meshExtents = model->getHFMModel().getUnscaledMeshExtents(); properties.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); properties.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); } @@ -403,7 +403,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // should never fall in here when collision model not fully loaded // TODO: assert that all geometries exist and are loaded //assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); - const HFMGeometry& collisionGeometry = _compoundShapeResource->getHFMGeometry(); + const HFMModel& collisionGeometry = _compoundShapeResource->getHFMModel(); ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); @@ -478,7 +478,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // to the visual model and apply them to the collision model (without regard for the // collision model's extents). - glm::vec3 scaleToFit = dimensions / model->getHFMGeometry().getUnscaledMeshExtents().size(); + glm::vec3 scaleToFit = dimensions / model->getHFMModel().getUnscaledMeshExtents().size(); // multiply each point by scale before handing the point-set off to the physics engine. // also determine the extents of the collision model. glm::vec3 registrationOffset = dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()); @@ -498,12 +498,12 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // compute meshPart local transforms QVector localTransforms; - const HFMGeometry& hfmGeometry = model->getHFMGeometry(); - int numHFMMeshes = hfmGeometry.meshes.size(); + const HFMModel& hfmModel = model->getHFMModel(); + int numHFMMeshes = hfmModel.meshes.size(); int totalNumVertices = 0; glm::mat4 invRegistraionOffset = glm::translate(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); for (int i = 0; i < numHFMMeshes; i++) { - const HFMMesh& mesh = hfmGeometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); if (mesh.clusters.size() > 0) { const HFMCluster& cluster = mesh.clusters.at(0); auto jointMatrix = model->getRig().getJointTransform(cluster.jointIndex); @@ -524,7 +524,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { std::vector> meshes; if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { - auto& hfmMeshes = _compoundShapeResource->getHFMGeometry().meshes; + auto& hfmMeshes = _compoundShapeResource->getHFMModel().meshes; meshes.reserve(hfmMeshes.size()); for (auto& hfmMesh : hfmMeshes) { meshes.push_back(hfmMesh._mesh); @@ -755,7 +755,7 @@ int RenderableModelEntityItem::avatarJointIndex(int modelJointIndex) { bool RenderableModelEntityItem::contains(const glm::vec3& point) const { auto model = getModel(); if (EntityItem::contains(point) && model && _compoundShapeResource && _compoundShapeResource->isLoaded()) { - return _compoundShapeResource->getHFMGeometry().convexHullContains(worldToEntity(point)); + return _compoundShapeResource->getHFMModel().convexHullContains(worldToEntity(point)); } return false; @@ -1159,11 +1159,11 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - QStringList animationJointNames = _animation->getGeometry().getJointNames(); - auto& hfmJoints = _animation->getGeometry().joints; + QStringList animationJointNames = _animation->getHFMModel().getJointNames(); + auto& hfmJoints = _animation->getHFMModel().joints; - auto& originalHFMJoints = _model->getHFMGeometry().joints; - auto& originalHFMIndices = _model->getHFMGeometry().jointIndices; + auto& originalHFMJoints = _model->getHFMModel().joints; + auto& originalHFMIndices = _model->getHFMModel().jointIndices; bool allowTranslation = entity->getAnimationAllowTranslation(); diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 8051dbafea..84caa98ace 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -231,7 +231,7 @@ public: bool needTangentSpace() const; }; -/// A single mesh (with optional blendshapes) extracted from an FBX document. +/// A single mesh (with optional blendshapes). class HFMMesh { public: @@ -277,7 +277,7 @@ public: * @property {Quat[]} rotations * @property {Vec3[]} translations */ -/// A single animation frame extracted from an FBX document. +/// A single animation frame. class HFMAnimationFrame { public: QVector rotations; @@ -305,10 +305,10 @@ public: Q_DECLARE_METATYPE(HFMAnimationFrame) Q_DECLARE_METATYPE(QVector) -/// A set of meshes extracted from an FBX document. -class HFMGeometry { +/// The runtime model format. +class HFMModel { public: - using Pointer = std::shared_ptr; + using Pointer = std::shared_ptr; QString originalURL; QString author; @@ -368,7 +368,7 @@ public: QList blendshapeChannelNames; }; -Q_DECLARE_METATYPE(HFMGeometry) -Q_DECLARE_METATYPE(HFMGeometry::Pointer) +Q_DECLARE_METATYPE(HFMModel) +Q_DECLARE_METATYPE(HFMModel::Pointer) #endif // hifi_FBX_h_ diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index df6abbfdf2..15ba5f0101 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -40,9 +40,9 @@ using namespace std; -int HFMGeometryPointerMetaTypeId = qRegisterMetaType(); +int HFMModelPointerMetaTypeId = qRegisterMetaType(); -QStringList HFMGeometry::getJointNames() const { +QStringList HFMModel::getJointNames() const { QStringList names; foreach (const HFMJoint& joint, joints) { names.append(joint.name); @@ -50,7 +50,7 @@ QStringList HFMGeometry::getJointNames() const { return names; } -bool HFMGeometry::hasBlendedMeshes() const { +bool HFMModel::hasBlendedMeshes() const { if (!meshes.isEmpty()) { foreach (const HFMMesh& mesh, meshes) { if (!mesh.blendshapes.isEmpty()) { @@ -61,7 +61,7 @@ bool HFMGeometry::hasBlendedMeshes() const { return false; } -Extents HFMGeometry::getUnscaledMeshExtents() const { +Extents HFMModel::getUnscaledMeshExtents() const { const Extents& extents = meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which @@ -74,7 +74,7 @@ Extents HFMGeometry::getUnscaledMeshExtents() const { } // TODO: Move to graphics::Mesh when Sam's ready -bool HFMGeometry::convexHullContains(const glm::vec3& point) const { +bool HFMModel::convexHullContains(const glm::vec3& point) const { if (!getUnscaledMeshExtents().containsPoint(point)) { return false; } @@ -124,16 +124,16 @@ bool HFMGeometry::convexHullContains(const glm::vec3& point) const { return false; } -QString HFMGeometry::getModelNameOfMesh(int meshIndex) const { +QString HFMModel::getModelNameOfMesh(int meshIndex) const { if (meshIndicesToModelNames.contains(meshIndex)) { return meshIndicesToModelNames.value(meshIndex); } return QString(); } -int hfmGeometryMetaTypeId = qRegisterMetaType(); +int hfmModelMetaTypeId = qRegisterMetaType(); int hfmAnimationFrameMetaTypeId = qRegisterMetaType(); -int hfmAnimationFrameVectorMetaTypeId = qRegisterMetaType >(); +int hfmAnimationFrameVectorMetaTypeId = qRegisterMetaType>(); glm::vec3 parseVec3(const QString& string) { @@ -403,7 +403,7 @@ static void createTangents(const HFMMesh& mesh, bool generateFromTexCoords, setTangents(mesh, accessor, part.triangleIndices.at(i + 2), part.triangleIndices.at(i), vertices, normals, tangents); } if ((part.triangleIndices.size() % 3) != 0) { - qCDebug(modelformat) << "Error in extractHFMGeometry part.triangleIndices.size() is not divisible by three "; + qCDebug(modelformat) << "Error in extractHFMModel part.triangleIndices.size() is not divisible by three "; } } } @@ -615,7 +615,7 @@ QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) { return filepath.mid(filepath.lastIndexOf('/') + 1); } -HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QString& url) { +HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& url) { const FBXNode& node = _rootNode; QMap meshes; QHash modelIDsToNames; @@ -624,7 +624,7 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS QVector blendshapes; - QHash models; + QHash fbxModels; QHash clusters; QHash animationCurves; @@ -689,10 +689,10 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS #if defined(DEBUG_FBXREADER) int unknown = 0; #endif - HFMGeometry* geometryPtr = new HFMGeometry; - HFMGeometry& geometry = *geometryPtr; + HFMModel* modelPtr = new HFMModel; + HFMModel& model = *modelPtr; - geometry.originalURL = url; + model.originalURL = url; float unitScaleFactor = 1.0f; glm::vec3 ambientColor; @@ -708,7 +708,7 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS if (subobject.name == "MetaData") { foreach (const FBXNode& subsubobject, subobject.children) { if (subsubobject.name == "Author") { - geometry.author = subsubobject.properties.at(0).toString(); + model.author = subsubobject.properties.at(0).toString(); } } } else if (subobject.name == "Properties70") { @@ -716,7 +716,7 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS static const QVariant APPLICATION_NAME = QVariant(QByteArray("Original|ApplicationName")); if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 && subsubobject.properties.at(0) == APPLICATION_NAME) { - geometry.applicationName = subsubobject.properties.at(4).toString(); + model.applicationName = subsubobject.properties.at(4).toString(); } } } @@ -814,7 +814,7 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS glm::vec3 geometricRotation; glm::vec3 rotationMin, rotationMax; - FBXModel model = { name, -1, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(), + FBXModel fbxModel = { name, -1, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(), glm::mat4(), glm::vec3(), glm::vec3(), false, glm::vec3(), glm::quat(), glm::vec3(1.0f) }; ExtractedMesh* mesh = NULL; @@ -963,27 +963,27 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS } // see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html - model.translation = translation; + fbxModel.translation = translation; - model.preTransform = glm::translate(rotationOffset) * glm::translate(rotationPivot); - model.preRotation = glm::quat(glm::radians(preRotation)); - model.rotation = glm::quat(glm::radians(rotation)); - model.postRotation = glm::inverse(glm::quat(glm::radians(postRotation))); - model.postTransform = glm::translate(-rotationPivot) * glm::translate(scaleOffset) * + fbxModel.preTransform = glm::translate(rotationOffset) * glm::translate(rotationPivot); + fbxModel.preRotation = glm::quat(glm::radians(preRotation)); + fbxModel.rotation = glm::quat(glm::radians(rotation)); + fbxModel.postRotation = glm::inverse(glm::quat(glm::radians(postRotation))); + fbxModel.postTransform = glm::translate(-rotationPivot) * glm::translate(scaleOffset) * glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot); // NOTE: angles from the FBX file are in degrees // so we convert them to radians for the FBXModel class - model.rotationMin = glm::radians(glm::vec3(rotationMinX ? rotationMin.x : -180.0f, + fbxModel.rotationMin = glm::radians(glm::vec3(rotationMinX ? rotationMin.x : -180.0f, rotationMinY ? rotationMin.y : -180.0f, rotationMinZ ? rotationMin.z : -180.0f)); - model.rotationMax = glm::radians(glm::vec3(rotationMaxX ? rotationMax.x : 180.0f, + fbxModel.rotationMax = glm::radians(glm::vec3(rotationMaxX ? rotationMax.x : 180.0f, rotationMaxY ? rotationMax.y : 180.0f, rotationMaxZ ? rotationMax.z : 180.0f)); - model.hasGeometricOffset = hasGeometricOffset; - model.geometricTranslation = geometricTranslation; - model.geometricRotation = glm::quat(glm::radians(geometricRotation)); - model.geometricScaling = geometricScaling; + fbxModel.hasGeometricOffset = hasGeometricOffset; + fbxModel.geometricTranslation = geometricTranslation; + fbxModel.geometricRotation = glm::quat(glm::radians(geometricRotation)); + fbxModel.geometricScaling = geometricScaling; - models.insert(getID(object.properties), model); + fbxModels.insert(getID(object.properties), fbxModel); } else if (object.name == "Texture") { TextureParam tex; foreach (const FBXNode& subobject, object.children) { @@ -1307,7 +1307,7 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS name = name.mid(name.lastIndexOf('.') + 1); } QString id = getID(object.properties); - geometry.blendshapeChannelNames << name; + model.blendshapeChannelNames << name; foreach (const WeightedIndex& index, blendshapeIndices.values(name)) { blendshapeChannelIndices.insert(id, index); } @@ -1454,26 +1454,26 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS float offsetScale = mapping.value("scale", 1.0f).toFloat() * unitScaleFactor * METERS_PER_CENTIMETER; glm::quat offsetRotation = glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(), mapping.value("ry").toFloat(), mapping.value("rz").toFloat()))); - geometry.offset = glm::translate(glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), + model.offset = glm::translate(glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), mapping.value("tz").toFloat())) * glm::mat4_cast(offsetRotation) * glm::scale(glm::vec3(offsetScale, offsetScale, offsetScale)); // get the list of models in depth-first traversal order QVector modelIDs; - QSet remainingModels; - for (QHash::const_iterator model = models.constBegin(); model != models.constEnd(); model++) { + QSet remainingFBXModels; + for (QHash::const_iterator fbxModel = fbxModels.constBegin(); fbxModel != fbxModels.constEnd(); fbxModel++) { // models with clusters must be parented to the cluster top // Unless the model is a root node. - bool isARootNode = !modelIDs.contains(_connectionParentMap.value(model.key())); + bool isARootNode = !modelIDs.contains(_connectionParentMap.value(fbxModel.key())); if (!isARootNode) { - foreach(const QString& deformerID, _connectionChildMap.values(model.key())) { + foreach(const QString& deformerID, _connectionChildMap.values(fbxModel.key())) { foreach(const QString& clusterID, _connectionChildMap.values(deformerID)) { if (!clusters.contains(clusterID)) { continue; } - QString topID = getTopModelID(_connectionParentMap, models, _connectionChildMap.value(clusterID), url); - _connectionChildMap.remove(_connectionParentMap.take(model.key()), model.key()); - _connectionParentMap.insert(model.key(), topID); + QString topID = getTopModelID(_connectionParentMap, fbxModels, _connectionChildMap.value(clusterID), url); + _connectionChildMap.remove(_connectionParentMap.take(fbxModel.key()), fbxModel.key()); + _connectionParentMap.insert(fbxModel.key(), topID); goto outerBreak; } } @@ -1481,21 +1481,21 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS } // make sure the parent is in the child map - QString parent = _connectionParentMap.value(model.key()); - if (!_connectionChildMap.contains(parent, model.key())) { - _connectionChildMap.insert(parent, model.key()); + QString parent = _connectionParentMap.value(fbxModel.key()); + if (!_connectionChildMap.contains(parent, fbxModel.key())) { + _connectionChildMap.insert(parent, fbxModel.key()); } - remainingModels.insert(model.key()); + remainingFBXModels.insert(fbxModel.key()); } - while (!remainingModels.isEmpty()) { - QString first = *remainingModels.constBegin(); - foreach (const QString& id, remainingModels) { + while (!remainingFBXModels.isEmpty()) { + QString first = *remainingFBXModels.constBegin(); + foreach (const QString& id, remainingFBXModels) { if (id < first) { first = id; } } - QString topID = getTopModelID(_connectionParentMap, models, first, url); - appendModelIDs(_connectionParentMap.value(topID), _connectionChildMap, models, remainingModels, modelIDs, true); + QString topID = getTopModelID(_connectionParentMap, fbxModels, first, url); + appendModelIDs(_connectionParentMap.value(topID), _connectionChildMap, fbxModels, remainingFBXModels, modelIDs, true); } // figure the number of animation frames from the curves @@ -1507,53 +1507,53 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS HFMAnimationFrame frame; frame.rotations.resize(modelIDs.size()); frame.translations.resize(modelIDs.size()); - geometry.animationFrames.append(frame); + model.animationFrames.append(frame); } // convert the models to joints QVariantList freeJoints = mapping.values("freeJoint"); - geometry.hasSkeletonJoints = false; + model.hasSkeletonJoints = false; foreach (const QString& modelID, modelIDs) { - const FBXModel& model = models[modelID]; + const FBXModel& fbxModel = fbxModels[modelID]; HFMJoint joint; - joint.isFree = freeJoints.contains(model.name); - joint.parentIndex = model.parentIndex; + joint.isFree = freeJoints.contains(fbxModel.name); + joint.parentIndex = fbxModel.parentIndex; // get the indices of all ancestors starting with the first free one (if any) - int jointIndex = geometry.joints.size(); + int jointIndex = model.joints.size(); joint.freeLineage.append(jointIndex); int lastFreeIndex = joint.isFree ? 0 : -1; - for (int index = joint.parentIndex; index != -1; index = geometry.joints.at(index).parentIndex) { - if (geometry.joints.at(index).isFree) { + for (int index = joint.parentIndex; index != -1; index = model.joints.at(index).parentIndex) { + if (model.joints.at(index).isFree) { lastFreeIndex = joint.freeLineage.size(); } joint.freeLineage.append(index); } joint.freeLineage.remove(lastFreeIndex + 1, joint.freeLineage.size() - lastFreeIndex - 1); - joint.translation = model.translation; // these are usually in centimeters - joint.preTransform = model.preTransform; - joint.preRotation = model.preRotation; - joint.rotation = model.rotation; - joint.postRotation = model.postRotation; - joint.postTransform = model.postTransform; - joint.rotationMin = model.rotationMin; - joint.rotationMax = model.rotationMax; + joint.translation = fbxModel.translation; // these are usually in centimeters + joint.preTransform = fbxModel.preTransform; + joint.preRotation = fbxModel.preRotation; + joint.rotation = fbxModel.rotation; + joint.postRotation = fbxModel.postRotation; + joint.postTransform = fbxModel.postTransform; + joint.rotationMin = fbxModel.rotationMin; + joint.rotationMax = fbxModel.rotationMax; - joint.hasGeometricOffset = model.hasGeometricOffset; - joint.geometricTranslation = model.geometricTranslation; - joint.geometricRotation = model.geometricRotation; - joint.geometricScaling = model.geometricScaling; + joint.hasGeometricOffset = fbxModel.hasGeometricOffset; + joint.geometricTranslation = fbxModel.geometricTranslation; + joint.geometricRotation = fbxModel.geometricRotation; + joint.geometricScaling = fbxModel.geometricScaling; glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; if (joint.parentIndex == -1) { - joint.transform = geometry.offset * glm::translate(joint.translation) * joint.preTransform * + joint.transform = model.offset * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation); joint.distanceToParent = 0.0f; } else { - const HFMJoint& parentJoint = geometry.joints.at(joint.parentIndex); + const HFMJoint& parentJoint = model.joints.at(joint.parentIndex); joint.transform = parentJoint.transform * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation) * parentJoint.inverseDefaultRotation; @@ -1561,20 +1561,20 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS extractTranslation(joint.transform)); } joint.inverseBindRotation = joint.inverseDefaultRotation; - joint.name = model.name; + joint.name = fbxModel.name; foreach (const QString& childID, _connectionChildMap.values(modelID)) { QString type = typeFlags.value(childID); if (!type.isEmpty()) { - geometry.hasSkeletonJoints |= (joint.isSkeletonJoint = type.toLower().contains("Skeleton")); + model.hasSkeletonJoints |= (joint.isSkeletonJoint = type.toLower().contains("Skeleton")); break; } } joint.bindTransformFoundInCluster = false; - geometry.joints.append(joint); - geometry.jointIndices.insert(model.name, geometry.joints.size()); + model.joints.append(joint); + model.jointIndices.insert(fbxModel.name, model.joints.size()); QString rotationID = localRotations.value(modelID); AnimationCurve xRotCurve = animationCurves.value(xComponents.value(rotationID)); @@ -1590,11 +1590,11 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS glm::vec3 defaultPosValues = joint.translation; for (int i = 0; i < frameCount; i++) { - geometry.animationFrames[i].rotations[jointIndex] = glm::quat(glm::radians(glm::vec3( + model.animationFrames[i].rotations[jointIndex] = glm::quat(glm::radians(glm::vec3( xRotCurve.values.isEmpty() ? defaultRotValues.x : xRotCurve.values.at(i % xRotCurve.values.size()), yRotCurve.values.isEmpty() ? defaultRotValues.y : yRotCurve.values.at(i % yRotCurve.values.size()), zRotCurve.values.isEmpty() ? defaultRotValues.z : zRotCurve.values.at(i % zRotCurve.values.size())))); - geometry.animationFrames[i].translations[jointIndex] = glm::vec3( + model.animationFrames[i].translations[jointIndex] = glm::vec3( xPosCurve.values.isEmpty() ? defaultPosValues.x : xPosCurve.values.at(i % xPosCurve.values.size()), yPosCurve.values.isEmpty() ? defaultPosValues.y : yPosCurve.values.at(i % yPosCurve.values.size()), zPosCurve.values.isEmpty() ? defaultPosValues.z : zPosCurve.values.at(i % zPosCurve.values.size())); @@ -1603,32 +1603,32 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS // NOTE: shapeVertices are in joint-frame std::vector shapeVertices; - shapeVertices.resize(std::max(1, geometry.joints.size()) ); + shapeVertices.resize(std::max(1, model.joints.size()) ); // find our special joints - geometry.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID); - geometry.rightEyeJointIndex = modelIDs.indexOf(jointEyeRightID); - geometry.neckJointIndex = modelIDs.indexOf(jointNeckID); - geometry.rootJointIndex = modelIDs.indexOf(jointRootID); - geometry.leanJointIndex = modelIDs.indexOf(jointLeanID); - geometry.headJointIndex = modelIDs.indexOf(jointHeadID); - geometry.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID); - geometry.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); - geometry.leftToeJointIndex = modelIDs.indexOf(jointLeftToeID); - geometry.rightToeJointIndex = modelIDs.indexOf(jointRightToeID); + model.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID); + model.rightEyeJointIndex = modelIDs.indexOf(jointEyeRightID); + model.neckJointIndex = modelIDs.indexOf(jointNeckID); + model.rootJointIndex = modelIDs.indexOf(jointRootID); + model.leanJointIndex = modelIDs.indexOf(jointLeanID); + model.headJointIndex = modelIDs.indexOf(jointHeadID); + model.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID); + model.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); + model.leftToeJointIndex = modelIDs.indexOf(jointLeftToeID); + model.rightToeJointIndex = modelIDs.indexOf(jointRightToeID); foreach (const QString& id, humanIKJointIDs) { - geometry.humanIKJointIndices.append(modelIDs.indexOf(id)); + model.humanIKJointIndices.append(modelIDs.indexOf(id)); } // extract the translation component of the neck transform - if (geometry.neckJointIndex != -1) { - const glm::mat4& transform = geometry.joints.at(geometry.neckJointIndex).transform; - geometry.neckPivot = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); + if (model.neckJointIndex != -1) { + const glm::mat4& transform = model.joints.at(model.neckJointIndex).transform; + model.neckPivot = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); } - geometry.bindExtents.reset(); - geometry.meshExtents.reset(); + model.bindExtents.reset(); + model.meshExtents.reset(); // Create the Material Library consolidateHFMMaterials(mapping); @@ -1664,7 +1664,7 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS } } #endif - geometry.materials = _hfmMaterials; + model.materials = _hfmMaterials; // see if any materials have texture children bool materialsHaveTextures = checkMaterialsHaveTextures(_hfmMaterials, _textureFilenames, _connectionChildMap); @@ -1675,14 +1675,14 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS extracted.mesh.meshExtents.reset(); // accumulate local transforms - QString modelID = models.contains(it.key()) ? it.key() : _connectionParentMap.value(it.key()); - glm::mat4 modelTransform = getGlobalTransform(_connectionParentMap, models, modelID, geometry.applicationName == "mixamo.com", url); + QString modelID = fbxModels.contains(it.key()) ? it.key() : _connectionParentMap.value(it.key()); + glm::mat4 modelTransform = getGlobalTransform(_connectionParentMap, fbxModels, modelID, model.applicationName == "mixamo.com", url); // compute the mesh extents from the transformed vertices foreach (const glm::vec3& vertex, extracted.mesh.vertices) { glm::vec3 transformedVertex = glm::vec3(modelTransform * glm::vec4(vertex, 1.0f)); - geometry.meshExtents.minimum = glm::min(geometry.meshExtents.minimum, transformedVertex); - geometry.meshExtents.maximum = glm::max(geometry.meshExtents.maximum, transformedVertex); + model.meshExtents.minimum = glm::min(model.meshExtents.minimum, transformedVertex); + model.meshExtents.maximum = glm::max(model.meshExtents.maximum, transformedVertex); extracted.mesh.meshExtents.minimum = glm::min(extracted.mesh.meshExtents.minimum, transformedVertex); extracted.mesh.meshExtents.maximum = glm::max(extracted.mesh.meshExtents.maximum, transformedVertex); @@ -1763,14 +1763,14 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS extracted.mesh.clusters.append(hfmCluster); // override the bind rotation with the transform link - HFMJoint& joint = geometry.joints[hfmCluster.jointIndex]; + HFMJoint& joint = model.joints[hfmCluster.jointIndex]; joint.inverseBindRotation = glm::inverse(extractRotation(cluster.transformLink)); joint.bindTransform = cluster.transformLink; joint.bindTransformFoundInCluster = true; // update the bind pose extents - glm::vec3 bindTranslation = extractTranslation(geometry.offset * joint.bindTransform); - geometry.bindExtents.addPoint(bindTranslation); + glm::vec3 bindTranslation = extractTranslation(model.offset * joint.bindTransform); + model.bindExtents.addPoint(bindTranslation); } } @@ -1801,14 +1801,14 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS const Cluster& cluster = clusters[clusterID]; const HFMCluster& hfmCluster = extracted.mesh.clusters.at(i); int jointIndex = hfmCluster.jointIndex; - HFMJoint& joint = geometry.joints[jointIndex]; + HFMJoint& joint = model.joints[jointIndex]; glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform; glm::vec3 boneEnd = extractTranslation(transformJointToMesh); glm::vec3 boneBegin = boneEnd; glm::vec3 boneDirection; float boneLength = 0.0f; if (joint.parentIndex != -1) { - boneBegin = extractTranslation(inverseModelTransform * geometry.joints[joint.parentIndex].bindTransform); + boneBegin = extractTranslation(inverseModelTransform * model.joints[joint.parentIndex].bindTransform); boneDirection = boneEnd - boneBegin; boneLength = glm::length(boneDirection); if (boneLength > EPSILON) { @@ -1882,7 +1882,7 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS } else { // this is a single-mesh joint int jointIndex = firstHFMCluster.jointIndex; - HFMJoint& joint = geometry.joints[jointIndex]; + HFMJoint& joint = model.joints[jointIndex]; // transform cluster vertices to joint-frame and save for later glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform; @@ -1902,8 +1902,8 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS } buildModelMesh(extracted.mesh, url); - geometry.meshes.append(extracted.mesh); - int meshIndex = geometry.meshes.size() - 1; + model.meshes.append(extracted.mesh); + int meshIndex = model.meshes.size() - 1; if (extracted.mesh._mesh) { extracted.mesh._mesh->displayName = QString("%1#/mesh/%2").arg(url).arg(meshIndex).toStdString(); extracted.mesh._mesh->modelName = modelIDsToNames.value(modelID).toStdString(); @@ -1923,8 +1923,8 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS }; // now that all joints have been scanned compute a k-Dop bounding volume of mesh - for (int i = 0; i < geometry.joints.size(); ++i) { - HFMJoint& joint = geometry.joints[i]; + for (int i = 0; i < model.joints.size(); ++i) { + HFMJoint& joint = model.joints[i]; // NOTE: points are in joint-frame ShapeVertices& points = shapeVertices.at(i); @@ -1958,7 +1958,7 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS generateBoundryLinesForDop14(joint.shapeInfo.dots, joint.shapeInfo.avgPoint, joint.shapeInfo.debugLines); } } - geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); + model.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); // attempt to map any meshes to a named model for (QHash::const_iterator m = meshIDsToMeshIndices.constBegin(); @@ -1971,14 +1971,14 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS const QString& modelID = ooChildToParent.value(meshID); if (modelIDsToNames.contains(modelID)) { const QString& modelName = modelIDsToNames.value(modelID); - geometry.meshIndicesToModelNames.insert(meshIndex, modelName); + model.meshIndicesToModelNames.insert(meshIndex, modelName); } } } { int i = 0; - for (const auto& mesh : geometry.meshes) { - auto name = geometry.getModelNameOfMesh(i++); + for (const auto& mesh : model.meshes) { + auto name = model.getModelNameOfMesh(i++); if (!name.isEmpty()) { if (mesh._mesh) { mesh._mesh->modelName = name.toStdString(); @@ -1991,16 +1991,16 @@ HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QS } } } - return geometryPtr; + return modelPtr; } -HFMGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { - QBuffer buffer(const_cast(&model)); +HFMModel* readFBX(const QByteArray& data, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { + QBuffer buffer(const_cast(&data)); buffer.open(QIODevice::ReadOnly); return readFBX(&buffer, mapping, url, loadLightmaps, lightmapLevel); } -HFMGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +HFMModel* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { FBXReader reader; reader._rootNode = FBXReader::parseFBX(device); reader._loadLightmaps = loadLightmaps; @@ -2008,5 +2008,5 @@ HFMGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QStri qCDebug(modelformat) << "Reading FBX: " << url; - return reader.extractHFMGeometry(mapping, url); + return reader.extractHFMModel(mapping, url); } diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index f95ba7fe73..d9a216eeb8 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -34,13 +34,13 @@ class QIODevice; class FBXNode; -/// Reads FBX geometry from the supplied model and mapping data. +/// Reads HFMModel from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -HFMGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +HFMModel* readFBX(const QByteArray& data, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); -/// Reads FBX geometry from the supplied model and mapping data. +/// Reads HFMModel from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -HFMGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +HFMModel* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); class TextureParam { public: @@ -103,12 +103,12 @@ class ExtractedMesh; class FBXReader { public: - HFMGeometry* _hfmGeometry; + HFMModel* _hfmModel; FBXNode _rootNode; static FBXNode parseFBX(QIODevice* device); - HFMGeometry* extractHFMGeometry(const QVariantHash& mapping, const QString& url); + HFMModel* extractHFMModel(const QVariantHash& mapping, const QString& url); static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate = true); QHash meshes; diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index 7ee13c5cdf..cc1f3bec1c 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -533,10 +533,10 @@ bool GLTFReader::addTexture(const QJsonObject& object) { return true; } -bool GLTFReader::parseGLTF(const QByteArray& model) { +bool GLTFReader::parseGLTF(const QByteArray& data) { PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr); - QJsonDocument d = QJsonDocument::fromJson(model); + QJsonDocument d = QJsonDocument::fromJson(data); QJsonObject jsFile = d.object(); bool isvalid = setAsset(jsFile); @@ -697,7 +697,7 @@ glm::mat4 GLTFReader::getModelTransform(const GLTFNode& node) { return tmat; } -bool GLTFReader::buildGeometry(HFMGeometry& geometry, const QUrl& url) { +bool GLTFReader::buildGeometry(HFMModel& model, const QUrl& url) { //Build dependencies QVector> nodeDependencies(_file.nodes.size()); @@ -727,17 +727,17 @@ bool GLTFReader::buildGeometry(HFMGeometry& geometry, const QUrl& url) { } //Build default joints - geometry.joints.resize(1); - geometry.joints[0].isFree = false; - geometry.joints[0].parentIndex = -1; - geometry.joints[0].distanceToParent = 0; - geometry.joints[0].translation = glm::vec3(0, 0, 0); - geometry.joints[0].rotationMin = glm::vec3(0, 0, 0); - geometry.joints[0].rotationMax = glm::vec3(0, 0, 0); - geometry.joints[0].name = "OBJ"; - geometry.joints[0].isSkeletonJoint = true; + model.joints.resize(1); + model.joints[0].isFree = false; + model.joints[0].parentIndex = -1; + model.joints[0].distanceToParent = 0; + model.joints[0].translation = glm::vec3(0, 0, 0); + model.joints[0].rotationMin = glm::vec3(0, 0, 0); + model.joints[0].rotationMax = glm::vec3(0, 0, 0); + model.joints[0].name = "OBJ"; + model.joints[0].isSkeletonJoint = true; - geometry.jointIndices["x"] = 1; + model.jointIndices["x"] = 1; //Build materials QVector materialIDs; @@ -750,8 +750,8 @@ bool GLTFReader::buildGeometry(HFMGeometry& geometry, const QUrl& url) { for (int i = 0; i < materialIDs.size(); i++) { QString& matid = materialIDs[i]; - geometry.materials[matid] = HFMMaterial(); - HFMMaterial& hfmMaterial = geometry.materials[matid]; + model.materials[matid] = HFMMaterial(); + HFMMaterial& hfmMaterial = model.materials[matid]; hfmMaterial._material = std::make_shared(); setHFMMaterial(hfmMaterial, _file.materials[i]); } @@ -765,8 +765,8 @@ bool GLTFReader::buildGeometry(HFMGeometry& geometry, const QUrl& url) { if (node.defined["mesh"]) { qCDebug(modelformat) << "node_transforms" << node.transforms; foreach(auto &primitive, _file.meshes[node.mesh].primitives) { - geometry.meshes.append(HFMMesh()); - HFMMesh& mesh = geometry.meshes[geometry.meshes.size() - 1]; + model.meshes.append(HFMMesh()); + HFMMesh& mesh = model.meshes[model.meshes.size() - 1]; HFMCluster cluster; cluster.jointIndex = 0; cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, @@ -886,7 +886,7 @@ bool GLTFReader::buildGeometry(HFMGeometry& geometry, const QUrl& url) { mesh.meshExtents.reset(); foreach(const glm::vec3& vertex, mesh.vertices) { mesh.meshExtents.addPoint(vertex); - geometry.meshExtents.addPoint(vertex); + model.meshExtents.addPoint(vertex); } // since mesh.modelTransform seems to not have any effect I apply the transformation the model @@ -898,7 +898,7 @@ bool GLTFReader::buildGeometry(HFMGeometry& geometry, const QUrl& url) { } } - mesh.meshIndex = geometry.meshes.size(); + mesh.meshIndex = model.meshes.size(); FBXReader::buildModelMesh(mesh, url.toString()); } @@ -910,7 +910,7 @@ bool GLTFReader::buildGeometry(HFMGeometry& geometry, const QUrl& url) { return true; } -HFMGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping, +HFMModel* GLTFReader::readGLTF(QByteArray& data, const QVariantHash& mapping, const QUrl& url, bool loadLightmaps, float lightmapLevel) { _url = url; @@ -922,15 +922,15 @@ HFMGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping _url = QUrl(QFileInfo(localFileName).absoluteFilePath()); } - parseGLTF(model); + parseGLTF(data); //_file.dump(); - HFMGeometry* geometryPtr = new HFMGeometry(); - HFMGeometry& geometry = *geometryPtr; + HFMModel* modelPtr = new HFMModel(); + HFMModel& model = *modelPtr; - buildGeometry(geometry, url); + buildGeometry(model, url); - //hfmDebugDump(geometry); - return geometryPtr; + //hfmDebugDump(data); + return modelPtr; } @@ -1181,37 +1181,37 @@ void GLTFReader::retriangulate(const QVector& inIndices, const QVector(); graphics::MaterialPointer modelMaterial = hfmMaterial._material; @@ -988,15 +988,15 @@ HFMGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m modelMaterial->setOpacity(hfmMaterial.opacity); } - return geometryPtr; + return modelPtr; } -void hfmDebugDump(const HFMGeometry& hfmgeo) { - qCDebug(modelformat) << "---------------- hfmGeometry ----------------"; - qCDebug(modelformat) << " hasSkeletonJoints =" << hfmgeo.hasSkeletonJoints; - qCDebug(modelformat) << " offset =" << hfmgeo.offset; - qCDebug(modelformat) << " meshes.count() =" << hfmgeo.meshes.count(); - foreach (HFMMesh mesh, hfmgeo.meshes) { +void hfmDebugDump(const HFMModel& hfmModel) { + qCDebug(modelformat) << "---------------- hfmModel ----------------"; + qCDebug(modelformat) << " hasSkeletonJoints =" << hfmModel.hasSkeletonJoints; + qCDebug(modelformat) << " offset =" << hfmModel.offset; + qCDebug(modelformat) << " meshes.count() =" << hfmModel.meshes.count(); + foreach (HFMMesh mesh, hfmModel.meshes) { qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.count(); qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); qCDebug(modelformat) << " normals.count() =" << mesh.normals.count(); @@ -1037,10 +1037,10 @@ void hfmDebugDump(const HFMGeometry& hfmgeo) { } } - qCDebug(modelformat) << " jointIndices =" << hfmgeo.jointIndices; - qCDebug(modelformat) << " joints.count() =" << hfmgeo.joints.count(); + qCDebug(modelformat) << " jointIndices =" << hfmModel.jointIndices; + qCDebug(modelformat) << " joints.count() =" << hfmModel.joints.count(); - foreach (HFMJoint joint, hfmgeo.joints) { + foreach (HFMJoint joint, hfmModel.joints) { qCDebug(modelformat) << " isFree =" << joint.isFree; qCDebug(modelformat) << " freeLineage" << joint.freeLineage; qCDebug(modelformat) << " parentIndex" << joint.parentIndex; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 2eb039eba2..1b259d90df 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -87,13 +87,13 @@ public: QString currentMaterialName; QHash materials; - HFMGeometry::Pointer readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); + HFMModel::Pointer readOBJ(QByteArray& data, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); private: QUrl _url; QHash librariesSeen; - bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, HFMGeometry& geometry, + bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, HFMModel& model, float& scaleGuess, bool combineParts); void parseMaterialLibrary(QIODevice* device); void parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions); @@ -104,4 +104,4 @@ private: // What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility. void setMeshPartDefaults(HFMMeshPart& meshPart, QString materialID); -void hfmDebugDump(const HFMGeometry& hfmgeo); +void hfmDebugDump(const HFMModel& hfmModel); diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index a950e1df3c..6430e4599e 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -128,7 +128,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) { void GeometryMappingResource::onGeometryMappingLoaded(bool success) { if (success && _geometryResource) { - _hfmGeometry = _geometryResource->_hfmGeometry; + _hfmModel = _geometryResource->_hfmModel; _meshParts = _geometryResource->_meshParts; _meshes = _geometryResource->_meshes; _materials = _geometryResource->_materials; @@ -193,38 +193,38 @@ void GeometryReader::run() { _url.path().toLower().endsWith(".obj.gz") || _url.path().toLower().endsWith(".gltf"))) { - HFMGeometry::Pointer hfmGeometry; + HFMModel::Pointer hfmModel; if (_url.path().toLower().endsWith(".fbx")) { - hfmGeometry.reset(readFBX(_data, _mapping, _url.path())); - if (hfmGeometry->meshes.size() == 0 && hfmGeometry->joints.size() == 0) { + hfmModel.reset(readFBX(_data, _mapping, _url.path())); + if (hfmModel->meshes.size() == 0 && hfmModel->joints.size() == 0) { throw QString("empty geometry, possibly due to an unsupported FBX version"); } } else if (_url.path().toLower().endsWith(".obj")) { - hfmGeometry = OBJReader().readOBJ(_data, _mapping, _combineParts, _url); + hfmModel = OBJReader().readOBJ(_data, _mapping, _combineParts, _url); } else if (_url.path().toLower().endsWith(".obj.gz")) { QByteArray uncompressedData; if (gunzip(_data, uncompressedData)){ - hfmGeometry = OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url); + hfmModel = OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url); } else { throw QString("failed to decompress .obj.gz"); } } else if (_url.path().toLower().endsWith(".gltf")) { std::shared_ptr glreader = std::make_shared(); - hfmGeometry.reset(glreader->readGLTF(_data, _mapping, _url)); - if (hfmGeometry->meshes.size() == 0 && hfmGeometry->joints.size() == 0) { + hfmModel.reset(glreader->readGLTF(_data, _mapping, _url)); + if (hfmModel->meshes.size() == 0 && hfmModel->joints.size() == 0) { throw QString("empty geometry, possibly due to an unsupported GLTF version"); } } else { throw QString("unsupported format"); } - // Add scripts to hfmGeometry + // Add scripts to hfmModel if (!_mapping.value(SCRIPT_FIELD).isNull()) { QVariantList scripts = _mapping.values(SCRIPT_FIELD); for (auto &script : scripts) { - hfmGeometry->scripts.push_back(script.toString()); + hfmModel->scripts.push_back(script.toString()); } } @@ -234,7 +234,7 @@ void GeometryReader::run() { qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; } else { QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", - Q_ARG(HFMGeometry::Pointer, hfmGeometry)); + Q_ARG(HFMModel::Pointer, hfmModel)); } } else { throw QString("url is invalid"); @@ -262,7 +262,7 @@ public: virtual void downloadFinished(const QByteArray& data) override; protected: - Q_INVOKABLE void setGeometryDefinition(HFMGeometry::Pointer hfmGeometry); + Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel); private: QVariantHash _mapping; @@ -277,13 +277,13 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { QThreadPool::globalInstance()->start(new GeometryReader(_self, _effectiveBaseURL, _mapping, data, _combineParts)); } -void GeometryDefinitionResource::setGeometryDefinition(HFMGeometry::Pointer hfmGeometry) { - // Assume ownership of the geometry pointer - _hfmGeometry = hfmGeometry; +void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel) { + // Assume ownership of the HFMModel pointer + _hfmModel = hfmModel; // Copy materials QHash materialIDAtlas; - for (const HFMMaterial& material : _hfmGeometry->materials) { + for (const HFMMaterial& material : _hfmModel->materials) { materialIDAtlas[material.materialID] = _materials.size(); _materials.push_back(std::make_shared(material, _textureBaseUrl)); } @@ -291,7 +291,7 @@ void GeometryDefinitionResource::setGeometryDefinition(HFMGeometry::Pointer hfmG std::shared_ptr meshes = std::make_shared(); std::shared_ptr parts = std::make_shared(); int meshID = 0; - for (const HFMMesh& mesh : _hfmGeometry->meshes) { + for (const HFMMesh& mesh : _hfmModel->meshes) { // Copy mesh pointers meshes->emplace_back(mesh._mesh); int partID = 0; @@ -371,7 +371,7 @@ const QVariantMap Geometry::getTextures() const { // FIXME: The materials should only be copied when modified, but the Model currently caches the original Geometry::Geometry(const Geometry& geometry) { - _hfmGeometry = geometry._hfmGeometry; + _hfmModel = geometry._hfmModel; _meshes = geometry._meshes; _meshParts = geometry._meshParts; @@ -444,8 +444,8 @@ void GeometryResource::deleter() { } void GeometryResource::setTextures() { - if (_hfmGeometry) { - for (const HFMMaterial& material : _hfmGeometry->materials) { + if (_hfmModel) { + for (const HFMMaterial& material : _hfmModel->materials) { _materials.push_back(std::make_shared(material, _textureBaseUrl)); } } diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 2283c355d8..1bb340b83c 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -45,9 +45,9 @@ public: // Mutable, but must retain structure of vector using NetworkMaterials = std::vector>; - bool isGeometryLoaded() const { return (bool)_hfmGeometry; } + bool isHFMModelLoaded() const { return (bool)_hfmModel; } - const HFMGeometry& getHFMGeometry() const { return *_hfmGeometry; } + const HFMModel& getHFMModel() const { return *_hfmModel; } const GeometryMeshes& getMeshes() const { return *_meshes; } const std::shared_ptr getShapeMaterial(int shapeID) const; @@ -62,7 +62,7 @@ protected: friend class GeometryMappingResource; // Shared across all geometries, constant throughout lifetime - std::shared_ptr _hfmGeometry; + std::shared_ptr _hfmModel; std::shared_ptr _meshes; std::shared_ptr _meshParts; @@ -94,7 +94,7 @@ protected: // Geometries may not hold onto textures while cached - that is for the texture cache // Instead, these methods clear and reset textures from the geometry when caching/loading - bool shouldSetTextures() const { return _hfmGeometry && _materials.empty(); } + bool shouldSetTextures() const { return _hfmModel && _materials.empty(); } void setTextures(); void resetTextures(); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 31d6cef060..c31345bc55 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -32,8 +32,8 @@ bool CauterizedModel::updateGeometry() { bool needsFullUpdate = Model::updateGeometry(); if (_isCauterized && needsFullUpdate) { assert(_cauterizeMeshStates.empty()); - const HFMGeometry& hfmGeometry = getHFMGeometry(); - foreach (const HFMMesh& mesh, hfmGeometry.meshes) { + const HFMModel& hfmModel = getHFMModel(); + foreach (const HFMMesh& mesh, hfmModel.meshes) { Model::MeshState state; if (_useDualQuaternionSkinning) { state.clusterDualQuaternions.resize(mesh.clusters.size()); @@ -76,7 +76,7 @@ void CauterizedModel::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); - const HFMGeometry& hfmGeometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -86,7 +86,7 @@ void CauterizedModel::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - initializeBlendshapes(hfmGeometry.meshes[i], i); + initializeBlendshapes(hfmModel.meshes[i], i); auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast(ptr); @@ -109,11 +109,11 @@ void CauterizedModel::updateClusterMatrices() { return; } _needsUpdateClusterMatrices = false; - const HFMGeometry& geometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; - const HFMMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); if (_useDualQuaternionSkinning) { @@ -133,7 +133,7 @@ void CauterizedModel::updateClusterMatrices() { // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { - AnimPose cauterizePose = _rig.getJointPose(geometry.neckJointIndex); + AnimPose cauterizePose = _rig.getJointPose(hfmModel.neckJointIndex); cauterizePose.scale() = glm::vec3(0.0001f, 0.0001f, 0.0001f); static const glm::mat4 zeroScale( @@ -141,11 +141,11 @@ void CauterizedModel::updateClusterMatrices() { glm::vec4(0.0f, 0.0001f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0001f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); - auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; + auto cauterizeMatrix = _rig.getJointTransform(hfmModel.neckJointIndex) * zeroScale; for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; - const HFMMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); @@ -175,7 +175,7 @@ void CauterizedModel::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 8e2541fdda..b493780aff 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -260,8 +260,8 @@ void ModelMeshPartPayload::initCache(const ModelPointer& model) { _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); _isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX); - const HFMGeometry& geometry = model->getHFMGeometry(); - const HFMMesh& mesh = geometry.meshes.at(_meshIndex); + const HFMModel& hfmModel = model->getHFMModel(); + const HFMMesh& mesh = hfmModel.meshes.at(_meshIndex); _isBlendShaped = !mesh.blendshapes.isEmpty(); _hasTangents = !mesh.tangents.isEmpty(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 65b3fef7c0..6f285a9f0c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -183,11 +183,11 @@ bool Model::shouldInvalidatePayloadShapeKey(int meshIndex) { return true; } - const HFMGeometry& geometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); const auto& networkMeshes = getGeometry()->getMeshes(); // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // to false to rebuild out mesh groups. - if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)geometry.meshes.size() || meshIndex >= (int)_meshStates.size()) { + if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)hfmModel.meshes.size() || meshIndex >= (int)_meshStates.size()) { _needsFixupInScene = true; // trigger remove/add cycle invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid return true; @@ -278,8 +278,8 @@ void Model::setRenderItemsNeedUpdate() { void Model::reset() { if (isLoaded()) { - const HFMGeometry& geometry = getHFMGeometry(); - _rig.reset(geometry); + const HFMModel& hfmModel = getHFMModel(); + _rig.reset(hfmModel); emit rigReset(); emit rigReady(); } @@ -295,13 +295,13 @@ bool Model::updateGeometry() { _needsReload = false; // TODO: should all Models have a valid _rig? - if (_rig.jointStatesEmpty() && getHFMGeometry().joints.size() > 0) { + if (_rig.jointStatesEmpty() && getHFMModel().joints.size() > 0) { initJointStates(); assert(_meshStates.empty()); - const HFMGeometry& hfmGeometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); int i = 0; - foreach (const HFMMesh& mesh, hfmGeometry.meshes) { + foreach (const HFMMesh& mesh, hfmModel.meshes) { MeshState state; state.clusterDualQuaternions.resize(mesh.clusters.size()); state.clusterMatrices.resize(mesh.clusters.size()); @@ -319,10 +319,10 @@ bool Model::updateGeometry() { // virtual void Model::initJointStates() { - const HFMGeometry& geometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig.initJointStates(geometry, modelOffset); + _rig.initJointStates(hfmModel, modelOffset); } bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, @@ -363,9 +363,9 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g int bestShapeID = 0; int bestSubMeshIndex = 0; - const HFMGeometry& geometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); if (!_triangleSetsValid) { - calculateTriangleSets(geometry); + calculateTriangleSets(hfmModel); } glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); @@ -448,7 +448,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g extraInfo["shapeID"] = bestShapeID; if (pickAgainstTriangles) { extraInfo["subMeshIndex"] = bestSubMeshIndex; - extraInfo["subMeshName"] = geometry.getModelNameOfMesh(bestSubMeshIndex); + extraInfo["subMeshName"] = hfmModel.getModelNameOfMesh(bestSubMeshIndex); extraInfo["subMeshTriangleWorld"] = QVariantMap{ { "v0", vec3toVariant(bestWorldTriangle.v0) }, { "v1", vec3toVariant(bestWorldTriangle.v1) }, @@ -506,9 +506,9 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co int bestShapeID = 0; int bestSubMeshIndex = 0; - const HFMGeometry& geometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); if (!_triangleSetsValid) { - calculateTriangleSets(geometry); + calculateTriangleSets(hfmModel); } glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); @@ -595,7 +595,7 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co extraInfo["shapeID"] = bestShapeID; if (pickAgainstTriangles) { extraInfo["subMeshIndex"] = bestSubMeshIndex; - extraInfo["subMeshName"] = geometry.getModelNameOfMesh(bestSubMeshIndex); + extraInfo["subMeshName"] = hfmModel.getModelNameOfMesh(bestSubMeshIndex); extraInfo["subMeshTriangleWorld"] = QVariantMap{ { "v0", vec3toVariant(bestWorldTriangle.v0) }, { "v1", vec3toVariant(bestWorldTriangle.v1) }, @@ -641,7 +641,7 @@ bool Model::convexHullContains(glm::vec3 point) { QMutexLocker locker(&_mutex); if (!_triangleSetsValid) { - calculateTriangleSets(getHFMGeometry()); + calculateTriangleSets(getHFMModel()); } // If we are inside the models box, then consider the submeshes... @@ -753,7 +753,7 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe } // update triangles for picking { - HFMGeometry geometry; + HFMModel hfmModel; for (const auto& newMesh : meshes) { HFMMesh mesh; mesh._mesh = newMesh.getMeshPointer(); @@ -767,15 +767,15 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe { foreach (const glm::vec3& vertex, mesh.vertices) { glm::vec3 transformedVertex = glm::vec3(mesh.modelTransform * glm::vec4(vertex, 1.0f)); - geometry.meshExtents.minimum = glm::min(geometry.meshExtents.minimum, transformedVertex); - geometry.meshExtents.maximum = glm::max(geometry.meshExtents.maximum, transformedVertex); + hfmModel.meshExtents.minimum = glm::min(hfmModel.meshExtents.minimum, transformedVertex); + hfmModel.meshExtents.maximum = glm::max(hfmModel.meshExtents.maximum, transformedVertex); mesh.meshExtents.minimum = glm::min(mesh.meshExtents.minimum, transformedVertex); mesh.meshExtents.maximum = glm::max(mesh.meshExtents.maximum, transformedVertex); } } - geometry.meshes << mesh; + hfmModel.meshes << mesh; } - calculateTriangleSets(geometry); + calculateTriangleSets(hfmModel); } return true; } @@ -789,11 +789,11 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { return result; } - const HFMGeometry& geometry = getHFMGeometry(); - int numberOfMeshes = geometry.meshes.size(); + const HFMModel& hfmModel = getHFMModel(); + int numberOfMeshes = hfmModel.meshes.size(); int shapeID = 0; for (int i = 0; i < numberOfMeshes; i++) { - const HFMMesh& hfmMesh = geometry.meshes.at(i); + const HFMMesh& hfmMesh = hfmModel.meshes.at(i); if (auto mesh = hfmMesh._mesh) { result.append(mesh); @@ -808,17 +808,17 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { return result; } -void Model::calculateTriangleSets(const HFMGeometry& geometry) { +void Model::calculateTriangleSets(const HFMModel& hfmModel) { PROFILE_RANGE(render, __FUNCTION__); - int numberOfMeshes = geometry.meshes.size(); + int numberOfMeshes = hfmModel.meshes.size(); _triangleSetsValid = true; _modelSpaceMeshTriangleSets.clear(); _modelSpaceMeshTriangleSets.resize(numberOfMeshes); for (int i = 0; i < numberOfMeshes; i++) { - const HFMMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); const int numberOfParts = mesh.parts.size(); auto& meshTriangleSets = _modelSpaceMeshTriangleSets[i]; @@ -839,7 +839,7 @@ void Model::calculateTriangleSets(const HFMGeometry& geometry) { int totalTriangles = (numberOfQuads * TRIANGLES_PER_QUAD) + numberOfTris; partTriangleSet.reserve(totalTriangles); - auto meshTransform = geometry.offset * mesh.modelTransform; + auto meshTransform = hfmModel.offset * mesh.modelTransform; if (part.quadIndices.size() > 0) { int vIndex = 0; @@ -1114,7 +1114,7 @@ Extents Model::getBindExtents() const { if (!isActive()) { return Extents(); } - const Extents& bindExtents = getHFMGeometry().bindExtents; + const Extents& bindExtents = getHFMModel().bindExtents; Extents scaledExtents = { bindExtents.minimum * _scale, bindExtents.maximum * _scale }; return scaledExtents; } @@ -1128,12 +1128,12 @@ Extents Model::getMeshExtents() const { if (!isActive()) { return Extents(); } - const Extents& extents = getHFMGeometry().meshExtents; + const Extents& extents = getHFMModel().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which // is captured in the offset matrix - glm::vec3 minimum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.minimum, 1.0f)); - glm::vec3 maximum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + glm::vec3 minimum = glm::vec3(getHFMModel().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(getHFMModel().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum * _scale, maximum * _scale }; return scaledExtents; } @@ -1143,12 +1143,12 @@ Extents Model::getUnscaledMeshExtents() const { return Extents(); } - const Extents& extents = getHFMGeometry().meshExtents; + const Extents& extents = getHFMModel().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which // is captured in the offset matrix - glm::vec3 minimum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.minimum, 1.0f)); - glm::vec3 maximum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + glm::vec3 minimum = glm::vec3(getHFMModel().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(getHFMModel().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum, maximum }; return scaledExtents; @@ -1171,11 +1171,11 @@ void Model::setJointTranslation(int index, bool valid, const glm::vec3& translat } int Model::getParentJointIndex(int jointIndex) const { - return (isActive() && jointIndex != -1) ? getHFMGeometry().joints.at(jointIndex).parentIndex : -1; + return (isActive() && jointIndex != -1) ? getHFMModel().joints.at(jointIndex).parentIndex : -1; } int Model::getLastFreeJointIndex(int jointIndex) const { - return (isActive() && jointIndex != -1) ? getHFMGeometry().joints.at(jointIndex).freeLineage.last() : -1; + return (isActive() && jointIndex != -1) ? getHFMModel().joints.at(jointIndex).freeLineage.last() : -1; } void Model::setTextures(const QVariantMap& textures) { @@ -1275,7 +1275,7 @@ QStringList Model::getJointNames() const { Q_RETURN_ARG(QStringList, result)); return result; } - return isActive() ? getHFMGeometry().getJointNames() : QStringList(); + return isActive() ? getHFMModel().getJointNames() : QStringList(); } void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) { @@ -1415,10 +1415,10 @@ void Model::updateClusterMatrices() { } _needsUpdateClusterMatrices = false; - const HFMGeometry& geometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; - const HFMMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); if (_useDualQuaternionSkinning) { @@ -1436,7 +1436,7 @@ void Model::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } @@ -1505,7 +1505,7 @@ void Model::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); - auto& hfmGeometry = getHFMGeometry(); + auto& hfmModel = getHFMModel(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -1515,7 +1515,7 @@ void Model::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - initializeBlendshapes(hfmGeometry.meshes[i], i); + initializeBlendshapes(hfmModel.meshes[i], i); _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); auto material = getGeometry()->getShapeMaterial(shapeID); _modelMeshMaterialNames.push_back(material ? material->getName() : ""); @@ -1600,7 +1600,7 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string class CollisionRenderGeometry : public Geometry { public: CollisionRenderGeometry(graphics::MeshPointer mesh) { - _hfmGeometry = std::make_shared(); + _hfmModel = std::make_shared(); std::shared_ptr meshes = std::make_shared(); meshes->push_back(mesh); _meshes = meshes; @@ -1656,7 +1656,7 @@ void Blender::run() { if (_model && _model->isLoaded()) { DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); int offset = 0; - auto meshes = _model->getHFMGeometry().meshes; + auto meshes = _model->getHFMModel().meshes; int meshIndex = 0; foreach(const HFMMesh& mesh, meshes) { auto modelMeshBlendshapeOffsets = _model->_blendshapeOffsets.find(meshIndex++); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index db5625b3d8..93a0626d28 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -163,7 +163,7 @@ public: bool maybeStartBlender(); - bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); } + bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isHFMModelLoaded(); } bool isAddedToScene() const { return _addedToScene; } void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; } @@ -184,8 +184,8 @@ public: Q_INVOKABLE virtual void setTextures(const QVariantMap& textures); /// Provided as a convenience, will crash if !isLoaded() - // And so that getGeometry() isn't chained everywhere - const HFMGeometry& getHFMGeometry() const { assert(isLoaded()); return _renderGeometry->getHFMGeometry(); } + // And so that getHFMModel() isn't chained everywhere + const HFMModel& getHFMModel() const { assert(isLoaded()); return _renderGeometry->getHFMModel(); } bool isActive() const { return isLoaded(); } @@ -450,7 +450,7 @@ protected: bool _overrideModelTransform { false }; bool _triangleSetsValid { false }; - void calculateTriangleSets(const HFMGeometry& geometry); + void calculateTriangleSets(const HFMModel& hfmModel); std::vector> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes virtual void createRenderItemSet(); diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 77b09caa1d..f26bad86b0 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -41,11 +41,11 @@ void SoftAttachmentModel::updateClusterMatrices() { _needsUpdateClusterMatrices = false; - const HFMGeometry& geometry = getHFMGeometry(); + const HFMModel& hfmModel = getHFMModel(); for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; - const HFMMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = hfmModel.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const HFMCluster& cluster = mesh.clusters.at(j); @@ -78,7 +78,7 @@ void SoftAttachmentModel::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && hfmModel.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } diff --git a/tests-manual/gpu/src/TestFbx.cpp b/tests-manual/gpu/src/TestFbx.cpp index 9890e4fbe7..9253f8bc91 100644 --- a/tests-manual/gpu/src/TestFbx.cpp +++ b/tests-manual/gpu/src/TestFbx.cpp @@ -100,12 +100,12 @@ bool TestFbx::isReady() const { void TestFbx::parseFbx(const QByteArray& fbxData) { QVariantHash mapping; - HFMGeometry* geometry = readFBX(fbxData, mapping); + HFMModel* hfmModel = readFBX(fbxData, mapping); size_t totalVertexCount = 0; size_t totalIndexCount = 0; size_t totalPartCount = 0; size_t highestIndex = 0; - for (const auto& mesh : geometry->meshes) { + for (const auto& mesh : hfmModel->meshes) { size_t vertexCount = mesh.vertices.size(); totalVertexCount += mesh.vertices.size(); highestIndex = std::max(highestIndex, vertexCount); @@ -123,7 +123,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { std::vector parts; parts.reserve(totalPartCount); _partCount = totalPartCount; - for (const auto& mesh : geometry->meshes) { + for (const auto& mesh : hfmModel->meshes) { baseVertex = vertices.size(); vec3 color; @@ -133,7 +133,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { partIndirect.firstIndex = (uint)indices.size(); partIndirect.baseInstance = (uint)parts.size(); _partTransforms.push_back(mesh.modelTransform); - auto material = geometry->materials[part.materialID]; + auto material = hfmModel->materials[part.materialID]; color = material.diffuseColor; for (auto index : part.quadTrianglesIndices) { indices.push_back(index); @@ -163,7 +163,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { _vertexBuffer->append(vertices); _indexBuffer->append(indices); _indirectBuffer->append(parts); - delete geometry; + delete hfmModel; } void TestFbx::renderTest(size_t testId, RenderArgs* args) { diff --git a/tests-manual/gpu/src/TestFbx.h b/tests-manual/gpu/src/TestFbx.h index 4e22928460..8056af21ec 100644 --- a/tests-manual/gpu/src/TestFbx.h +++ b/tests-manual/gpu/src/TestFbx.h @@ -11,7 +11,7 @@ #include -class HFMGeometry; +class HFMModel; class TestFbx : public GpuTestBase { size_t _partCount { 0 }; diff --git a/tests/animation/src/AnimInverseKinematicsTests.cpp b/tests/animation/src/AnimInverseKinematicsTests.cpp index f51fe12ecb..910770bb0d 100644 --- a/tests/animation/src/AnimInverseKinematicsTests.cpp +++ b/tests/animation/src/AnimInverseKinematicsTests.cpp @@ -28,7 +28,7 @@ const glm::quat identity = glm::quat(); const glm::quat quaterTurnAroundZ = glm::angleAxis(0.5f * PI, zAxis); -void makeTestFBXJoints(HFMGeometry& geometry) { +void makeTestFBXJoints(HFMModel& hfmModel) { HFMJoint joint; joint.isFree = false; joint.freeLineage.clear(); @@ -60,29 +60,29 @@ void makeTestFBXJoints(HFMGeometry& geometry) { joint.name = "A"; joint.parentIndex = -1; joint.translation = origin; - geometry.joints.push_back(joint); + hfmModel.joints.push_back(joint); joint.name = "B"; joint.parentIndex = 0; joint.translation = xAxis; - geometry.joints.push_back(joint); + hfmModel.joints.push_back(joint); joint.name = "C"; joint.parentIndex = 1; joint.translation = xAxis; - geometry.joints.push_back(joint); + hfmModel.joints.push_back(joint); joint.name = "D"; joint.parentIndex = 2; joint.translation = xAxis; - geometry.joints.push_back(joint); + hfmModel.joints.push_back(joint); // compute each joint's transform - for (int i = 1; i < (int)geometry.joints.size(); ++i) { - HFMJoint& j = geometry.joints[i]; + for (int i = 1; i < (int)hfmModel.joints.size(); ++i) { + HFMJoint& j = hfmModel.joints[i]; int parentIndex = j.parentIndex; // World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1) - j.transform = geometry.joints[parentIndex].transform * + j.transform = hfmModel.joints[parentIndex].transform * glm::translate(j.translation) * j.preTransform * glm::mat4_cast(j.preRotation * j.rotation * j.postRotation) * @@ -96,12 +96,12 @@ void AnimInverseKinematicsTests::testSingleChain() { AnimContext context(false, false, false, glm::mat4(), glm::mat4()); - HFMGeometry geometry; - makeTestFBXJoints(geometry); + HFMModel hfmModel; + makeTestFBXJoints(hfmModel); // create a skeleton and doll AnimPose offset; - AnimSkeleton::Pointer skeletonPtr = std::make_shared(geometry); + AnimSkeleton::Pointer skeletonPtr = std::make_shared(hfmModel); AnimInverseKinematics ikDoll("doll"); ikDoll.setSkeleton(skeletonPtr); @@ -119,7 +119,7 @@ void AnimInverseKinematicsTests::testSingleChain() { poses.push_back(pose); pose.trans() = xAxis; - for (int i = 1; i < (int)geometry.joints.size(); ++i) { + for (int i = 1; i < (int)hfmModel.joints.size(); ++i) { poses.push_back(pose); } ikDoll.loadPoses(poses); diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index 5107931da1..10b13aef36 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -54,7 +54,7 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc return; } QByteArray blob = file.readAll(); - std::unique_ptr geometry(readFBX(blob, QVariantHash())); + std::unique_ptr geometry(readFBX(blob, QVariantHash())); std::unique_ptr skeleton(new AnimSkeleton(*geometry)); skeleton->dump(verbose); } diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index bb2958e11d..b5367a77ca 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -19,16 +19,16 @@ // FBXReader jumbles the order of the meshes by reading them back out of a hashtable. This will put // them back in the order in which they appeared in the file. -bool HFMGeometryLessThan(const HFMMesh& e1, const HFMMesh& e2) { +bool HFMModelLessThan(const HFMMesh& e1, const HFMMesh& e2) { return e1.meshIndex < e2.meshIndex; } -void reSortHFMGeometryMeshes(HFMGeometry& geometry) { - qSort(geometry.meshes.begin(), geometry.meshes.end(), HFMGeometryLessThan); +void reSortHFMModelMeshes(HFMModel& model) { + qSort(model.meshes.begin(), model.meshes.end(), HFMModelLessThan); } // Read all the meshes from provided FBX file -bool vhacd::VHACDUtil::loadFBX(const QString filename, HFMGeometry& result) { +bool vhacd::VHACDUtil::loadFBX(const QString filename, HFMModel& result) { if (_verbose) { qDebug() << "reading FBX file =" << filename << "..."; } @@ -41,19 +41,19 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, HFMGeometry& result) { } try { QByteArray fbxContents = fbx.readAll(); - HFMGeometry::Pointer geom; + HFMModel::Pointer model; if (filename.toLower().endsWith(".obj")) { bool combineParts = false; - geom = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts); + model = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts); } else if (filename.toLower().endsWith(".fbx")) { - geom.reset(readFBX(fbxContents, QVariantHash(), filename)); + model.reset(readFBX(fbxContents, QVariantHash(), filename)); } else { qWarning() << "file has unknown extension" << filename; return false; } - result = *geom; + result = *model; - reSortHFMGeometryMeshes(result); + reSortHFMModelMeshes(result); } catch (const QString& error) { qWarning() << "error reading" << filename << ":" << error; return false; @@ -88,7 +88,7 @@ void getTrianglesInMeshPart(const HFMMeshPart &meshPart, std::vector& trian } } -void vhacd::VHACDUtil::fattenMesh(const HFMMesh& mesh, const glm::mat4& geometryOffset, HFMMesh& result) const { +void vhacd::VHACDUtil::fattenMesh(const HFMMesh& mesh, const glm::mat4& modelOffset, HFMMesh& result) const { // this is used to make meshes generated from a highfield collidable. each triangle // is converted into a tetrahedron and made into its own mesh-part. @@ -104,7 +104,7 @@ void vhacd::VHACDUtil::fattenMesh(const HFMMesh& mesh, const glm::mat4& geometry int indexStartOffset = result.vertices.size(); // new mesh gets the transformed points from the original - glm::mat4 totalTransform = geometryOffset * mesh.modelTransform; + glm::mat4 totalTransform = modelOffset * mesh.modelTransform; for (int i = 0; i < mesh.vertices.size(); i++) { // apply the source mesh's transform to the points glm::vec4 v = totalTransform * glm::vec4(mesh.vertices[i], 1.0f); @@ -288,17 +288,17 @@ float computeDt(uint64_t start) { return (float)(usecTimestampNow() - start) / (float)USECS_PER_SECOND; } -bool vhacd::VHACDUtil::computeVHACD(HFMGeometry& geometry, +bool vhacd::VHACDUtil::computeVHACD(HFMModel& model, VHACD::IVHACD::Parameters params, - HFMGeometry& result, + HFMModel& result, float minimumMeshSize, float maximumMeshSize) { if (_verbose) { - qDebug() << "meshes =" << geometry.meshes.size(); + qDebug() << "meshes =" << model.meshes.size(); } // count the mesh-parts int numParts = 0; - foreach (const HFMMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, model.meshes) { numParts += mesh.parts.size(); } if (_verbose) { @@ -316,7 +316,7 @@ bool vhacd::VHACDUtil::computeVHACD(HFMGeometry& geometry, int meshIndex = 0; int validPartsFound = 0; - foreach (const HFMMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, model.meshes) { // find duplicate points int numDupes = 0; @@ -337,7 +337,7 @@ bool vhacd::VHACDUtil::computeVHACD(HFMGeometry& geometry, // each mesh has its own transform to move it to model-space std::vector vertices; - glm::mat4 totalTransform = geometry.offset * mesh.modelTransform; + glm::mat4 totalTransform = model.offset * mesh.modelTransform; foreach (glm::vec3 vertex, mesh.vertices) { vertices.push_back(glm::vec3(totalTransform * glm::vec4(vertex, 1.0f))); } diff --git a/tools/vhacd-util/src/VHACDUtil.h b/tools/vhacd-util/src/VHACDUtil.h index 64e86ed7df..6ef568d170 100644 --- a/tools/vhacd-util/src/VHACDUtil.h +++ b/tools/vhacd-util/src/VHACDUtil.h @@ -27,13 +27,13 @@ namespace vhacd { public: void setVerbose(bool verbose) { _verbose = verbose; } - bool loadFBX(const QString filename, HFMGeometry& result); + bool loadFBX(const QString filename, HFMModel& result); - void fattenMesh(const HFMMesh& mesh, const glm::mat4& gometryOffset, HFMMesh& result) const; + void fattenMesh(const HFMMesh& mesh, const glm::mat4& modelOffset, HFMMesh& result) const; - bool computeVHACD(HFMGeometry& geometry, + bool computeVHACD(HFMModel& model, VHACD::IVHACD::Parameters params, - HFMGeometry& result, + HFMModel& result, float minimumMeshSize, float maximumMeshSize); void getConvexResults(VHACD::IVHACD* convexifier, HFMMesh& resultMesh) const; diff --git a/tools/vhacd-util/src/VHACDUtilApp.cpp b/tools/vhacd-util/src/VHACDUtilApp.cpp index 0941198234..3d675f8baf 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.cpp +++ b/tools/vhacd-util/src/VHACDUtilApp.cpp @@ -36,7 +36,7 @@ QString formatFloat(double n) { } -bool VHACDUtilApp::writeOBJ(QString outFileName, HFMGeometry& geometry, bool outputCentimeters, int whichMeshPart) { +bool VHACDUtilApp::writeOBJ(QString outFileName, HFMModel& hfmModel, bool outputCentimeters, int whichMeshPart) { QFile file(outFileName); if (!file.open(QIODevice::WriteOnly)) { qWarning() << "unable to write to" << outFileName; @@ -56,7 +56,7 @@ bool VHACDUtilApp::writeOBJ(QString outFileName, HFMGeometry& geometry, bool out int vertexIndexOffset = 0; - foreach (const HFMMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, hfmModel.meshes) { bool verticesHaveBeenOutput = false; foreach (const HFMMeshPart &meshPart, mesh.parts) { if (whichMeshPart >= 0 && nth != (unsigned int) whichMeshPart) { @@ -297,7 +297,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } // load the mesh - HFMGeometry fbx; + HFMModel fbx; auto begin = std::chrono::high_resolution_clock::now(); if (!vUtil.loadFBX(inputFilename, fbx)){ _returnCode = VHACD_RETURN_CODE_FAILURE_TO_READ; @@ -358,7 +358,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } begin = std::chrono::high_resolution_clock::now(); - HFMGeometry result; + HFMModel result; bool success = vUtil.computeVHACD(fbx, params, result, minimumMeshSize, maximumMeshSize); end = std::chrono::high_resolution_clock::now(); @@ -398,7 +398,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } if (fattenFaces) { - HFMGeometry newFbx; + HFMModel newFbx; HFMMesh result; // count the mesh-parts diff --git a/tools/vhacd-util/src/VHACDUtilApp.h b/tools/vhacd-util/src/VHACDUtilApp.h index 3db49456a0..72e8cfb7e4 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.h +++ b/tools/vhacd-util/src/VHACDUtilApp.h @@ -28,7 +28,7 @@ public: VHACDUtilApp(int argc, char* argv[]); ~VHACDUtilApp(); - bool writeOBJ(QString outFileName, HFMGeometry& geometry, bool outputCentimeters, int whichMeshPart = -1); + bool writeOBJ(QString outFileName, HFMModel& model, bool outputCentimeters, int whichMeshPart = -1); int getReturnCode() const { return _returnCode; } From 9ca862ad6bdaa6f6822976362df18b14906ffd60 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Thu, 1 Nov 2018 17:22:47 -0700 Subject: [PATCH 083/125] Case 19373 - Lowering volume on another user in PAL mutes them --- assignment-client/src/audio/AudioMixerClientData.cpp | 2 +- assignment-client/src/audio/AudioMixerClientData.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 7e1420ef60..7f0838d8a5 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -202,7 +202,7 @@ void AudioMixerClientData::parsePerAvatarGainSet(ReceivedMessage& message, const } } -void AudioMixerClientData::setGainForAvatar(QUuid nodeID, uint8_t gain) { +void AudioMixerClientData::setGainForAvatar(QUuid nodeID, float gain) { auto it = std::find_if(_streams.active.cbegin(), _streams.active.cend(), [nodeID](const MixableStream& mixableStream){ return mixableStream.nodeStreamID.nodeID == nodeID && mixableStream.nodeStreamID.streamID.isNull(); }); diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 610b258789..0a66a8164b 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -172,7 +172,7 @@ private: void optionallyReplicatePacket(ReceivedMessage& packet, const Node& node); - void setGainForAvatar(QUuid nodeID, uint8_t gain); + void setGainForAvatar(QUuid nodeID, float gain); bool containsValidPosition(ReceivedMessage& message) const; From 917fc4ad58c5fa368f2d429f86789f7445973827 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 1 Nov 2018 16:50:37 -0700 Subject: [PATCH 084/125] Update language --- .../resources/qml/hifi/commerce/common/CommerceLightbox.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml index 5ddfa98923..5901adc484 100644 --- a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml +++ b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml @@ -33,7 +33,7 @@ Rectangle { property string buttonLayout: "leftright"; readonly property string securityPicBodyText: "When you see your Security Pic, your actions and data are securely making use of your " + - "Wallet's private keys.

You can change your Security Pic in your Wallet."; + "private keys.

You can change your Security Pic via Settings > Security..."; id: root; visible: false; From 2b50c40224dcc3da5e29e5f8f7c2c93220cdef26 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 2 Nov 2018 09:33:06 -0700 Subject: [PATCH 085/125] Minor changes found by Chang --- .../qml/hifi/commerce/checkout/Checkout.qml | 10 ++-- .../InspectionCertificate.qml | 53 ++++++++++--------- scripts/system/marketplaces/marketplaces.js | 2 +- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 95b040eceb..db030d2fee 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -937,9 +937,9 @@ Rectangle { } } - // "Continue Shopping" button + // "Continue" button HifiControlsUit.Button { - id: continueShoppingButton; + id: continueButton; color: hifi.buttons.noneBorderlessGray; colorScheme: hifi.colorSchemes.light; anchors.bottom: parent.bottom; @@ -947,9 +947,9 @@ Rectangle { anchors.right: parent.right; width: 193; height: 44; - text: "Continue Shopping"; + text: "Continue"; onClicked: { - sendToScript({method: 'checkout_continueShopping', itemId: itemId}); + sendToScript({method: 'checkout_continue', itemId: itemId}); } } } @@ -1038,7 +1038,7 @@ Rectangle { width: parent.width/2 - anchors.leftMargin*2; text: "Back to Marketplace"; onClicked: { - sendToScript({method: 'checkout_continueShopping', itemId: itemId}); + sendToScript({method: 'checkout_continue', itemId: itemId}); } } } diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index d24344b40a..734292d774 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -28,7 +28,7 @@ Rectangle { property string itemName: "--"; property string itemOwner: "--"; property string itemEdition: "--"; - property string dateOfPurchase: "--"; + property string dateAcquired: "--"; property string itemCost: "--"; property string certTitleTextColor: hifi.colors.darkGray; property string certTextColor: hifi.colors.white; @@ -64,7 +64,7 @@ Rectangle { root.itemName = ""; root.itemEdition = ""; root.itemOwner = ""; - root.dateOfPurchase = ""; + root.dateAcquired = ""; root.itemCost = ""; errorText.text = "Information about this certificate is currently unavailable. Please try again later."; } @@ -77,8 +77,9 @@ Rectangle { // "\u2022" is the Unicode character 'BULLET' - it's what's used in password fields on the web, etc root.itemOwner = root.isMyCert ? Account.username : "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"; - root.dateOfPurchase = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed"; - root.itemCost = (root.isMyCert && result.data.cost !== undefined) ? result.data.cost : "Undisclosed"; + root.dateAcquired = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed"; + root.itemCost = (root.isMyCert && result.data.cost !== undefined) ? + (parseInt(result.data.cost) > 0 ? result.data.cost : "Free") : "Undisclosed"; } if (root.certInfoReplaceMode > 4) { root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run); @@ -86,7 +87,7 @@ Rectangle { if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED if (root.isMyCert) { - errorText.text = "This item is an uncertified copy of an item you purchased."; + errorText.text = "This item is an uncertified copy of an item you acquired."; } else { errorText.text = "The person who placed this item doesn't own it."; } @@ -102,8 +103,8 @@ Rectangle { showInMarketplaceButton.visible = false; // "Edition" text previously set above in this function // "Owner" text previously set above in this function - // "Purchase Date" text previously set above in this function - // "Purchase Price" text previously set above in this function + // "Date Acquired" text previously set above in this function + // "Original Price" text previously set above in this function if (result.data.invalid_reason) { errorText.text = result.data.invalid_reason; } @@ -117,8 +118,8 @@ Rectangle { showInMarketplaceButton.visible = true; // "Edition" text previously set above in this function // "Owner" text previously set above in this function - // "Purchase Date" text previously set above in this function - // "Purchase Price" text previously set above in this function + // "Date Acquired" text previously set above in this function + // "Original Price" text previously set above in this function errorText.text = "The status of this item is still pending confirmation. If the purchase is not confirmed, " + "this entity will be cleaned up by the domain."; } @@ -145,8 +146,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" // "Edition" text will be set in "onCertificateInfoResult()" // "Owner" text will be set in "onCertificateInfoResult()" - // "Purchase Date" text will be set in "onCertificateInfoResult()" - // "Purchase Price" text will be set in "onCertificateInfoResult()" + // "Date Acquired" text will be set in "onCertificateInfoResult()" + // "Original Price" text will be set in "onCertificateInfoResult()" errorText.text = ""; } else if (root.certificateStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT root.useGoldCert = false; @@ -160,7 +161,7 @@ Rectangle { root.itemName = ""; root.itemEdition = ""; root.itemOwner = ""; - root.dateOfPurchase = ""; + root.dateAcquired = ""; root.itemCost = ""; errorText.text = "Your request to inspect this item timed out. Please try again later."; } else if (root.certificateStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED @@ -175,8 +176,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" // "Edition" text will be set in "onCertificateInfoResult()" // "Owner" text will be set in "onCertificateInfoResult()" - // "Purchase Date" text will be set in "onCertificateInfoResult()" - // "Purchase Price" text will be set in "onCertificateInfoResult()" + // "Date Acquired" text will be set in "onCertificateInfoResult()" + // "Original Price" text will be set in "onCertificateInfoResult()" errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item."; } else if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED root.useGoldCert = false; @@ -190,8 +191,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" root.itemEdition = "Uncertified Copy" // "Owner" text will be set in "onCertificateInfoResult()" - // "Purchase Date" text will be set in "onCertificateInfoResult()" - // "Purchase Price" text will be set in "onCertificateInfoResult()" + // "Date Acquired" text will be set in "onCertificateInfoResult()" + // "Original Price" text will be set in "onCertificateInfoResult()" // "Error Text" text will be set in "onCertificateInfoResult()" } else { console.log("Unknown certificate status received from ledger signal!"); @@ -485,8 +486,8 @@ Rectangle { } RalewayRegular { - id: dateOfPurchaseHeader; - text: "PURCHASE DATE"; + id: dateAcquiredHeader; + text: "DATE ACQUIRED"; // Text size size: 16; // Anchors @@ -500,15 +501,15 @@ Rectangle { color: hifi.colors.darkGray; } AnonymousProRegular { - id: dateOfPurchase; - text: root.dateOfPurchase; + id: dateAcquired; + text: root.dateAcquired; // Text size size: 18; // Anchors - anchors.top: dateOfPurchaseHeader.bottom; + anchors.top: dateAcquiredHeader.bottom; anchors.topMargin: 8; - anchors.left: dateOfPurchaseHeader.left; - anchors.right: dateOfPurchaseHeader.right; + anchors.left: dateAcquiredHeader.left; + anchors.right: dateAcquiredHeader.right; height: paintedHeight; // Style color: root.infoTextColor; @@ -516,7 +517,7 @@ Rectangle { RalewayRegular { id: priceHeader; - text: "PURCHASE PRICE"; + text: "ORIGINAL PRICE"; // Text size size: 16; // Anchors @@ -530,7 +531,7 @@ Rectangle { } HiFiGlyphs { id: hfcGlyph; - visible: priceText.text !== "Undisclosed" && priceText.text !== ""; + visible: priceText.text !== "Undisclosed" && priceText.text !== "" && priceText.text !== "Free"; text: hifi.glyphs.hfc; // Size size: 24; @@ -618,7 +619,7 @@ Rectangle { root.itemName = "--"; root.itemOwner = "--"; root.itemEdition = "--"; - root.dateOfPurchase = "--"; + root.dateAcquired = "--"; root.marketplaceUrl = ""; root.itemCost = "--"; root.isMyCert = false; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 9f55bade6a..01ab9b5639 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -554,7 +554,7 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { case 'checkout_itemLinkClicked': openMarketplace(message.itemId); break; - case 'checkout_continueShopping': + case 'checkout_continue': openMarketplace(); break; case 'checkout_rezClicked': From b33934ec467005517562025e3548a195741a7a31 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Nov 2018 15:43:56 -0700 Subject: [PATCH 086/125] fixing Image3DOverlay scaling --- interface/src/ui/overlays/Image3DOverlay.cpp | 14 ++++++++-- interface/src/ui/overlays/Image3DOverlay.h | 1 + scripts/system/interstitialPage.js | 29 ++++++++++---------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 18da15f019..e24e3b3ed8 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -98,8 +98,8 @@ void Image3DOverlay::render(RenderArgs* args) { } float maxSize = glm::max(fromImage.width(), fromImage.height()); - float x = fromImage.width() / (2.0f * maxSize); - float y = -fromImage.height() / (2.0f * maxSize); + float x = _keepAspectRatio ? fromImage.width() / (2.0f * maxSize) : 0.5f; + float y = _keepAspectRatio ? -fromImage.height() / (2.0f * maxSize) : -0.5f; glm::vec2 topLeft(-x, -y); glm::vec2 bottomRight(x, y); @@ -176,6 +176,11 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) { } } + auto keepAspectRatioValue = properties["keepAspectRatio"]; + if (keepAspectRatioValue.isValid()) { + _keepAspectRatio = keepAspectRatioValue.toBool(); + } + auto emissiveValue = properties["emissive"]; if (emissiveValue.isValid()) { _emissive = emissiveValue.toBool(); @@ -225,6 +230,8 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) { * * @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: scale, size. * + * @property {bool} keepAspectRatio=true - overlays will maintain the aspect ratio when the subImage is applied. + * * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis * parallel to the user's avatar's "up" direction. * @@ -246,6 +253,9 @@ QVariant Image3DOverlay::getProperty(const QString& property) { if (property == "emissive") { return _emissive; } + if (property == "keepAspectRatio") { + return _keepAspectRatio; + } return Billboard3DOverlay::getProperty(property); } diff --git a/interface/src/ui/overlays/Image3DOverlay.h b/interface/src/ui/overlays/Image3DOverlay.h index 1ffa062d45..1000401abb 100644 --- a/interface/src/ui/overlays/Image3DOverlay.h +++ b/interface/src/ui/overlays/Image3DOverlay.h @@ -58,6 +58,7 @@ private: bool _textureIsLoaded { false }; bool _alphaTexture { false }; bool _emissive { false }; + bool _keepAspectRatio { true }; QRect _fromImage; // where from in the image to sample int _geometryId { 0 }; diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index e6e8d3d6d6..e2db032d8c 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -16,10 +16,7 @@ Script.include("/~/system/libraries/Xform.js"); Script.include("/~/system/libraries/globals.js"); var DEBUG = false; - var MIN_LOADING_PROGRESS = 3.6; var TOTAL_LOADING_PROGRESS = 3.7; - var FINAL_Y_DIMENSIONS = 2.8; - var BEGIN_Y_DIMENSIONS = 0.03; var EPSILON = 0.05; var TEXTURE_EPSILON = 0.01; var isVisible = false; @@ -191,14 +188,15 @@ localPosition: { x: 0.0, y: -0.86, z: 0.0 }, url: Script.resourcesPath() + "images/loadingBar_progress.png", alpha: 1, - dimensions: { x: 3.8, y: 2.8 }, + dimensions: { x: TOTAL_LOADING_PROGRESS, y: 0.3}, visible: isVisible, emissive: true, ignoreRayIntersection: false, drawInFront: true, grabbable: false, localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 180.0, z: 0.0 }), - parentID: anchorOverlay + parentID: anchorOverlay, + keepAspectRatio: false }); var loadingBarPlacard = Overlays.addOverlay("image3d", { @@ -259,10 +257,11 @@ target = 0; textureMemSizeStabilityCount = 0; textureMemSizeAtLastCheck = 0; - currentProgress = 0.1; + currentProgress = 0.0; connectionToDomainFailed = false; previousCameraMode = Camera.mode; Camera.mode = "first person"; + updateProgressBar(0.0); timer = Script.setTimeout(update, 2000); } } @@ -373,7 +372,7 @@ } } - var currentProgress = 0.1; + var currentProgress = 0.0; function updateOverlays(physicsEnabled) { @@ -396,7 +395,6 @@ }; var loadingBarProperties = { - dimensions: { x: 2.0, y: 2.8 }, visible: !physicsEnabled }; @@ -458,19 +456,22 @@ function updateProgressBar(progress) { var progressPercentage = progress / TOTAL_LOADING_PROGRESS; var subImageWidth = progressPercentage * LOADING_IMAGE_WIDTH_PIXELS; - var subImageWidthPercentage = subImageWidth / LOADING_IMAGE_WIDTH_PIXELS; + var start = TOTAL_LOADING_PROGRESS / 2; + var end = 0; + var xLocalPosition = (progressPercentage * (end - start)) + start; var properties = { - localPosition: { x: (TOTAL_LOADING_PROGRESS / 2) - (currentProgress / 2), y: -0.86, z: 0.0 }, + localPosition: { x: xLocalPosition, y: -0.93, z: 0.0 }, dimensions: { - x: currentProgress, - y: (subImageWidthPercentage * (FINAL_Y_DIMENSIONS - BEGIN_Y_DIMENSIONS)) + BEGIN_Y_DIMENSIONS + x: progress, + y: 0.3 }, + localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 180.0, z: 0.0 }), subImage: { x: 0.0, y: 0.0, width: subImageWidth, - height: 90 + height: 128 } }; @@ -479,7 +480,7 @@ var MAX_TEXTURE_STABILITY_COUNT = 30; var INTERVAL_PROGRESS = 0.04; - var INTERVAL_PROGRESS_PHYSICS_ENABLED = 0.2; + var INTERVAL_PROGRESS_PHYSICS_ENABLED = 0.09; function update() { var renderStats = Render.getConfig("Stats"); var physicsEnabled = Window.isPhysicsEnabled(); From 402c23e359f0a9cc6f4a12e1cc54c10e90d9635e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 2 Nov 2018 16:07:29 -0700 Subject: [PATCH 087/125] Use useragent instead of queryparam to determine limitedCommerce --- interface/src/Application.cpp | 3 --- interface/src/scripting/WalletScriptingInterface.cpp | 9 ++++++++- interface/src/scripting/WalletScriptingInterface.h | 3 +-- interface/src/ui/overlays/ContextOverlayInterface.cpp | 7 +------ libraries/ui/src/ui/types/RequestFilters.cpp | 9 +++------ scripts/system/avatarapp.js | 2 +- scripts/system/commerce/wallet.js | 3 +-- scripts/system/edit.js | 2 +- scripts/system/html/js/marketplacesInject.js | 6 +++--- scripts/system/marketplaces/marketplace.js | 2 +- scripts/system/marketplaces/marketplaces.js | 5 +---- 11 files changed, 21 insertions(+), 30 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 70c25ce823..ff1822013b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -7881,9 +7881,6 @@ void Application::loadAvatarBrowser() const { auto tablet = dynamic_cast(DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")); // construct the url to the marketplace item QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars"; - if (DependencyManager::get()->getLimitedCommerce()) { - url += "&isFree=1"; - } QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js"; tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH); diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 77ca232712..386cb4cba6 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -10,6 +10,7 @@ // #include "WalletScriptingInterface.h" +#include CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) { Q_ASSERT(QThread::currentThread() == qApp->thread()); @@ -38,4 +39,10 @@ void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUui } else { qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is not an avatar entity"; } -} \ No newline at end of file +} + +void WalletScriptingInterface::setLimitedCommerce(bool isLimited) { + _limitedCommerce = isLimited; + Setting::Handle limitedCommerceSetting{ "limitedCommerce", false }; + limitedCommerceSetting.set(_limitedCommerce); +} diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 8aebb68a47..950fc9262a 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -15,7 +15,6 @@ #include #include -#include "scripting/HMDScriptingInterface.h" #include #include #include @@ -68,7 +67,7 @@ public: void setWalletStatus(const uint& status); bool getLimitedCommerce() { return _limitedCommerce; } - void setLimitedCommerce(bool isLimited) { _limitedCommerce = isLimited; } + void setLimitedCommerce(bool isLimited); signals: diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 9f1018f6c7..11c268dd48 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -223,12 +223,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - Setting::Handle _settingSwitch{ "commerce", true }; - if (_settingSwitch.get()) { - return (entityProperties.getCertificateID().length() != 0); - } else { - return (entityProperties.getMarketplaceID().length() != 0); - } + return (entityProperties.getCertificateID().length() != 0); } bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { diff --git a/libraries/ui/src/ui/types/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp index 7f192d6e52..0e08874251 100644 --- a/libraries/ui/src/ui/types/RequestFilters.cpp +++ b/libraries/ui/src/ui/types/RequestFilters.cpp @@ -84,13 +84,10 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, static const QString USER_AGENT = "User-Agent"; const QString tokenStringMobile{ "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36" }; const QString tokenStringMetaverse{ "Chrome/48.0 (HighFidelityInterface)" }; + const QString tokenStringLimitedCommerce{ "Chrome/48.0 (HighFidelityInterface limitedCommerce)" }; + Setting::Handle limitedCommerceSetting{ "limitedCommerce", false }; - // During the period in which we have HFC commerce in the system, but not applied everywhere: - const QString tokenStringCommerce{ "Chrome/48.0 (HighFidelityInterface WithHFC)" }; - Setting::Handle _settingSwitch{ "commerce", true }; - bool isMoney = _settingSwitch.get(); - - const QString tokenString = !isAuthable ? tokenStringMobile : (isMoney ? tokenStringCommerce : tokenStringMetaverse); + const QString tokenString = !isAuthable ? tokenStringMobile : (limitedCommerceSetting.get() ? tokenStringLimitedCommerce : tokenStringMetaverse); info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit()); } diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 2f05b1b337..44f10396ca 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -160,7 +160,7 @@ var selectedAvatarEntityID = null; var grabbedAvatarEntityChangeNotifier = null; var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; -var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace" + (WalletScriptingInterface.limitedCommerce ? "?isFree=1" : ""); +var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("html/js/marketplacesInject.js"); function fromQml(message) { // messages are {method, params}, like json-rpc. See also sendToQml. diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index bc7bccd94f..fd230de0e4 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -18,8 +18,7 @@ Script.include("/~/system/libraries/accountUtils.js"); Script.include("/~/system/libraries/connectionUtils.js"); var AppUi = Script.require('appUi'); -var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace" + - (WalletScriptingInterface.limitedCommerce ? "?isFree=1" : ""); + var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace";; // BEGIN AVATAR SELECTOR LOGIC var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; diff --git a/scripts/system/edit.js b/scripts/system/edit.js index ee1f5ecbec..6425806771 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -193,7 +193,7 @@ var importingSVOTextOverlay = Overlays.addOverlay("text", { visible: false }); -var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace" + (WalletScriptingInterface.limitedCommerce ? "?isFree=1" : ""); +var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; var marketplaceWindow = new OverlayWebWindow({ title: 'Marketplace', source: "about:blank", diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index db433c5058..5cae42e33e 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -60,7 +60,7 @@ ); // Footer. - var isInitialHiFiPage = location.href === (marketplaceBaseURL + "/marketplace" + (limitedCommerce ? "?isFree=1" : "?")); + var isInitialHiFiPage = location.href === (marketplaceBaseURL + "/marketplace?"); $("body").append( '
' + (!isInitialHiFiPage ? '' : '') + @@ -72,7 +72,7 @@ // Footer actions. $("#back-button").on("click", function () { - (document.referrer !== "") ? window.history.back() : window.location = (marketplaceBaseURL + "/marketplace?") + (limitedCommerce ? "isFree=1" : ""); + (document.referrer !== "") ? window.history.back() : window.location = (marketplaceBaseURL + "/marketplace?"); }); $("#all-markets").on("click", function () { EventBridge.emitWebEvent(JSON.stringify({ @@ -93,7 +93,7 @@ window.location = "https://clara.io/library?gameCheck=true&public=true"; }); $('#exploreHifiMarketplace').on('click', function () { - window.location = marketplaceBaseURL + "/marketplace" + (limitedCommerce ? "?isFree=1" : "?"); + window.location = marketplaceBaseURL + "/marketplace?"; }); } diff --git a/scripts/system/marketplaces/marketplace.js b/scripts/system/marketplaces/marketplace.js index 2f749656d3..d90695c767 100644 --- a/scripts/system/marketplaces/marketplace.js +++ b/scripts/system/marketplaces/marketplace.js @@ -15,7 +15,7 @@ Script.include("../libraries/WebTablet.js"); var toolIconUrl = Script.resolvePath("../assets/images/tools/"); -var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace" + (WalletScriptingInterface.limitedCommerce ? "?isFree=1" : ""); +var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; var marketplaceWindow = new OverlayWebWindow({ title: "Marketplace", source: "about:blank", diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 01ab9b5639..ed8ed9f288 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -165,9 +165,6 @@ function openMarketplace(optionalItemOrUrl) { if (optionalItemOrUrl && optionalItemOrUrl.indexOf(METAVERSE_SERVER_URL) === -1) { url = MARKETPLACE_URL + '/items/' + optionalItemOrUrl; } - if (WalletScriptingInterface.limitedCommerce) { - url += "?isFree=1"; - } ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL); } @@ -748,7 +745,7 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { var BUTTON_NAME = "MARKET"; -var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace" + (WalletScriptingInterface.limitedCommerce ? "?isFree=1" : ""); +var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace"; // Append "?" if necessary to signal injected script that it's the initial page. var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + (MARKETPLACE_URL.indexOf("?") > -1 ? "" : "?"); var ui; From c22df4dd947134fb7bd807cd3d843d935bacbb39 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Nov 2018 16:20:35 -0700 Subject: [PATCH 088/125] Fix horizontal and vertical props in entity properties being swapped --- scripts/system/html/js/entityProperties.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 78de0d075a..1ce4626b8f 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -902,13 +902,13 @@ const GROUPS = [ { label: "Horizontal Angle Start", type: "slider", - min: -180, - max: 0, + min: 0, + max: 180, step: 1, decimals: 0, multiplier: DEGREES_TO_RADIANS, unit: "deg", - propertyID: "azimuthStart", + propertyID: "polarStart", }, { label: "Horizontal Angle Finish", @@ -919,18 +919,18 @@ const GROUPS = [ decimals: 0, multiplier: DEGREES_TO_RADIANS, unit: "deg", - propertyID: "azimuthFinish", + propertyID: "polarFinish", }, { label: "Vertical Angle Start", type: "slider", - min: 0, - max: 180, + min: -180, + max: 0, step: 1, decimals: 0, multiplier: DEGREES_TO_RADIANS, unit: "deg", - propertyID: "polarStart", + propertyID: "azimuthStart", }, { label: "Vertical Angle Finish", @@ -941,7 +941,7 @@ const GROUPS = [ decimals: 0, multiplier: DEGREES_TO_RADIANS, unit: "deg", - propertyID: "polarFinish", + propertyID: "azimuthFinish", }, ] }, From d0854ca2ab1219977068a55bb83558a1b0ed546c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 2 Nov 2018 16:42:18 -0700 Subject: [PATCH 089/125] Use AccountManager instead of settings --- interface/src/scripting/WalletScriptingInterface.cpp | 11 +++++------ interface/src/scripting/WalletScriptingInterface.h | 8 +++++--- libraries/networking/src/AccountManager.cpp | 4 ++++ libraries/networking/src/AccountManager.h | 6 ++++++ libraries/ui/src/ui/types/RequestFilters.cpp | 5 ++--- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 386cb4cba6..93ee60ba5b 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -16,6 +16,11 @@ CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(q Q_ASSERT(QThread::currentThread() == qApp->thread()); } +WalletScriptingInterface::WalletScriptingInterface() { + connect(DependencyManager::get().data(), + &AccountManager::limitedCommerceChanged, this, &WalletScriptingInterface::limitedCommerceChanged); +} + void WalletScriptingInterface::refreshWalletStatus() { auto wallet = DependencyManager::get(); wallet->getWalletStatus(); @@ -40,9 +45,3 @@ void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUui qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is not an avatar entity"; } } - -void WalletScriptingInterface::setLimitedCommerce(bool isLimited) { - _limitedCommerce = isLimited; - Setting::Handle limitedCommerceSetting{ "limitedCommerce", false }; - limitedCommerceSetting.set(_limitedCommerce); -} diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 950fc9262a..36ee021b29 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -21,6 +21,7 @@ #include "Application.h" #include "commerce/Wallet.h" #include "ui/overlays/ContextOverlayInterface.h" +#include class CheckoutProxy : public QmlWrapper { Q_OBJECT @@ -45,6 +46,8 @@ class WalletScriptingInterface : public QObject, public Dependency { public: + WalletScriptingInterface(); + /**jsdoc * @function WalletScriptingInterface.refreshWalletStatus */ @@ -66,8 +69,8 @@ public: // scripts could cause the Wallet to incorrectly report its status. void setWalletStatus(const uint& status); - bool getLimitedCommerce() { return _limitedCommerce; } - void setLimitedCommerce(bool isLimited); + bool getLimitedCommerce() { return DependencyManager::get()->getLimitedCommerce(); } + void setLimitedCommerce(bool isLimited) { DependencyManager::get()->setLimitedCommerce(isLimited); }; signals: @@ -105,7 +108,6 @@ signals: private: uint _walletStatus; - bool _limitedCommerce = false; }; #endif // hifi_WalletScriptingInterface_h diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index d99c0020da..2d1a0c0afb 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -837,3 +837,7 @@ void AccountManager::handleKeypairGenerationError() { // reset our waiting state for keypair response _isWaitingForKeypairResponse = false; } + +void AccountManager::setLimitedCommerce(bool isLimited) { + _limitedCommerce = isLimited; +} diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index f3b81cf1c9..093ac9a27c 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -98,6 +98,9 @@ public: void removeAccountFromFile(); + bool getLimitedCommerce() { return _limitedCommerce; } + void setLimitedCommerce(bool isLimited); + public slots: void requestAccessToken(const QString& login, const QString& password); void requestAccessTokenWithSteam(QByteArray authSessionTicket); @@ -121,6 +124,7 @@ signals: void loginFailed(); void logoutComplete(); void newKeypair(); + void limitedCommerceChanged(); private slots: void handleKeypairGenerationError(); @@ -150,6 +154,8 @@ private: QByteArray _pendingPrivateKey; QUuid _sessionID { QUuid::createUuid() }; + + bool _limitedCommerce { false }; }; #endif // hifi_AccountManager_h diff --git a/libraries/ui/src/ui/types/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp index 0e08874251..a3b3b7dc57 100644 --- a/libraries/ui/src/ui/types/RequestFilters.cpp +++ b/libraries/ui/src/ui/types/RequestFilters.cpp @@ -70,9 +70,9 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, // check if this is a request to a highfidelity URL bool isAuthable = isAuthableHighFidelityURL(info.requestUrl()); + auto accountManager = DependencyManager::get(); if (isAuthable) { // if we have an access token, add it to the right HTTP header for authorization - auto accountManager = DependencyManager::get(); if (accountManager->hasValidAccessToken()) { static const QString OAUTH_AUTHORIZATION_HEADER = "Authorization"; @@ -85,9 +85,8 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, const QString tokenStringMobile{ "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36" }; const QString tokenStringMetaverse{ "Chrome/48.0 (HighFidelityInterface)" }; const QString tokenStringLimitedCommerce{ "Chrome/48.0 (HighFidelityInterface limitedCommerce)" }; - Setting::Handle limitedCommerceSetting{ "limitedCommerce", false }; - const QString tokenString = !isAuthable ? tokenStringMobile : (limitedCommerceSetting.get() ? tokenStringLimitedCommerce : tokenStringMetaverse); + const QString tokenString = !isAuthable ? tokenStringMobile : (accountManager->getLimitedCommerce() ? tokenStringLimitedCommerce : tokenStringMetaverse); info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit()); } From ae51b95ca13f9d3f66a28f565d42c903dfc69276 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Sun, 4 Nov 2018 11:15:46 -0800 Subject: [PATCH 090/125] fix bad tab reference --- interface/resources/qml/hifi/commerce/wallet/Wallet.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index d882ef8d38..97736bf781 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -630,7 +630,7 @@ Rectangle { visible: !walletSetup.visible; color: root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; - anchors.left: securityButtonContainer.right; + anchors.left: sendMoneyButtonContainer.right; anchors.bottom: parent.bottom; width: parent.width / tabButtonsContainer.numTabs; From 8ad8398ef3d04f6f9b9f200f53227ea4cb3491d6 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Sun, 4 Nov 2018 11:17:56 -0800 Subject: [PATCH 091/125] remove 'purchase' from login notice. --- scripts/system/html/js/marketplacesInject.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 5cae42e33e..c4a4adebdf 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -170,7 +170,7 @@ var span = document.createElement('span'); span.style = "margin:10px;color:#1b6420;font-size:15px;"; - span.innerHTML = "to purchase items from the Marketplace."; + span.innerHTML = "to get items from the Marketplace."; var xButton = document.createElement('a'); xButton.id = "xButton"; From 100a17a2b9ee8293ca10a30747970ecfc096c1f1 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Sun, 4 Nov 2018 11:25:40 -0800 Subject: [PATCH 092/125] remove wallet reference in update notification --- scripts/system/commerce/wallet.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 641112b6b4..1fbf952dc6 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -582,7 +582,7 @@ function notificationPollCallbackUpdates(updatesArray) { if (!ui.notificationInitialCallbackMade[0]) { message = updatesArray.length + " of your purchased items " + (updatesArray.length === 1 ? "has an update " : "have updates ") + - "available. Open WALLET to update."; + "available. Open ASSETS to update."; ui.notificationDisplayBanner(message); ui.notificationPollCaresAboutSince[0] = true; @@ -590,7 +590,7 @@ function notificationPollCallbackUpdates(updatesArray) { for (var i = 0; i < updatesArray.length; i++) { message = "Update available for \"" + updatesArray[i].base_item_title + "\"." + - "Open WALLET to update."; + "Open ASSETS to update."; ui.notificationDisplayBanner(message); } } From 8906b909eb715fba8e20b1f941a5e9c4f2a7cee6 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Sun, 4 Nov 2018 12:08:29 -0800 Subject: [PATCH 093/125] close wallet app during login so that it doesn't tell to login after login --- scripts/system/commerce/wallet.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 1fbf952dc6..837cf6a78a 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -437,6 +437,7 @@ function fromQml(message) { } break; case 'needsLogIn_loginClicked': + ui.close(); openLoginWindow(); break; case 'disableHmdPreview': From 7115f0e6886783e9aab0d4342695c858218cacdc Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Sun, 4 Nov 2018 12:17:41 -0800 Subject: [PATCH 094/125] update notification refs inventory, not assets --- scripts/system/commerce/wallet.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 8b6fdbd1b1..4fb7ddd40b 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -583,7 +583,7 @@ function notificationPollCallbackUpdates(updatesArray) { if (!ui.notificationInitialCallbackMade[0]) { message = updatesArray.length + " of your purchased items " + (updatesArray.length === 1 ? "has an update " : "have updates ") + - "available. Open ASSETS to update."; + "available. Open INVENTORY to update."; ui.notificationDisplayBanner(message); ui.notificationPollCaresAboutSince[0] = true; @@ -591,7 +591,7 @@ function notificationPollCallbackUpdates(updatesArray) { for (var i = 0; i < updatesArray.length; i++) { message = "Update available for \"" + updatesArray[i].base_item_title + "\"." + - "Open ASSETS to update."; + "Open INVENTORY to update."; ui.notificationDisplayBanner(message); } } From c6cde2d412833ea0e6bdaf06c54c608d8572843a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 Nov 2018 11:13:32 -0800 Subject: [PATCH 095/125] don't assign null Shape to RigidBody already in physics simulation --- libraries/physics/src/ObjectMotionState.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index c814140930..acfb0c9236 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -305,15 +305,16 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* } return true; } - } - if (_shape == newShape) { - // the shape didn't actually change, so we clear the DIRTY_SHAPE flag - flags &= ~Simulation::DIRTY_SHAPE; - // and clear the reference we just created - getShapeManager()->releaseShape(_shape); } else { - _body->setCollisionShape(const_cast(newShape)); - setShape(newShape); + if (_shape == newShape) { + // the shape didn't actually change, so we clear the DIRTY_SHAPE flag + flags &= ~Simulation::DIRTY_SHAPE; + // and clear the reference we just created + getShapeManager()->releaseShape(_shape); + } else { + _body->setCollisionShape(const_cast(newShape)); + setShape(newShape); + } } } if (flags & EASY_DIRTY_PHYSICS_FLAGS) { From 9e7b68feadd572f863bf1e03e5a8e1804b1ec11b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 Nov 2018 11:14:32 -0800 Subject: [PATCH 096/125] find and remove dangling pointers from _activeStaticBodies on remove --- libraries/physics/src/PhysicsEngine.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 8343919ae1..9e75dec475 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -289,6 +289,12 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) bumpAndPruneContacts(object); btRigidBody* body = object->getRigidBody(); if (body) { + if (_activeStaticBodies.size() > 0) { + std::set::iterator itr = _activeStaticBodies.find(body); + if (itr != _activeStaticBodies.end()) { + _activeStaticBodies.erase(itr); + } + } removeDynamicsForBody(body); _dynamicsWorld->removeRigidBody(body); From 228847b5075b2d0ff1997bbc87f3eaa86f752ec1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 5 Nov 2018 11:44:58 -0800 Subject: [PATCH 097/125] only look in _activeStaticBodies when body is static --- libraries/physics/src/PhysicsEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 9e75dec475..bf6e2463e5 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -289,7 +289,7 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) bumpAndPruneContacts(object); btRigidBody* body = object->getRigidBody(); if (body) { - if (_activeStaticBodies.size() > 0) { + if (body->isStaticObject() && _activeStaticBodies.size() > 0) { std::set::iterator itr = _activeStaticBodies.find(body); if (itr != _activeStaticBodies.end()) { _activeStaticBodies.erase(itr); From 30eb360f626c0b3e91fb5fd5c22e708c4093bc80 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 5 Nov 2018 12:07:17 -0800 Subject: [PATCH 098/125] Convert remaining HFMModels called model to hfmModel, and rename some missed local variables --- interface/src/ModelPackager.cpp | 34 ++--- interface/src/ModelPackager.h | 4 +- interface/src/ModelPropertiesDialog.cpp | 20 +-- interface/src/ModelPropertiesDialog.h | 4 +- libraries/animation/src/AnimClip.cpp | 10 +- libraries/animation/src/AnimSkeleton.cpp | 6 +- libraries/animation/src/AnimSkeleton.h | 2 +- .../src/avatars-renderer/SkeletonModel.cpp | 6 +- libraries/baking/src/FBXBaker.cpp | 6 +- libraries/baking/src/FBXBaker.h | 2 +- libraries/baking/src/OBJBaker.cpp | 20 +-- libraries/baking/src/OBJBaker.h | 4 +- libraries/fbx/src/FBXReader.cpp | 138 +++++++++--------- libraries/fbx/src/GLTFReader.cpp | 42 +++--- libraries/fbx/src/GLTFReader.h | 4 +- libraries/fbx/src/OBJReader.cpp | 46 +++--- libraries/fbx/src/OBJReader.h | 2 +- tools/vhacd-util/src/VHACDUtil.cpp | 22 +-- tools/vhacd-util/src/VHACDUtil.h | 2 +- tools/vhacd-util/src/VHACDUtilApp.h | 2 +- 20 files changed, 188 insertions(+), 188 deletions(-) diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index f3a69bbad2..21d3477d7e 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -109,10 +109,10 @@ bool ModelPackager::loadModel() { qCDebug(interfaceapp) << "Reading FBX file : " << _fbxInfo.filePath(); QByteArray fbxContents = fbx.readAll(); - _model.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath())); + _hfmModel.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath())); // make sure we have some basic mappings - populateBasicMapping(_mapping, _fbxInfo.filePath(), *_model); + populateBasicMapping(_mapping, _fbxInfo.filePath(), *_hfmModel); } catch (const QString& error) { qCDebug(interfaceapp) << "Error reading " << _fbxInfo.filePath() << ": " << error; return false; @@ -122,7 +122,7 @@ bool ModelPackager::loadModel() { bool ModelPackager::editProperties() { // open the dialog to configure the rest - ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), *_model); + ModelPropertiesDialog properties(_modelType, _mapping, _modelFile.path(), *_hfmModel); if (properties.exec() == QDialog::Rejected) { return false; } @@ -235,18 +235,18 @@ bool ModelPackager::zipModel() { return true; } -void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& model) { +void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& hfmModel) { bool isBodyType = _modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL; // mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file - bool likelyMixamoFile = model.applicationName == "mixamo.com" || - (model.blendshapeChannelNames.contains("BrowsDown_Right") && - model.blendshapeChannelNames.contains("MouthOpen") && - model.blendshapeChannelNames.contains("Blink_Left") && - model.blendshapeChannelNames.contains("Blink_Right") && - model.blendshapeChannelNames.contains("Squint_Right")); + bool likelyMixamoFile = hfmModel.applicationName == "mixamo.com" || + (hfmModel.blendshapeChannelNames.contains("BrowsDown_Right") && + hfmModel.blendshapeChannelNames.contains("MouthOpen") && + hfmModel.blendshapeChannelNames.contains("Blink_Left") && + hfmModel.blendshapeChannelNames.contains("Blink_Right") && + hfmModel.blendshapeChannelNames.contains("Squint_Right")); if (!mapping.contains(NAME_FIELD)) { mapping.insert(NAME_FIELD, QFileInfo(filename).baseName()); @@ -268,15 +268,15 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename } QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); if (!joints.contains("jointEyeLeft")) { - joints.insert("jointEyeLeft", model.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" : - (model.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye")); + joints.insert("jointEyeLeft", hfmModel.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" : + (hfmModel.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye")); } if (!joints.contains("jointEyeRight")) { - joints.insert("jointEyeRight", model.jointIndices.contains("jointEyeRight") ? "jointEyeRight" : - model.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye"); + joints.insert("jointEyeRight", hfmModel.jointIndices.contains("jointEyeRight") ? "jointEyeRight" : + hfmModel.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye"); } if (!joints.contains("jointNeck")) { - joints.insert("jointNeck", model.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck"); + joints.insert("jointNeck", hfmModel.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck"); } if (isBodyType) { @@ -296,7 +296,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename if (!joints.contains("jointHead")) { const char* topName = likelyMixamoFile ? "HeadTop_End" : "HeadEnd"; - joints.insert("jointHead", model.jointIndices.contains(topName) ? topName : "Head"); + joints.insert("jointHead", hfmModel.jointIndices.contains(topName) ? topName : "Head"); } mapping.insert(JOINT_FIELD, joints); @@ -370,7 +370,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename void ModelPackager::listTextures() { _textures.clear(); - foreach (const HFMMaterial mat, _model->materials) { + foreach (const HFMMaterial mat, _hfmModel->materials) { if (!mat.albedoTexture.filename.isEmpty() && mat.albedoTexture.content.isEmpty() && !_textures.contains(mat.albedoTexture.filename)) { _textures << mat.albedoTexture.filename; diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index 2ec629a363..ed86f15008 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -32,7 +32,7 @@ private: bool editProperties(); bool zipModel(); - void populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& model); + void populateBasicMapping(QVariantHash& mapping, QString filename, const HFMModel& hfmModel); void listTextures(); bool copyTextures(const QString& oldDir, const QDir& newDir); @@ -44,7 +44,7 @@ private: QString _scriptDir; QVariantHash _mapping; - std::unique_ptr _model; + std::unique_ptr _hfmModel; QStringList _textures; QStringList _scripts; }; diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index 0b1638a5bc..49c57744a9 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -27,11 +27,11 @@ ModelPropertiesDialog::ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const HFMModel& model) : + const QString& basePath, const HFMModel& hfmModel) : _modelType(modelType), _originalMapping(originalMapping), _basePath(basePath), -_model(model) +_hfmModel(hfmModel) { setWindowTitle("Set Model Properties"); @@ -108,8 +108,8 @@ QVariantHash ModelPropertiesDialog::getMapping() const { // update the joint indices QVariantHash jointIndices; - for (int i = 0; i < _model.joints.size(); i++) { - jointIndices.insert(_model.joints.at(i).name, QString::number(i)); + for (int i = 0; i < _hfmModel.joints.size(); i++) { + jointIndices.insert(_hfmModel.joints.at(i).name, QString::number(i)); } mapping.insert(JOINT_INDEX_FIELD, jointIndices); @@ -118,10 +118,10 @@ QVariantHash ModelPropertiesDialog::getMapping() const { if (_modelType == FSTReader::ATTACHMENT_MODEL) { glm::vec3 pivot; if (_pivotAboutCenter->isChecked()) { - pivot = (_model.meshExtents.minimum + _model.meshExtents.maximum) * 0.5f; + pivot = (_hfmModel.meshExtents.minimum + _hfmModel.meshExtents.maximum) * 0.5f; } else if (_pivotJoint->currentIndex() != 0) { - pivot = extractTranslation(_model.joints.at(_pivotJoint->currentIndex() - 1).transform); + pivot = extractTranslation(_hfmModel.joints.at(_pivotJoint->currentIndex() - 1).transform); } mapping.insert(TRANSLATION_X_FIELD, -pivot.x * (float)_scale->value() + (float)_translationX->value()); mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * (float)_scale->value() + (float)_translationY->value()); @@ -191,7 +191,7 @@ void ModelPropertiesDialog::reset() { } foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) { QString jointName = joint.toString(); - if (_model.jointIndices.contains(jointName)) { + if (_hfmModel.jointIndices.contains(jointName)) { createNewFreeJoint(jointName); } } @@ -249,8 +249,8 @@ QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const { if (withNone) { box->addItem("(none)"); } - foreach (const HFMJoint& joint, _model.joints) { - if (joint.isSkeletonJoint || !_model.hasSkeletonJoints) { + foreach (const HFMJoint& joint, _hfmModel.joints) { + if (joint.isSkeletonJoint || !_hfmModel.hasSkeletonJoints) { box->addItem(joint.name); } } @@ -266,7 +266,7 @@ QDoubleSpinBox* ModelPropertiesDialog::createTranslationBox() const { } void ModelPropertiesDialog::insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const { - if (_model.jointIndices.contains(name)) { + if (_hfmModel.jointIndices.contains(name)) { joints.insert(joint, name); } else { joints.remove(joint); diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h index b979768a2d..0bf8075197 100644 --- a/interface/src/ModelPropertiesDialog.h +++ b/interface/src/ModelPropertiesDialog.h @@ -30,7 +30,7 @@ class ModelPropertiesDialog : public QDialog { public: ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const HFMModel& model); + const QString& basePath, const HFMModel& hfmModel); QVariantHash getMapping() const; @@ -50,7 +50,7 @@ private: FSTReader::ModelType _modelType; QVariantHash _originalMapping; QString _basePath; - HFMModel _model; + HFMModel _hfmModel; QLineEdit* _name = nullptr; QPushButton* _textureDirectory = nullptr; QPushButton* _scriptDirectory = nullptr; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 7380ca8a13..eeac8fadce 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -101,8 +101,8 @@ void AnimClip::copyFromNetworkAnim() { // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. - const HFMModel& model = _networkAnim->getHFMModel(); - AnimSkeleton animSkeleton(model); + const HFMModel& hfmModel = _networkAnim->getHFMModel(); + AnimSkeleton animSkeleton(hfmModel); const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); std::vector jointMap; @@ -115,12 +115,12 @@ void AnimClip::copyFromNetworkAnim() { jointMap.push_back(skeletonJoint); } - const int frameCount = model.animationFrames.size(); + const int frameCount = hfmModel.animationFrames.size(); _anim.resize(frameCount); for (int frame = 0; frame < frameCount; frame++) { - const HFMAnimationFrame& hfmAnimFrame = model.animationFrames[frame]; + const HFMAnimationFrame& hfmAnimFrame = hfmModel.animationFrames[frame]; // init all joints in animation to default pose // this will give us a resonable result for bones in the model skeleton but not in the animation. @@ -150,7 +150,7 @@ void AnimClip::copyFromNetworkAnim() { // adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. - const glm::vec3& hfmZeroTrans = model.animationFrames[0].translations[animJoint]; + const glm::vec3& hfmZeroTrans = hfmModel.animationFrames[0].translations[animJoint]; const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); float boneLengthScale = 1.0f; const float EPSILON = 0.0001f; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index bc179739a0..73a0891fbe 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -16,11 +16,11 @@ #include "AnimationLogging.h" -AnimSkeleton::AnimSkeleton(const HFMModel& model) { +AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { // convert to std::vector of joints std::vector joints; - joints.reserve(model.joints.size()); - for (auto& joint : model.joints) { + joints.reserve(hfmModel.joints.size()); + for (auto& joint : hfmModel.joints) { joints.push_back(joint); } buildSkeletonFromJoints(joints); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 80353523d1..3a384388d0 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -23,7 +23,7 @@ public: using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; - explicit AnimSkeleton(const HFMModel& model); + explicit AnimSkeleton(const HFMModel& hfmModel); explicit AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; const QString& getJointName(int jointIndex) const; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 13ee5854bf..36e37dd3d4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -330,15 +330,15 @@ void SkeletonModel::computeBoundingShape() { return; } - const HFMModel& model = getHFMModel(); - if (model.joints.isEmpty() || model.rootJointIndex == -1) { + const HFMModel& hfmModel = getHFMModel(); + if (hfmModel.joints.isEmpty() || hfmModel.rootJointIndex == -1) { // rootJointIndex == -1 if the avatar model has no skeleton return; } float radius, height; glm::vec3 offset; - _rig.computeAvatarBoundingCapsule(model, radius, height, offset); + _rig.computeAvatarBoundingCapsule(hfmModel, radius, height, offset); float invScale = 1.0f / _owningAvatar->getModelScale(); _boundingCapsuleRadius = invScale * radius; _boundingCapsuleHeight = invScale * height; diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 9898651268..cef6c9b900 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -206,7 +206,7 @@ void FBXBaker::importScene() { } #endif - _model = reader.extractHFMModel({}, _modelURL.toString()); + _hfmModel = reader.extractHFMModel({}, _modelURL.toString()); _textureContentMap = reader._textureContent; } @@ -231,7 +231,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { for (FBXNode& objectChild : rootChild.children) { if (objectChild.name == "Geometry") { - // TODO Pull this out of _model instead so we don't have to reprocess it + // TODO Pull this out of _hfmModel instead so we don't have to reprocess it auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex, false); // Callback to get MaterialID @@ -293,7 +293,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { QHash textureTypes; // enumerate the materials in the extracted geometry so we can determine the texture type for each texture ID - for (const auto& material : _model->materials) { + for (const auto& material : _hfmModel->materials) { if (material.normalTexture.isBumpmap) { textureTypes[material.normalTexture.id] = BUMP_TEXTURE; } else { diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 8dfde12b31..2af51b2190 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -53,7 +53,7 @@ private: void rewriteAndBakeSceneModels(); void rewriteAndBakeSceneTextures(); - HFMModel* _model; + HFMModel* _hfmModel; QHash _textureNameMatchCount; QHash _remappedTexturePaths; diff --git a/libraries/baking/src/OBJBaker.cpp b/libraries/baking/src/OBJBaker.cpp index 67bd2d5d62..d9f56b393e 100644 --- a/libraries/baking/src/OBJBaker.cpp +++ b/libraries/baking/src/OBJBaker.cpp @@ -153,7 +153,7 @@ void OBJBaker::bakeOBJ() { checkIfTexturesFinished(); } -void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMModel& model) { +void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMModel& hfmModel) { // Generating FBX Header Node FBXNode headerNode; headerNode.name = FBX_HEADER_EXTENSION; @@ -199,7 +199,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMModel& model) { // Compress the mesh information and store in dracoNode bool hasDeformers = false; // No concept of deformers for an OBJ FBXNode dracoNode; - compressMesh(model.meshes[0], hasDeformers, dracoNode); + compressMesh(hfmModel.meshes[0], hasDeformers, dracoNode); geometryNode.children.append(dracoNode); // Generating Object node's child - Model node @@ -214,17 +214,17 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMModel& model) { objectNode.children = { geometryNode, modelNode }; // Generating Objects node's child - Material node - auto& meshParts = model.meshes[0].parts; + auto& meshParts = hfmModel.meshes[0].parts; for (auto& meshPart : meshParts) { FBXNode materialNode; materialNode.name = MATERIAL_NODE_NAME; - if (model.materials.size() == 1) { + if (hfmModel.materials.size() == 1) { // case when no material information is provided, OBJReader considers it as a single default material - for (auto& materialID : model.materials.keys()) { - setMaterialNodeProperties(materialNode, materialID, model); + for (auto& materialID : hfmModel.materials.keys()) { + setMaterialNodeProperties(materialNode, materialID, hfmModel); } } else { - setMaterialNodeProperties(materialNode, meshPart.materialID, model); + setMaterialNodeProperties(materialNode, meshPart.materialID, hfmModel); } objectNode.children.append(materialNode); @@ -235,7 +235,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMModel& model) { auto size = meshParts.size(); for (int i = 0; i < size; i++) { QString material = meshParts[i].materialID; - HFMMaterial currentMaterial = model.materials[material]; + HFMMaterial currentMaterial = hfmModel.materials[material]; if (!currentMaterial.albedoTexture.filename.isEmpty() || !currentMaterial.specularTexture.filename.isEmpty()) { auto textureID = nextNodeID(); _mapTextureMaterial.emplace_back(textureID, i); @@ -325,12 +325,12 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMModel& model) { } // Set properties for material nodes -void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMModel& model) { +void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMModel& hfmModel) { auto materialID = nextNodeID(); _materialIDs.push_back(materialID); materialNode.properties = { materialID, material, MESH }; - HFMMaterial currentMaterial = model.materials[material]; + HFMMaterial currentMaterial = hfmModel.materials[material]; // Setting the hierarchy: Material -> Properties70 -> P -> Properties FBXNode properties70Node; diff --git a/libraries/baking/src/OBJBaker.h b/libraries/baking/src/OBJBaker.h index f889730ffa..5aaae49d4a 100644 --- a/libraries/baking/src/OBJBaker.h +++ b/libraries/baking/src/OBJBaker.h @@ -39,8 +39,8 @@ private slots: private: void loadOBJ(); - void createFBXNodeTree(FBXNode& rootNode, HFMModel& model); - void setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMModel& model); + void createFBXNodeTree(FBXNode& rootNode, HFMModel& hfmModel); + void setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMModel& hfmModel); NodeID nextNodeID() { return _nodeID++; } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 15ba5f0101..2cf57e54b4 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -264,17 +264,17 @@ public: }; glm::mat4 getGlobalTransform(const QMultiMap& _connectionParentMap, - const QHash& models, QString nodeID, bool mixamoHack, const QString& url) { + const QHash& fbxModels, QString nodeID, bool mixamoHack, const QString& url) { glm::mat4 globalTransform; QVector visitedNodes; // Used to prevent following a cycle while (!nodeID.isNull()) { visitedNodes.append(nodeID); // Append each node we visit - const FBXModel& model = models.value(nodeID); - globalTransform = glm::translate(model.translation) * model.preTransform * glm::mat4_cast(model.preRotation * - model.rotation * model.postRotation) * model.postTransform * globalTransform; - if (model.hasGeometricOffset) { - glm::mat4 geometricOffset = createMatFromScaleQuatAndPos(model.geometricScaling, model.geometricRotation, model.geometricTranslation); + const FBXModel& fbxModel = fbxModels.value(nodeID); + globalTransform = glm::translate(fbxModel.translation) * fbxModel.preTransform * glm::mat4_cast(fbxModel.preRotation * + fbxModel.rotation * fbxModel.postRotation) * fbxModel.postTransform * globalTransform; + if (fbxModel.hasGeometricOffset) { + glm::mat4 geometricOffset = createMatFromScaleQuatAndPos(fbxModel.geometricScaling, fbxModel.geometricRotation, fbxModel.geometricTranslation); globalTransform = globalTransform * geometricOffset; } @@ -290,7 +290,7 @@ glm::mat4 getGlobalTransform(const QMultiMap& _connectionParen continue; } - if (models.contains(parentID)) { + if (fbxModels.contains(parentID)) { nodeID = parentID; break; } @@ -329,7 +329,7 @@ public: }; void appendModelIDs(const QString& parentID, const QMultiMap& connectionChildMap, - QHash& models, QSet& remainingModels, QVector& modelIDs, bool isRootNode = false) { + QHash& fbxModels, QSet& remainingModels, QVector& modelIDs, bool isRootNode = false) { if (remainingModels.contains(parentID)) { modelIDs.append(parentID); remainingModels.remove(parentID); @@ -337,10 +337,10 @@ void appendModelIDs(const QString& parentID, const QMultiMap& int parentIndex = isRootNode ? -1 : modelIDs.size() - 1; foreach (const QString& childID, connectionChildMap.values(parentID)) { if (remainingModels.contains(childID)) { - FBXModel& model = models[childID]; - if (model.parentIndex == -1) { - model.parentIndex = parentIndex; - appendModelIDs(childID, connectionChildMap, models, remainingModels, modelIDs); + FBXModel& fbxModel = fbxModels[childID]; + if (fbxModel.parentIndex == -1) { + fbxModel.parentIndex = parentIndex; + appendModelIDs(childID, connectionChildMap, fbxModels, remainingModels, modelIDs); } } } @@ -503,7 +503,7 @@ void addBlendshapes(const ExtractedBlendshape& extracted, const QList& connectionParentMap, - const QHash& models, const QString& modelID, const QString& url) { + const QHash& fbxModels, const QString& modelID, const QString& url) { QString topID = modelID; QVector visitedNodes; // Used to prevent following a cycle forever { @@ -515,7 +515,7 @@ QString getTopModelID(const QMultiMap& connectionParentMap, continue; } - if (models.contains(parentID)) { + if (fbxModels.contains(parentID)) { topID = parentID; goto outerContinue; } @@ -689,10 +689,10 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& #if defined(DEBUG_FBXREADER) int unknown = 0; #endif - HFMModel* modelPtr = new HFMModel; - HFMModel& model = *modelPtr; + HFMModel* hfmModelPtr = new HFMModel; + HFMModel& hfmModel = *hfmModelPtr; - model.originalURL = url; + hfmModel.originalURL = url; float unitScaleFactor = 1.0f; glm::vec3 ambientColor; @@ -708,7 +708,7 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& if (subobject.name == "MetaData") { foreach (const FBXNode& subsubobject, subobject.children) { if (subsubobject.name == "Author") { - model.author = subsubobject.properties.at(0).toString(); + hfmModel.author = subsubobject.properties.at(0).toString(); } } } else if (subobject.name == "Properties70") { @@ -716,7 +716,7 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& static const QVariant APPLICATION_NAME = QVariant(QByteArray("Original|ApplicationName")); if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 && subsubobject.properties.at(0) == APPLICATION_NAME) { - model.applicationName = subsubobject.properties.at(4).toString(); + hfmModel.applicationName = subsubobject.properties.at(4).toString(); } } } @@ -1307,7 +1307,7 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& name = name.mid(name.lastIndexOf('.') + 1); } QString id = getID(object.properties); - model.blendshapeChannelNames << name; + hfmModel.blendshapeChannelNames << name; foreach (const WeightedIndex& index, blendshapeIndices.values(name)) { blendshapeChannelIndices.insert(id, index); } @@ -1454,7 +1454,7 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& float offsetScale = mapping.value("scale", 1.0f).toFloat() * unitScaleFactor * METERS_PER_CENTIMETER; glm::quat offsetRotation = glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(), mapping.value("ry").toFloat(), mapping.value("rz").toFloat()))); - model.offset = glm::translate(glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), + hfmModel.offset = glm::translate(glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), mapping.value("tz").toFloat())) * glm::mat4_cast(offsetRotation) * glm::scale(glm::vec3(offsetScale, offsetScale, offsetScale)); @@ -1507,12 +1507,12 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& HFMAnimationFrame frame; frame.rotations.resize(modelIDs.size()); frame.translations.resize(modelIDs.size()); - model.animationFrames.append(frame); + hfmModel.animationFrames.append(frame); } // convert the models to joints QVariantList freeJoints = mapping.values("freeJoint"); - model.hasSkeletonJoints = false; + hfmModel.hasSkeletonJoints = false; foreach (const QString& modelID, modelIDs) { const FBXModel& fbxModel = fbxModels[modelID]; HFMJoint joint; @@ -1520,11 +1520,11 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& joint.parentIndex = fbxModel.parentIndex; // get the indices of all ancestors starting with the first free one (if any) - int jointIndex = model.joints.size(); + int jointIndex = hfmModel.joints.size(); joint.freeLineage.append(jointIndex); int lastFreeIndex = joint.isFree ? 0 : -1; - for (int index = joint.parentIndex; index != -1; index = model.joints.at(index).parentIndex) { - if (model.joints.at(index).isFree) { + for (int index = joint.parentIndex; index != -1; index = hfmModel.joints.at(index).parentIndex) { + if (hfmModel.joints.at(index).isFree) { lastFreeIndex = joint.freeLineage.size(); } joint.freeLineage.append(index); @@ -1547,13 +1547,13 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; if (joint.parentIndex == -1) { - joint.transform = model.offset * glm::translate(joint.translation) * joint.preTransform * + joint.transform = hfmModel.offset * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation); joint.distanceToParent = 0.0f; } else { - const HFMJoint& parentJoint = model.joints.at(joint.parentIndex); + const HFMJoint& parentJoint = hfmModel.joints.at(joint.parentIndex); joint.transform = parentJoint.transform * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation) * parentJoint.inverseDefaultRotation; @@ -1566,15 +1566,15 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& foreach (const QString& childID, _connectionChildMap.values(modelID)) { QString type = typeFlags.value(childID); if (!type.isEmpty()) { - model.hasSkeletonJoints |= (joint.isSkeletonJoint = type.toLower().contains("Skeleton")); + hfmModel.hasSkeletonJoints |= (joint.isSkeletonJoint = type.toLower().contains("Skeleton")); break; } } joint.bindTransformFoundInCluster = false; - model.joints.append(joint); - model.jointIndices.insert(fbxModel.name, model.joints.size()); + hfmModel.joints.append(joint); + hfmModel.jointIndices.insert(fbxModel.name, hfmModel.joints.size()); QString rotationID = localRotations.value(modelID); AnimationCurve xRotCurve = animationCurves.value(xComponents.value(rotationID)); @@ -1590,11 +1590,11 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& glm::vec3 defaultPosValues = joint.translation; for (int i = 0; i < frameCount; i++) { - model.animationFrames[i].rotations[jointIndex] = glm::quat(glm::radians(glm::vec3( + hfmModel.animationFrames[i].rotations[jointIndex] = glm::quat(glm::radians(glm::vec3( xRotCurve.values.isEmpty() ? defaultRotValues.x : xRotCurve.values.at(i % xRotCurve.values.size()), yRotCurve.values.isEmpty() ? defaultRotValues.y : yRotCurve.values.at(i % yRotCurve.values.size()), zRotCurve.values.isEmpty() ? defaultRotValues.z : zRotCurve.values.at(i % zRotCurve.values.size())))); - model.animationFrames[i].translations[jointIndex] = glm::vec3( + hfmModel.animationFrames[i].translations[jointIndex] = glm::vec3( xPosCurve.values.isEmpty() ? defaultPosValues.x : xPosCurve.values.at(i % xPosCurve.values.size()), yPosCurve.values.isEmpty() ? defaultPosValues.y : yPosCurve.values.at(i % yPosCurve.values.size()), zPosCurve.values.isEmpty() ? defaultPosValues.z : zPosCurve.values.at(i % zPosCurve.values.size())); @@ -1603,32 +1603,32 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& // NOTE: shapeVertices are in joint-frame std::vector shapeVertices; - shapeVertices.resize(std::max(1, model.joints.size()) ); + shapeVertices.resize(std::max(1, hfmModel.joints.size()) ); // find our special joints - model.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID); - model.rightEyeJointIndex = modelIDs.indexOf(jointEyeRightID); - model.neckJointIndex = modelIDs.indexOf(jointNeckID); - model.rootJointIndex = modelIDs.indexOf(jointRootID); - model.leanJointIndex = modelIDs.indexOf(jointLeanID); - model.headJointIndex = modelIDs.indexOf(jointHeadID); - model.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID); - model.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); - model.leftToeJointIndex = modelIDs.indexOf(jointLeftToeID); - model.rightToeJointIndex = modelIDs.indexOf(jointRightToeID); + hfmModel.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID); + hfmModel.rightEyeJointIndex = modelIDs.indexOf(jointEyeRightID); + hfmModel.neckJointIndex = modelIDs.indexOf(jointNeckID); + hfmModel.rootJointIndex = modelIDs.indexOf(jointRootID); + hfmModel.leanJointIndex = modelIDs.indexOf(jointLeanID); + hfmModel.headJointIndex = modelIDs.indexOf(jointHeadID); + hfmModel.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID); + hfmModel.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); + hfmModel.leftToeJointIndex = modelIDs.indexOf(jointLeftToeID); + hfmModel.rightToeJointIndex = modelIDs.indexOf(jointRightToeID); foreach (const QString& id, humanIKJointIDs) { - model.humanIKJointIndices.append(modelIDs.indexOf(id)); + hfmModel.humanIKJointIndices.append(modelIDs.indexOf(id)); } // extract the translation component of the neck transform - if (model.neckJointIndex != -1) { - const glm::mat4& transform = model.joints.at(model.neckJointIndex).transform; - model.neckPivot = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); + if (hfmModel.neckJointIndex != -1) { + const glm::mat4& transform = hfmModel.joints.at(hfmModel.neckJointIndex).transform; + hfmModel.neckPivot = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); } - model.bindExtents.reset(); - model.meshExtents.reset(); + hfmModel.bindExtents.reset(); + hfmModel.meshExtents.reset(); // Create the Material Library consolidateHFMMaterials(mapping); @@ -1664,7 +1664,7 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } } #endif - model.materials = _hfmMaterials; + hfmModel.materials = _hfmMaterials; // see if any materials have texture children bool materialsHaveTextures = checkMaterialsHaveTextures(_hfmMaterials, _textureFilenames, _connectionChildMap); @@ -1676,13 +1676,13 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& // accumulate local transforms QString modelID = fbxModels.contains(it.key()) ? it.key() : _connectionParentMap.value(it.key()); - glm::mat4 modelTransform = getGlobalTransform(_connectionParentMap, fbxModels, modelID, model.applicationName == "mixamo.com", url); + glm::mat4 modelTransform = getGlobalTransform(_connectionParentMap, fbxModels, modelID, hfmModel.applicationName == "mixamo.com", url); // compute the mesh extents from the transformed vertices foreach (const glm::vec3& vertex, extracted.mesh.vertices) { glm::vec3 transformedVertex = glm::vec3(modelTransform * glm::vec4(vertex, 1.0f)); - model.meshExtents.minimum = glm::min(model.meshExtents.minimum, transformedVertex); - model.meshExtents.maximum = glm::max(model.meshExtents.maximum, transformedVertex); + hfmModel.meshExtents.minimum = glm::min(hfmModel.meshExtents.minimum, transformedVertex); + hfmModel.meshExtents.maximum = glm::max(hfmModel.meshExtents.maximum, transformedVertex); extracted.mesh.meshExtents.minimum = glm::min(extracted.mesh.meshExtents.minimum, transformedVertex); extracted.mesh.meshExtents.maximum = glm::max(extracted.mesh.meshExtents.maximum, transformedVertex); @@ -1763,14 +1763,14 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& extracted.mesh.clusters.append(hfmCluster); // override the bind rotation with the transform link - HFMJoint& joint = model.joints[hfmCluster.jointIndex]; + HFMJoint& joint = hfmModel.joints[hfmCluster.jointIndex]; joint.inverseBindRotation = glm::inverse(extractRotation(cluster.transformLink)); joint.bindTransform = cluster.transformLink; joint.bindTransformFoundInCluster = true; // update the bind pose extents - glm::vec3 bindTranslation = extractTranslation(model.offset * joint.bindTransform); - model.bindExtents.addPoint(bindTranslation); + glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * joint.bindTransform); + hfmModel.bindExtents.addPoint(bindTranslation); } } @@ -1801,14 +1801,14 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& const Cluster& cluster = clusters[clusterID]; const HFMCluster& hfmCluster = extracted.mesh.clusters.at(i); int jointIndex = hfmCluster.jointIndex; - HFMJoint& joint = model.joints[jointIndex]; + HFMJoint& joint = hfmModel.joints[jointIndex]; glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform; glm::vec3 boneEnd = extractTranslation(transformJointToMesh); glm::vec3 boneBegin = boneEnd; glm::vec3 boneDirection; float boneLength = 0.0f; if (joint.parentIndex != -1) { - boneBegin = extractTranslation(inverseModelTransform * model.joints[joint.parentIndex].bindTransform); + boneBegin = extractTranslation(inverseModelTransform * hfmModel.joints[joint.parentIndex].bindTransform); boneDirection = boneEnd - boneBegin; boneLength = glm::length(boneDirection); if (boneLength > EPSILON) { @@ -1882,7 +1882,7 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } else { // this is a single-mesh joint int jointIndex = firstHFMCluster.jointIndex; - HFMJoint& joint = model.joints[jointIndex]; + HFMJoint& joint = hfmModel.joints[jointIndex]; // transform cluster vertices to joint-frame and save for later glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform; @@ -1902,8 +1902,8 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } buildModelMesh(extracted.mesh, url); - model.meshes.append(extracted.mesh); - int meshIndex = model.meshes.size() - 1; + hfmModel.meshes.append(extracted.mesh); + int meshIndex = hfmModel.meshes.size() - 1; if (extracted.mesh._mesh) { extracted.mesh._mesh->displayName = QString("%1#/mesh/%2").arg(url).arg(meshIndex).toStdString(); extracted.mesh._mesh->modelName = modelIDsToNames.value(modelID).toStdString(); @@ -1923,8 +1923,8 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& }; // now that all joints have been scanned compute a k-Dop bounding volume of mesh - for (int i = 0; i < model.joints.size(); ++i) { - HFMJoint& joint = model.joints[i]; + for (int i = 0; i < hfmModel.joints.size(); ++i) { + HFMJoint& joint = hfmModel.joints[i]; // NOTE: points are in joint-frame ShapeVertices& points = shapeVertices.at(i); @@ -1958,7 +1958,7 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& generateBoundryLinesForDop14(joint.shapeInfo.dots, joint.shapeInfo.avgPoint, joint.shapeInfo.debugLines); } } - model.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); + hfmModel.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); // attempt to map any meshes to a named model for (QHash::const_iterator m = meshIDsToMeshIndices.constBegin(); @@ -1971,14 +1971,14 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& const QString& modelID = ooChildToParent.value(meshID); if (modelIDsToNames.contains(modelID)) { const QString& modelName = modelIDsToNames.value(modelID); - model.meshIndicesToModelNames.insert(meshIndex, modelName); + hfmModel.meshIndicesToModelNames.insert(meshIndex, modelName); } } } { int i = 0; - for (const auto& mesh : model.meshes) { - auto name = model.getModelNameOfMesh(i++); + for (const auto& mesh : hfmModel.meshes) { + auto name = hfmModel.getModelNameOfMesh(i++); if (!name.isEmpty()) { if (mesh._mesh) { mesh._mesh->modelName = name.toStdString(); @@ -1991,7 +1991,7 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& } } } - return modelPtr; + return hfmModelPtr; } HFMModel* readFBX(const QByteArray& data, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index cc1f3bec1c..9cd43ddf08 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -697,7 +697,7 @@ glm::mat4 GLTFReader::getModelTransform(const GLTFNode& node) { return tmat; } -bool GLTFReader::buildGeometry(HFMModel& model, const QUrl& url) { +bool GLTFReader::buildGeometry(HFMModel& hfmModel, const QUrl& url) { //Build dependencies QVector> nodeDependencies(_file.nodes.size()); @@ -727,17 +727,17 @@ bool GLTFReader::buildGeometry(HFMModel& model, const QUrl& url) { } //Build default joints - model.joints.resize(1); - model.joints[0].isFree = false; - model.joints[0].parentIndex = -1; - model.joints[0].distanceToParent = 0; - model.joints[0].translation = glm::vec3(0, 0, 0); - model.joints[0].rotationMin = glm::vec3(0, 0, 0); - model.joints[0].rotationMax = glm::vec3(0, 0, 0); - model.joints[0].name = "OBJ"; - model.joints[0].isSkeletonJoint = true; + hfmModel.joints.resize(1); + hfmModel.joints[0].isFree = false; + hfmModel.joints[0].parentIndex = -1; + hfmModel.joints[0].distanceToParent = 0; + hfmModel.joints[0].translation = glm::vec3(0, 0, 0); + hfmModel.joints[0].rotationMin = glm::vec3(0, 0, 0); + hfmModel.joints[0].rotationMax = glm::vec3(0, 0, 0); + hfmModel.joints[0].name = "OBJ"; + hfmModel.joints[0].isSkeletonJoint = true; - model.jointIndices["x"] = 1; + hfmModel.jointIndices["x"] = 1; //Build materials QVector materialIDs; @@ -750,8 +750,8 @@ bool GLTFReader::buildGeometry(HFMModel& model, const QUrl& url) { for (int i = 0; i < materialIDs.size(); i++) { QString& matid = materialIDs[i]; - model.materials[matid] = HFMMaterial(); - HFMMaterial& hfmMaterial = model.materials[matid]; + hfmModel.materials[matid] = HFMMaterial(); + HFMMaterial& hfmMaterial = hfmModel.materials[matid]; hfmMaterial._material = std::make_shared(); setHFMMaterial(hfmMaterial, _file.materials[i]); } @@ -765,8 +765,8 @@ bool GLTFReader::buildGeometry(HFMModel& model, const QUrl& url) { if (node.defined["mesh"]) { qCDebug(modelformat) << "node_transforms" << node.transforms; foreach(auto &primitive, _file.meshes[node.mesh].primitives) { - model.meshes.append(HFMMesh()); - HFMMesh& mesh = model.meshes[model.meshes.size() - 1]; + hfmModel.meshes.append(HFMMesh()); + HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; HFMCluster cluster; cluster.jointIndex = 0; cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, @@ -886,7 +886,7 @@ bool GLTFReader::buildGeometry(HFMModel& model, const QUrl& url) { mesh.meshExtents.reset(); foreach(const glm::vec3& vertex, mesh.vertices) { mesh.meshExtents.addPoint(vertex); - model.meshExtents.addPoint(vertex); + hfmModel.meshExtents.addPoint(vertex); } // since mesh.modelTransform seems to not have any effect I apply the transformation the model @@ -898,7 +898,7 @@ bool GLTFReader::buildGeometry(HFMModel& model, const QUrl& url) { } } - mesh.meshIndex = model.meshes.size(); + mesh.meshIndex = hfmModel.meshes.size(); FBXReader::buildModelMesh(mesh, url.toString()); } @@ -924,13 +924,13 @@ HFMModel* GLTFReader::readGLTF(QByteArray& data, const QVariantHash& mapping, parseGLTF(data); //_file.dump(); - HFMModel* modelPtr = new HFMModel(); - HFMModel& model = *modelPtr; + HFMModel* hfmModelPtr = new HFMModel(); + HFMModel& hfmModel = *hfmModelPtr; - buildGeometry(model, url); + buildGeometry(hfmModel, url); //hfmDebugDump(data); - return modelPtr; + return hfmModelPtr; } diff --git a/libraries/fbx/src/GLTFReader.h b/libraries/fbx/src/GLTFReader.h index 544c85f1c1..44186504f0 100644 --- a/libraries/fbx/src/GLTFReader.h +++ b/libraries/fbx/src/GLTFReader.h @@ -706,7 +706,7 @@ class GLTFReader : public QObject { Q_OBJECT public: GLTFReader(); - HFMModel* readGLTF(QByteArray& model, const QVariantHash& mapping, + HFMModel* readGLTF(QByteArray& data, const QVariantHash& mapping, const QUrl& url, bool loadLightmaps = true, float lightmapLevel = 1.0f); private: GLTFFile _file; @@ -714,7 +714,7 @@ private: glm::mat4 getModelTransform(const GLTFNode& node); - bool buildGeometry(HFMModel& model, const QUrl& url); + bool buildGeometry(HFMModel& hfmModel, const QUrl& url); bool parseGLTF(const QByteArray& data); bool getStringVal(const QJsonObject& object, const QString& fieldname, diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index a37359aa2a..87fae0e965 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -488,10 +488,10 @@ QNetworkReply* request(QUrl& url, bool isTest) { } -bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, HFMModel& model, +bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, HFMModel& hfmModel, float& scaleGuess, bool combineParts) { FaceGroup faces; - HFMMesh& mesh = model.meshes[0]; + HFMMesh& mesh = hfmModel.meshes[0]; mesh.parts.append(HFMMeshPart()); HFMMeshPart& meshPart = mesh.parts.last(); bool sawG = false; @@ -657,36 +657,36 @@ HFMModel::Pointer OBJReader::readOBJ(QByteArray& data, const QVariantHash& mappi QBuffer buffer { &data }; buffer.open(QIODevice::ReadOnly); - auto modelPtr { std::make_shared() }; - HFMModel& model { *modelPtr }; + auto hfmModelPtr { std::make_shared() }; + HFMModel& hfmModel { *hfmModelPtr }; OBJTokenizer tokenizer { &buffer }; float scaleGuess = 1.0f; bool needsMaterialLibrary = false; _url = url; - model.meshExtents.reset(); - model.meshes.append(HFMMesh()); + hfmModel.meshExtents.reset(); + hfmModel.meshes.append(HFMMesh()); try { // call parseOBJGroup as long as it's returning true. Each successful call will // add a new meshPart to the model's single mesh. - while (parseOBJGroup(tokenizer, mapping, model, scaleGuess, combineParts)) {} + while (parseOBJGroup(tokenizer, mapping, hfmModel, scaleGuess, combineParts)) {} - HFMMesh& mesh = model.meshes[0]; + HFMMesh& mesh = hfmModel.meshes[0]; mesh.meshIndex = 0; - model.joints.resize(1); - model.joints[0].isFree = false; - model.joints[0].parentIndex = -1; - model.joints[0].distanceToParent = 0; - model.joints[0].translation = glm::vec3(0, 0, 0); - model.joints[0].rotationMin = glm::vec3(0, 0, 0); - model.joints[0].rotationMax = glm::vec3(0, 0, 0); - model.joints[0].name = "OBJ"; - model.joints[0].isSkeletonJoint = true; + hfmModel.joints.resize(1); + hfmModel.joints[0].isFree = false; + hfmModel.joints[0].parentIndex = -1; + hfmModel.joints[0].distanceToParent = 0; + hfmModel.joints[0].translation = glm::vec3(0, 0, 0); + hfmModel.joints[0].rotationMin = glm::vec3(0, 0, 0); + hfmModel.joints[0].rotationMax = glm::vec3(0, 0, 0); + hfmModel.joints[0].name = "OBJ"; + hfmModel.joints[0].isSkeletonJoint = true; - model.jointIndices["x"] = 1; + hfmModel.jointIndices["x"] = 1; HFMCluster cluster; cluster.jointIndex = 0; @@ -818,13 +818,13 @@ HFMModel::Pointer OBJReader::readOBJ(QByteArray& data, const QVariantHash& mappi mesh.meshExtents.reset(); foreach(const glm::vec3& vertex, mesh.vertices) { mesh.meshExtents.addPoint(vertex); - model.meshExtents.addPoint(vertex); + hfmModel.meshExtents.addPoint(vertex); } // Build the single mesh. FBXReader::buildModelMesh(mesh, url.toString()); - // hfmDebugDump(model); + // hfmDebugDump(hfmModel); } catch(const std::exception& e) { qCDebug(modelformat) << "OBJ reader fail: " << e.what(); } @@ -885,12 +885,12 @@ HFMModel::Pointer OBJReader::readOBJ(QByteArray& data, const QVariantHash& mappi if (!objMaterial.used) { continue; } - model.materials[materialID] = HFMMaterial(objMaterial.diffuseColor, + hfmModel.materials[materialID] = HFMMaterial(objMaterial.diffuseColor, objMaterial.specularColor, objMaterial.emissiveColor, objMaterial.shininess, objMaterial.opacity); - HFMMaterial& hfmMaterial = model.materials[materialID]; + HFMMaterial& hfmMaterial = hfmModel.materials[materialID]; hfmMaterial.materialID = materialID; hfmMaterial._material = std::make_shared(); graphics::MaterialPointer modelMaterial = hfmMaterial._material; @@ -988,7 +988,7 @@ HFMModel::Pointer OBJReader::readOBJ(QByteArray& data, const QVariantHash& mappi modelMaterial->setOpacity(hfmMaterial.opacity); } - return modelPtr; + return hfmModelPtr; } void hfmDebugDump(const HFMModel& hfmModel) { diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 1b259d90df..0088e8e9d7 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -93,7 +93,7 @@ private: QUrl _url; QHash librariesSeen; - bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, HFMModel& model, + bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, HFMModel& hfmModel, float& scaleGuess, bool combineParts); void parseMaterialLibrary(QIODevice* device); void parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions); diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index b5367a77ca..8de9c39da9 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -22,8 +22,8 @@ bool HFMModelLessThan(const HFMMesh& e1, const HFMMesh& e2) { return e1.meshIndex < e2.meshIndex; } -void reSortHFMModelMeshes(HFMModel& model) { - qSort(model.meshes.begin(), model.meshes.end(), HFMModelLessThan); +void reSortHFMModelMeshes(HFMModel& hfmModel) { + qSort(hfmModel.meshes.begin(), hfmModel.meshes.end(), HFMModelLessThan); } @@ -41,17 +41,17 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, HFMModel& result) { } try { QByteArray fbxContents = fbx.readAll(); - HFMModel::Pointer model; + HFMModel::Pointer hfmModel; if (filename.toLower().endsWith(".obj")) { bool combineParts = false; - model = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts); + hfmModel = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts); } else if (filename.toLower().endsWith(".fbx")) { - model.reset(readFBX(fbxContents, QVariantHash(), filename)); + hfmModel.reset(readFBX(fbxContents, QVariantHash(), filename)); } else { qWarning() << "file has unknown extension" << filename; return false; } - result = *model; + result = *hfmModel; reSortHFMModelMeshes(result); } catch (const QString& error) { @@ -288,17 +288,17 @@ float computeDt(uint64_t start) { return (float)(usecTimestampNow() - start) / (float)USECS_PER_SECOND; } -bool vhacd::VHACDUtil::computeVHACD(HFMModel& model, +bool vhacd::VHACDUtil::computeVHACD(HFMModel& hfmModel, VHACD::IVHACD::Parameters params, HFMModel& result, float minimumMeshSize, float maximumMeshSize) { if (_verbose) { - qDebug() << "meshes =" << model.meshes.size(); + qDebug() << "meshes =" << hfmModel.meshes.size(); } // count the mesh-parts int numParts = 0; - foreach (const HFMMesh& mesh, model.meshes) { + foreach (const HFMMesh& mesh, hfmModel.meshes) { numParts += mesh.parts.size(); } if (_verbose) { @@ -316,7 +316,7 @@ bool vhacd::VHACDUtil::computeVHACD(HFMModel& model, int meshIndex = 0; int validPartsFound = 0; - foreach (const HFMMesh& mesh, model.meshes) { + foreach (const HFMMesh& mesh, hfmModel.meshes) { // find duplicate points int numDupes = 0; @@ -337,7 +337,7 @@ bool vhacd::VHACDUtil::computeVHACD(HFMModel& model, // each mesh has its own transform to move it to model-space std::vector vertices; - glm::mat4 totalTransform = model.offset * mesh.modelTransform; + glm::mat4 totalTransform = hfmModel.offset * mesh.modelTransform; foreach (glm::vec3 vertex, mesh.vertices) { vertices.push_back(glm::vec3(totalTransform * glm::vec4(vertex, 1.0f))); } diff --git a/tools/vhacd-util/src/VHACDUtil.h b/tools/vhacd-util/src/VHACDUtil.h index 6ef568d170..dd8f606756 100644 --- a/tools/vhacd-util/src/VHACDUtil.h +++ b/tools/vhacd-util/src/VHACDUtil.h @@ -31,7 +31,7 @@ namespace vhacd { void fattenMesh(const HFMMesh& mesh, const glm::mat4& modelOffset, HFMMesh& result) const; - bool computeVHACD(HFMModel& model, + bool computeVHACD(HFMModel& hfmModel, VHACD::IVHACD::Parameters params, HFMModel& result, float minimumMeshSize, float maximumMeshSize); diff --git a/tools/vhacd-util/src/VHACDUtilApp.h b/tools/vhacd-util/src/VHACDUtilApp.h index 72e8cfb7e4..7dadad20b3 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.h +++ b/tools/vhacd-util/src/VHACDUtilApp.h @@ -28,7 +28,7 @@ public: VHACDUtilApp(int argc, char* argv[]); ~VHACDUtilApp(); - bool writeOBJ(QString outFileName, HFMModel& model, bool outputCentimeters, int whichMeshPart = -1); + bool writeOBJ(QString outFileName, HFMModel& hfmModel, bool outputCentimeters, int whichMeshPart = -1); int getReturnCode() const { return _returnCode; } From 515ff016a46c079d234705dc107cb21415a5d8a0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 5 Nov 2018 13:03:34 -0800 Subject: [PATCH 099/125] Fix seven bugs --- .../qml/hifi/commerce/checkout/Checkout.qml | 7 +---- .../qml/hifi/commerce/wallet/Help.qml | 2 +- .../qml/hifi/commerce/wallet/Wallet.qml | 20 ++++++++------ .../qml/hifi/dialogs/security/Security.qml | 2 +- scripts/system/commerce/wallet.js | 8 +++--- scripts/system/marketplaces/marketplaces.js | 27 ++++++++++++++++--- 6 files changed, 42 insertions(+), 24 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index db030d2fee..271aab87d1 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -240,11 +240,6 @@ Rectangle { lightboxPopup.button1method = function() { lightboxPopup.visible = false; } - lightboxPopup.button2text = "GO TO WALLET"; - lightboxPopup.button2method = function() { - lightboxPopup.visible = false; - sendToScript({method: 'checkout_openWallet'}); - }; lightboxPopup.visible = true; } else { sendToScript(msg); @@ -903,7 +898,7 @@ Rectangle { horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignVCenter; onLinkActivated: { - sendToScript({method: 'checkout_openWallet'}); + sendToScript({method: 'checkout_openRecentActivity'}); } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 6cf00f78eb..575edfc34d 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -62,7 +62,7 @@ Item { isExpanded: false; question: "How can I get HFC?"; answer: "High Fidelity commerce is in open beta right now. Want more HFC? \ -Get it by going to

BankOfHighFidelity. and meeting with the banker!"; +Get it by going to BankOfHighFidelity and meeting with the banker!"; } ListElement { isExpanded: false; diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 97736bf781..76c2484f0b 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -29,6 +29,7 @@ Rectangle { id: root; property string activeView: "initialize"; + property string initialActiveViewAfterStatus5: "walletInventory"; property bool keyboardRaised: false; property bool isPassword: false; @@ -66,7 +67,7 @@ Rectangle { } } else if (walletStatus === 5) { if (root.activeView !== "walletSetup") { - root.activeView = "walletInventory"; + root.activeView = root.initialActiveViewAfterStatus5; Commerce.getAvailableUpdates(); Commerce.getSecurityImage(); } @@ -526,7 +527,7 @@ Rectangle { id: exchangeMoneyMessagesWaitingLight; visible: parent.messagesWaiting; anchors.right: exchangeMoneyTabIcon.left; - anchors.rightMargin: -4; + anchors.rightMargin: 9; anchors.top: exchangeMoneyTabIcon.top; anchors.topMargin: 16; height: 10; @@ -682,15 +683,12 @@ Rectangle { function resetTabButtonColors() { walletHomeButtonContainer.color = hifi.colors.black; sendMoneyButtonContainer.color = hifi.colors.black; - securityButtonContainer.color = hifi.colors.black; helpButtonContainer.color = hifi.colors.black; exchangeMoneyButtonContainer.color = hifi.colors.black; if (root.activeView === "walletHome") { walletHomeButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "sendMoney") { sendMoneyButtonContainer.color = hifi.colors.blueAccent; - } else if (root.activeView === "security") { - securityButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "help") { helpButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView == "walletInventory") { @@ -760,10 +758,12 @@ Rectangle { break; case 'updateConnections': sendMoney.updateConnections(message.connections); + walletInventory.fromScript(message); break; case 'selectRecipient': case 'updateSelectedRecipientUsername': sendMoney.fromScript(message); + walletInventory.fromScript(message); break; case 'http.response': http.handleHttpResponse(message); @@ -779,15 +779,19 @@ Rectangle { break; case 'updatePurchases': case 'purchases_showMyItems': - case 'updateConnections': - case 'selectRecipient': - case 'updateSelectedRecipientUsername': case 'updateWearables': walletInventory.fromScript(message); break; case 'updateRecentActivityMessageLight': walletHomeButtonContainer.messagesWaiting = message.messagesWaiting; break; + case 'checkout_openRecentActivity': + if (root.activeView === "initialize") { + root.initialActiveViewAfterStatus5 = "walletHome"; + } else { + root.activeView = "walletHome"; + } + break; default: console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); } diff --git a/interface/resources/qml/hifi/dialogs/security/Security.qml b/interface/resources/qml/hifi/dialogs/security/Security.qml index 8baff0ac13..96a554838f 100644 --- a/interface/resources/qml/hifi/dialogs/security/Security.qml +++ b/interface/resources/qml/hifi/dialogs/security/Security.qml @@ -328,7 +328,7 @@ Rectangle { HifiStylesUit.RalewayRegular { text: "Your wallet is not set up.\n" + - "Open the WALLET app to get started."; + "Open the ASSETS app to get started."; // Anchors anchors.fill: parent; // Text size diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 837cf6a78a..033ca638e4 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -18,7 +18,7 @@ Script.include("/~/system/libraries/accountUtils.js"); Script.include("/~/system/libraries/connectionUtils.js"); var AppUi = Script.require('appUi'); - var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace";; +var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; // BEGIN AVATAR SELECTOR LOGIC var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; @@ -607,13 +607,13 @@ function notificationPollCallbackHistory(historyArray) { if (notificationCount > 0) { var message; if (!ui.notificationInitialCallbackMade[1]) { - message = "You have " + notificationCount + " unread wallet " + - "transaction" + (notificationCount === 1 ? "" : "s") + ". Open WALLET to see all activity."; + message = "You have " + notificationCount + " unread recent " + + "transaction" + (notificationCount === 1 ? "" : "s") + ". Open ASSETS to see all activity."; ui.notificationDisplayBanner(message); } else { for (var i = 0; i < notificationCount; i++) { message = '"' + (historyArray[i].message) + '" ' + - "Open WALLET to see all activity."; + "Open ASSETS to see all activity."; ui.notificationDisplayBanner(message); } } diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index ed8ed9f288..8bfd776971 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -527,7 +527,13 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { Window.location = "hifi://BankOfHighFidelity"; } break; - case 'checkout_openWallet': + case 'checkout_openRecentActivity': + ui.open(MARKETPLACE_WALLET_QML_PATH); + wireQmlEventBridge(true); + ui.tablet.sendToQml({ + method: 'checkout_openRecentActivity' + }); + break; case 'checkout_setUpClicked': openWallet(); break; @@ -589,9 +595,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { break; case 'updateItemClicked': openMarketplace(message.upgradeUrl + "?edition=" + message.itemEdition); - break; - case 'giftAsset': - break; case 'passphrasePopup_cancelClicked': case 'needsLogIn_cancelClicked': @@ -635,6 +638,20 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { case 'http.request': // Handled elsewhere, don't log. break; + // All of these are handled by wallet.js + case 'purchases_updateWearables': + case 'enable_ChooseRecipientNearbyMode': + case 'disable_ChooseRecipientNearbyMode': + case 'sendAsset_sendPublicly': + case 'refreshConnections': + case 'transactionHistory_goToBank': + case 'purchases_walletNotSetUp': + case 'purchases_openGoTo': + case 'purchases_itemInfoClicked': + case 'purchases_itemCertificateClicked': + case 'clearShouldShowDotHistory': + case 'giftAsset': + break; default: print('Unrecognized message from Checkout.qml: ' + JSON.stringify(message)); } @@ -705,6 +722,8 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { referrerURL: referrerURL, filterText: filterText }); + referrerURL = ""; + filterText = ""; } ui.isOpen = (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen; From f10b9ae30cb234c6528edc91f2721f47355f89f8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 5 Nov 2018 13:12:43 -0800 Subject: [PATCH 100/125] Squashed commit of the following: commit 515ff016a46c079d234705dc107cb21415a5d8a0 Author: Zach Fox Date: Mon Nov 5 13:03:34 2018 -0800 Fix seven bugs commit a4d36b384551abfa8d83e3ff560b9f271d4aea6b Merge: 8906b909eb d0181283dd Author: Zach Fox Date: Mon Nov 5 10:25:58 2018 -0800 Merge branch 'master' of github.com:highfidelity/hifi into oculus-store-commerce commit d0181283dda0f10856112e0d302258412ede36b3 Merge: be17ed0910 643026abd6 Author: Brad Hefta-Gaub Date: Fri Nov 2 16:44:31 2018 -0700 Merge pull request #14308 from sabrina-shanman/fbx2hfm (case 19302) Re-name FBXGeometry to HFMGeometry and do the same for related classes commit 643026abd61ed38f0907bb65da7693a13110a432 Author: sabrina-shanman Date: Wed Oct 31 14:15:30 2018 -0700 Change local variable in TestFbx for clarity commit 0b7ddca5f66058e5df4fe7d356362f1358dcfcc7 Author: sabrina-shanman Date: Wed Oct 31 14:03:20 2018 -0700 Change naming for straggler names in model loading debug dumps commit becee7f010571d3ffd61deeb062d16a73a06e2a1 Author: sabrina-shanman Date: Tue Oct 30 17:28:42 2018 -0700 Re-name FBXGeometry to HFMGeometry and do the same for related classes --- .../src/avatars/ScriptableAvatar.cpp | 12 +- .../qml/hifi/commerce/checkout/Checkout.qml | 7 +- .../qml/hifi/commerce/wallet/Help.qml | 2 +- .../qml/hifi/commerce/wallet/Wallet.qml | 18 ++- .../qml/hifi/dialogs/security/Security.qml | 2 +- interface/src/ModelPackager.cpp | 4 +- interface/src/ModelPackager.h | 6 +- interface/src/ModelPropertiesDialog.cpp | 4 +- interface/src/ModelPropertiesDialog.h | 4 +- interface/src/avatar/MyAvatar.cpp | 14 +- interface/src/avatar/MySkeletonModel.cpp | 2 +- interface/src/raypick/CollisionPick.cpp | 30 ++-- interface/src/ui/overlays/ModelOverlay.cpp | 22 +-- libraries/animation/src/AnimClip.cpp | 18 +-- libraries/animation/src/AnimSkeleton.cpp | 16 +- libraries/animation/src/AnimSkeleton.h | 8 +- libraries/animation/src/AnimationCache.cpp | 18 +-- libraries/animation/src/AnimationCache.h | 12 +- libraries/animation/src/AnimationObject.cpp | 8 +- libraries/animation/src/AnimationObject.h | 4 +- libraries/animation/src/Rig.cpp | 18 +-- libraries/animation/src/Rig.h | 22 +-- .../src/avatars-renderer/Avatar.cpp | 6 +- .../src/avatars-renderer/SkeletonModel.cpp | 16 +- .../src/avatars-renderer/SkeletonModel.h | 4 +- libraries/baking/src/FBXBaker.cpp | 6 +- libraries/baking/src/FBXBaker.h | 2 +- libraries/baking/src/ModelBaker.cpp | 2 +- libraries/baking/src/ModelBaker.h | 2 +- libraries/baking/src/OBJBaker.cpp | 8 +- libraries/baking/src/OBJBaker.h | 4 +- .../src/RenderableModelEntityItem.cpp | 66 ++++---- libraries/fbx/src/FBX.h | 90 +++++------ libraries/fbx/src/FBXReader.cpp | 150 +++++++++--------- libraries/fbx/src/FBXReader.h | 16 +- libraries/fbx/src/FBXReader_Material.cpp | 40 ++--- libraries/fbx/src/FBXReader_Mesh.cpp | 76 ++++----- libraries/fbx/src/GLTFReader.cpp | 112 ++++++------- libraries/fbx/src/GLTFReader.h | 10 +- libraries/fbx/src/OBJReader.cpp | 96 +++++------ libraries/fbx/src/OBJReader.h | 12 +- .../src/model-networking/ModelCache.cpp | 54 +++---- .../src/model-networking/ModelCache.h | 14 +- .../render-utils/src/CauterizedModel.cpp | 18 +-- .../render-utils/src/MeshPartPayload.cpp | 4 +- libraries/render-utils/src/Model.cpp | 76 ++++----- libraries/render-utils/src/Model.h | 6 +- .../render-utils/src/SoftAttachmentModel.cpp | 6 +- scripts/system/commerce/wallet.js | 8 +- scripts/system/marketplaces/marketplaces.js | 27 +++- tests-manual/gpu/src/TestFbx.cpp | 10 +- tests-manual/gpu/src/TestFbx.h | 2 +- .../src/AnimInverseKinematicsTests.cpp | 8 +- tools/skeleton-dump/src/SkeletonDumpApp.cpp | 4 +- tools/vhacd-util/src/VHACDUtil.cpp | 44 ++--- tools/vhacd-util/src/VHACDUtil.h | 12 +- tools/vhacd-util/src/VHACDUtilApp.cpp | 26 +-- tools/vhacd-util/src/VHACDUtilApp.h | 2 +- 58 files changed, 654 insertions(+), 636 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 7d2b267a05..385f94d9f3 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -69,10 +69,10 @@ void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { AvatarData::setSkeletonModelURL(skeletonModelURL); } -static AnimPose composeAnimPose(const FBXJoint& fbxJoint, const glm::quat rotation, const glm::vec3 translation) { +static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, const glm::vec3 translation) { glm::mat4 translationMat = glm::translate(translation); - glm::mat4 rotationMat = glm::mat4_cast(fbxJoint.preRotation * rotation * fbxJoint.postRotation); - glm::mat4 finalMat = translationMat * fbxJoint.preTransform * rotationMat * fbxJoint.postTransform; + glm::mat4 rotationMat = glm::mat4_cast(joint.preRotation * rotation * joint.postRotation); + glm::mat4 finalMat = translationMat * joint.preTransform * rotationMat * joint.postTransform; return AnimPose(finalMat); } @@ -93,7 +93,7 @@ void ScriptableAvatar::update(float deltatime) { } _animationDetails.currentFrame = currentFrame; - const QVector& modelJoints = _bind->getGeometry().joints; + const QVector& modelJoints = _bind->getGeometry().joints; QStringList animationJointNames = _animation->getJointNames(); const int nJoints = modelJoints.size(); @@ -102,8 +102,8 @@ void ScriptableAvatar::update(float deltatime) { } const int frameCount = _animation->getFrames().size(); - const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount); - const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); + const HFMAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount); + const HFMAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); const float frameFraction = glm::fract(currentFrame); std::vector poses = _animSkeleton->getRelativeDefaultPoses(); diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index db030d2fee..271aab87d1 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -240,11 +240,6 @@ Rectangle { lightboxPopup.button1method = function() { lightboxPopup.visible = false; } - lightboxPopup.button2text = "GO TO WALLET"; - lightboxPopup.button2method = function() { - lightboxPopup.visible = false; - sendToScript({method: 'checkout_openWallet'}); - }; lightboxPopup.visible = true; } else { sendToScript(msg); @@ -903,7 +898,7 @@ Rectangle { horizontalAlignment: Text.AlignLeft; verticalAlignment: Text.AlignVCenter; onLinkActivated: { - sendToScript({method: 'checkout_openWallet'}); + sendToScript({method: 'checkout_openRecentActivity'}); } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 6cf00f78eb..575edfc34d 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -62,7 +62,7 @@ Item { isExpanded: false; question: "How can I get HFC?"; answer: "High Fidelity commerce is in open beta right now. Want more HFC? \ -Get it by going to

BankOfHighFidelity. and meeting with the banker!"; +Get it by going to BankOfHighFidelity and meeting with the banker!"; } ListElement { isExpanded: false; diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index ad472f85f8..1f8490b7c7 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -29,6 +29,7 @@ Rectangle { id: root; property string activeView: "initialize"; + property string initialActiveViewAfterStatus5: "walletInventory"; property bool keyboardRaised: false; property bool isPassword: false; @@ -66,7 +67,7 @@ Rectangle { } } else if (walletStatus === 5) { if (root.activeView !== "walletSetup") { - root.activeView = "walletInventory"; + root.activeView = root.initialActiveViewAfterStatus5; Commerce.getAvailableUpdates(); Commerce.getSecurityImage(); } @@ -688,15 +689,12 @@ Rectangle { function resetTabButtonColors() { walletHomeButtonContainer.color = hifi.colors.black; sendMoneyButtonContainer.color = hifi.colors.black; - securityButtonContainer.color = hifi.colors.black; helpButtonContainer.color = hifi.colors.black; exchangeMoneyButtonContainer.color = hifi.colors.black; if (root.activeView === "walletHome") { walletHomeButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "sendMoney") { sendMoneyButtonContainer.color = hifi.colors.blueAccent; - } else if (root.activeView === "security") { - securityButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView === "help") { helpButtonContainer.color = hifi.colors.blueAccent; } else if (root.activeView == "walletInventory") { @@ -766,10 +764,12 @@ Rectangle { break; case 'updateConnections': sendMoney.updateConnections(message.connections); + walletInventory.fromScript(message); break; case 'selectRecipient': case 'updateSelectedRecipientUsername': sendMoney.fromScript(message); + walletInventory.fromScript(message); break; case 'http.response': http.handleHttpResponse(message); @@ -785,15 +785,19 @@ Rectangle { break; case 'updatePurchases': case 'purchases_showMyItems': - case 'updateConnections': - case 'selectRecipient': - case 'updateSelectedRecipientUsername': case 'updateWearables': walletInventory.fromScript(message); break; case 'updateRecentActivityMessageLight': walletHomeButtonContainer.messagesWaiting = message.messagesWaiting; break; + case 'checkout_openRecentActivity': + if (root.activeView === "initialize") { + root.initialActiveViewAfterStatus5 = "walletHome"; + } else { + root.activeView = "walletHome"; + } + break; default: console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); } diff --git a/interface/resources/qml/hifi/dialogs/security/Security.qml b/interface/resources/qml/hifi/dialogs/security/Security.qml index 8baff0ac13..96a554838f 100644 --- a/interface/resources/qml/hifi/dialogs/security/Security.qml +++ b/interface/resources/qml/hifi/dialogs/security/Security.qml @@ -328,7 +328,7 @@ Rectangle { HifiStylesUit.RalewayRegular { text: "Your wallet is not set up.\n" + - "Open the WALLET app to get started."; + "Open the ASSETS app to get started."; // Anchors anchors.fill: parent; // Text size diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 3a5d92eb8c..0b2846006b 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -235,7 +235,7 @@ bool ModelPackager::zipModel() { return true; } -void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry) { +void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const HFMGeometry& geometry) { bool isBodyType = _modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL; @@ -370,7 +370,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename void ModelPackager::listTextures() { _textures.clear(); - foreach (const FBXMaterial mat, _geometry->materials) { + foreach (const HFMMaterial mat, _geometry->materials) { if (!mat.albedoTexture.filename.isEmpty() && mat.albedoTexture.content.isEmpty() && !_textures.contains(mat.albedoTexture.filename)) { _textures << mat.albedoTexture.filename; diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index 76295e5a85..b68d9e746d 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -19,7 +19,7 @@ #include "ui/ModelsBrowser.h" -class FBXGeometry; +class HFMGeometry; class ModelPackager : public QObject { public: @@ -32,7 +32,7 @@ private: bool editProperties(); bool zipModel(); - void populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry); + void populateBasicMapping(QVariantHash& mapping, QString filename, const HFMGeometry& geometry); void listTextures(); bool copyTextures(const QString& oldDir, const QDir& newDir); @@ -44,7 +44,7 @@ private: QString _scriptDir; QVariantHash _mapping; - std::unique_ptr _geometry; + std::unique_ptr _geometry; QStringList _textures; QStringList _scripts; }; diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index 8984f89d07..dcda85d117 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -27,7 +27,7 @@ ModelPropertiesDialog::ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const FBXGeometry& geometry) : + const QString& basePath, const HFMGeometry& geometry) : _modelType(modelType), _originalMapping(originalMapping), _basePath(basePath), @@ -249,7 +249,7 @@ QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const { if (withNone) { box->addItem("(none)"); } - foreach (const FBXJoint& joint, _geometry.joints) { + foreach (const HFMJoint& joint, _geometry.joints) { if (joint.isSkeletonJoint || !_geometry.hasSkeletonJoints) { box->addItem(joint.name); } diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h index e3c2d8ed6a..d1a020bec3 100644 --- a/interface/src/ModelPropertiesDialog.h +++ b/interface/src/ModelPropertiesDialog.h @@ -30,7 +30,7 @@ class ModelPropertiesDialog : public QDialog { public: ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, - const QString& basePath, const FBXGeometry& geometry); + const QString& basePath, const HFMGeometry& geometry); QVariantHash getMapping() const; @@ -50,7 +50,7 @@ private: FSTReader::ModelType _modelType; QVariantHash _originalMapping; QString _basePath; - FBXGeometry _geometry; + HFMGeometry _geometry; QLineEdit* _name = nullptr; QPushButton* _textureDirectory = nullptr; QPushButton* _scriptDirectory = nullptr; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3299bd10e7..b3a66f70b8 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -155,7 +155,7 @@ MyAvatar::MyAvatar(QThread* thread) : }); connect(_skeletonModel.get(), &Model::rigReady, this, [this]() { if (_shouldLoadScripts) { - auto geometry = getSkeletonModel()->getFBXGeometry(); + auto geometry = getSkeletonModel()->getHFMGeometry(); qApp->loadAvatarScripts(geometry.scripts); _shouldLoadScripts = false; } @@ -2429,10 +2429,10 @@ void MyAvatar::attachmentDataToEntityProperties(const AttachmentData& data, Enti void MyAvatar::initHeadBones() { int neckJointIndex = -1; if (_skeletonModel->isLoaded()) { - neckJointIndex = _skeletonModel->getFBXGeometry().neckJointIndex; + neckJointIndex = _skeletonModel->getHFMGeometry().neckJointIndex; } if (neckJointIndex == -1) { - neckJointIndex = (_skeletonModel->getFBXGeometry().headJointIndex - 1); + neckJointIndex = (_skeletonModel->getHFMGeometry().headJointIndex - 1); if (neckJointIndex < 0) { // return if the head is not even there. can't cauterize!! return; @@ -2443,7 +2443,7 @@ void MyAvatar::initHeadBones() { q.push(neckJointIndex); _headBoneSet.insert(neckJointIndex); - // fbxJoints only hold links to parents not children, so we have to do a bit of extra work here. + // hfmJoints only hold links to parents not children, so we have to do a bit of extra work here. while (q.size() > 0) { int jointIndex = q.front(); for (int i = 0; i < _skeletonModel->getJointStateCount(); i++) { @@ -2592,11 +2592,11 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { if (_skeletonModel && _skeletonModel->isLoaded()) { const Rig& rig = _skeletonModel->getRig(); - const FBXGeometry& geometry = _skeletonModel->getFBXGeometry(); + const HFMGeometry& geometry = _skeletonModel->getHFMGeometry(); for (int i = 0; i < rig.getJointStateCount(); i++) { AnimPose jointPose; rig.getAbsoluteJointPoseInRigFrame(i, jointPose); - const FBXJointShapeInfo& shapeInfo = geometry.joints[i].shapeInfo; + const HFMJointShapeInfo& shapeInfo = geometry.joints[i].shapeInfo; const AnimPose pose = rigToWorldPose * jointPose; for (size_t j = 0; j < shapeInfo.debugLines.size() / 2; j++) { glm::vec3 pointA = pose.xformPoint(shapeInfo.debugLines[2 * j]); @@ -4012,7 +4012,7 @@ float MyAvatar::getSitStandStateChange() const { } QVector MyAvatar::getScriptUrls() { - QVector scripts = _skeletonModel->isLoaded() ? _skeletonModel->getFBXGeometry().scripts : QVector(); + QVector scripts = _skeletonModel->isLoaded() ? _skeletonModel->getHFMGeometry().scripts : QVector(); return scripts; } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index c6aae6124a..3ec40d372b 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -90,7 +90,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { // Called within Model::simulate call, below. void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); Head* head = _owningAvatar->getHead(); diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 25927c5b68..e8a53aa9b6 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -131,7 +131,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // should never fall in here when collision model not fully loaded // TODO: assert that all geometries exist and are loaded //assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); - const FBXGeometry& collisionGeometry = resource->getFBXGeometry(); + const HFMGeometry& collisionGeometry = resource->getHFMGeometry(); ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); @@ -139,15 +139,15 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case. - foreach (const FBXMesh& mesh, collisionGeometry.meshes) { + foreach (const HFMMesh& mesh, collisionGeometry.meshes) { // each meshPart is a convex hull - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { pointCollection.push_back(QVector()); ShapeInfo::PointList& pointsInPart = pointCollection[i]; // run through all the triangles and (uniquely) add each point to the hull uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -168,7 +168,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // run through all the quads and (uniquely) add each point to the hull numIndices = (uint32_t)meshPart.quadIndices.size(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % QUAD_STRIDE == 0); numIndices -= numIndices % QUAD_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -206,7 +206,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha // to the visual model and apply them to the collision model (without regard for the // collision model's extents). - glm::vec3 scaleToFit = dimensions / resource->getFBXGeometry().getUnscaledMeshExtents().size(); + glm::vec3 scaleToFit = dimensions / resource->getHFMGeometry().getUnscaledMeshExtents().size(); // multiply each point by scale for (int32_t i = 0; i < pointCollection.size(); i++) { for (int32_t j = 0; j < pointCollection[i].size(); j++) { @@ -216,11 +216,11 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha } shapeInfo.setParams(type, dimensions, resource->getURL().toString()); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { - const FBXGeometry& fbxGeometry = resource->getFBXGeometry(); - int numFbxMeshes = fbxGeometry.meshes.size(); + const HFMGeometry& hfmGeometry = resource->getHFMGeometry(); + int numHFMMeshes = hfmGeometry.meshes.size(); int totalNumVertices = 0; - for (int i = 0; i < numFbxMeshes; i++) { - const FBXMesh& mesh = fbxGeometry.meshes.at(i); + for (int i = 0; i < numHFMMeshes; i++) { + const HFMMesh& mesh = hfmGeometry.meshes.at(i); totalNumVertices += mesh.vertices.size(); } const int32_t MAX_VERTICES_PER_STATIC_MESH = 1e6; @@ -230,7 +230,7 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha return; } - auto& meshes = resource->getFBXGeometry().meshes; + auto& meshes = resource->getHFMGeometry().meshes; int32_t numMeshes = (int32_t)(meshes.size()); const int MAX_ALLOWED_MESH_COUNT = 1000; @@ -285,12 +285,12 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha if (type == SHAPE_TYPE_STATIC_MESH) { // copy into triangleIndices size_t triangleIndicesCount = 0; - for (const FBXMeshPart& meshPart : mesh.parts) { + for (const HFMMeshPart& meshPart : mesh.parts) { triangleIndicesCount += meshPart.triangleIndices.count(); } triangleIndices.reserve((int)triangleIndicesCount); - for (const FBXMeshPart& meshPart : mesh.parts) { + for (const HFMMeshPart& meshPart : mesh.parts) { const int* indexItr = meshPart.triangleIndices.cbegin(); while (indexItr != meshPart.triangleIndices.cend()) { triangleIndices.push_back(*indexItr); @@ -299,11 +299,11 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha } } else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { // for each mesh copy unique part indices, separated by special bogus (flag) index values - for (const FBXMeshPart& meshPart : mesh.parts) { + for (const HFMMeshPart& meshPart : mesh.parts) { // collect unique list of indices for this part std::set uniqueIndices; auto numIndices = meshPart.triangleIndices.count(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices% TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index eee8222051..1b66ff08ad 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -446,7 +446,7 @@ QVariant ModelOverlay::getProperty(const QString& property) { if (property == "jointNames") { if (_model && _model->isActive()) { - // note: going through Rig because Model::getJointNames() (which proxies to FBXGeometry) was always empty + // note: going through Rig because Model::getJointNames() (which proxies to HFMGeometry) was always empty const Rig* rig = &(_model->getRig()); return mapJoints([rig](int jointIndex) -> QString { return rig->nameOfJoint(jointIndex); @@ -574,7 +574,7 @@ void ModelOverlay::animate() { QVector jointsData; - const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy + const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy int frameCount = frames.size(); if (frameCount <= 0) { return; @@ -606,10 +606,10 @@ void ModelOverlay::animate() { } QStringList animationJointNames = _animation->getGeometry().getJointNames(); - auto& fbxJoints = _animation->getGeometry().joints; + auto& hfmJoints = _animation->getGeometry().joints; - auto& originalFbxJoints = _model->getFBXGeometry().joints; - auto& originalFbxIndices = _model->getFBXGeometry().jointIndices; + auto& originalHFMJoints = _model->getHFMGeometry().joints; + auto& originalFbxIndices = _model->getHFMGeometry().jointIndices; const QVector& rotations = frames[_lastKnownCurrentFrame].rotations; const QVector& translations = frames[_lastKnownCurrentFrame].translations; @@ -626,23 +626,23 @@ void ModelOverlay::animate() { translationMat = glm::translate(translations[index]); } } else if (index < animationJointNames.size()) { - QString jointName = fbxJoints[index].name; + QString jointName = hfmJoints[index].name; if (originalFbxIndices.contains(jointName)) { // Making sure the joint names exist in the original model the animation is trying to apply onto. If they do, then remap and get its translation. int remappedIndex = originalFbxIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. - translationMat = glm::translate(originalFbxJoints[remappedIndex].translation); + translationMat = glm::translate(originalHFMJoints[remappedIndex].translation); } } glm::mat4 rotationMat; if (index < rotations.size()) { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * rotations[index] * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * rotations[index] * hfmJoints[index].postRotation); } else { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * hfmJoints[index].postRotation); } - glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform * - rotationMat * fbxJoints[index].postTransform); + glm::mat4 finalMat = (translationMat * hfmJoints[index].preTransform * + rotationMat * hfmJoints[index].postTransform); auto& jointData = jointsData[j]; jointData.translation = extractTranslation(finalMat); jointData.translationIsDefaultPose = false; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index f9195a608b..d630218165 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -101,7 +101,7 @@ void AnimClip::copyFromNetworkAnim() { // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. - const FBXGeometry& geom = _networkAnim->getGeometry(); + const HFMGeometry& geom = _networkAnim->getGeometry(); AnimSkeleton animSkeleton(geom); const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); @@ -120,7 +120,7 @@ void AnimClip::copyFromNetworkAnim() { for (int frame = 0; frame < frameCount; frame++) { - const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; + const HFMAnimationFrame& hfmAnimFrame = geom.animationFrames[frame]; // init all joints in animation to default pose // this will give us a resonable result for bones in the model skeleton but not in the animation. @@ -132,8 +132,8 @@ void AnimClip::copyFromNetworkAnim() { for (int animJoint = 0; animJoint < animJointCount; animJoint++) { int skeletonJoint = jointMap[animJoint]; - const glm::vec3& fbxAnimTrans = fbxAnimFrame.translations[animJoint]; - const glm::quat& fbxAnimRot = fbxAnimFrame.rotations[animJoint]; + const glm::vec3& hfmAnimTrans = hfmAnimFrame.translations[animJoint]; + const glm::quat& hfmAnimRot = hfmAnimFrame.rotations[animJoint]; // skip joints that are in the animation but not in the skeleton. if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { @@ -146,19 +146,19 @@ void AnimClip::copyFromNetworkAnim() { preRot.scale() = glm::vec3(1.0f); postRot.scale() = glm::vec3(1.0f); - AnimPose rot(glm::vec3(1.0f), fbxAnimRot, glm::vec3()); + AnimPose rot(glm::vec3(1.0f), hfmAnimRot, glm::vec3()); // adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. - const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint]; + const glm::vec3& hfmZeroTrans = geom.animationFrames[0].translations[animJoint]; const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); float boneLengthScale = 1.0f; const float EPSILON = 0.0001f; - if (fabsf(glm::length(fbxZeroTrans)) > EPSILON) { - boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(fbxZeroTrans); + if (fabsf(glm::length(hfmZeroTrans)) > EPSILON) { + boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(hfmZeroTrans); } - AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (fbxAnimTrans - fbxZeroTrans)); + AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (hfmAnimTrans - hfmZeroTrans)); _anim[frame][skeletonJoint] = trans * preRot * rot * postRot; } diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index bed9c590be..fc4114ac7b 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -16,17 +16,17 @@ #include "AnimationLogging.h" -AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { +AnimSkeleton::AnimSkeleton(const HFMGeometry& geometry) { // convert to std::vector of joints - std::vector joints; - joints.reserve(fbxGeometry.joints.size()); - for (auto& joint : fbxGeometry.joints) { + std::vector joints; + joints.reserve(geometry.joints.size()); + for (auto& joint : geometry.joints) { joints.push_back(joint); } buildSkeletonFromJoints(joints); } -AnimSkeleton::AnimSkeleton(const std::vector& joints) { +AnimSkeleton::AnimSkeleton(const std::vector& joints) { buildSkeletonFromJoints(joints); } @@ -166,7 +166,7 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { } } -void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { +void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { _joints = joints; _jointsSize = (int)joints.size(); // build a cache of bind poses @@ -177,7 +177,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) _relativePreRotationPoses.reserve(_jointsSize); _relativePostRotationPoses.reserve(_jointsSize); - // iterate over FBXJoints and extract the bind pose information. + // iterate over HFMJoints and extract the bind pose information. for (int i = 0; i < _jointsSize; i++) { // build pre and post transforms @@ -240,7 +240,7 @@ void AnimSkeleton::dump(bool verbose) const { qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i); qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i); if (verbose) { - qCDebug(animation) << " fbxJoint ="; + qCDebug(animation) << " hfmJoint ="; qCDebug(animation) << " isFree =" << _joints[i].isFree; qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage; qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex; diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 2ebf3f4f5d..1717d75985 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -23,8 +23,8 @@ public: using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; - explicit AnimSkeleton(const FBXGeometry& fbxGeometry); - explicit AnimSkeleton(const std::vector& joints); + explicit AnimSkeleton(const HFMGeometry& geometry); + explicit AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; const QString& getJointName(int jointIndex) const; int getNumJoints() const; @@ -64,9 +64,9 @@ public: std::vector lookUpJointIndices(const std::vector& jointNames) const; protected: - void buildSkeletonFromJoints(const std::vector& joints); + void buildSkeletonFromJoints(const std::vector& joints); - std::vector _joints; + std::vector _joints; int _jointsSize { 0 }; AnimPoseVec _relativeDefaultPoses; AnimPoseVec _absoluteDefaultPoses; diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index 04b7952ddb..b5b27c3a24 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -69,7 +69,7 @@ void AnimationReader::run() { if (urlValid) { // Parse the FBX directly from the QNetworkReply - FBXGeometry::Pointer fbxgeo; + HFMGeometry::Pointer fbxgeo; if (_url.path().toLower().endsWith(".fbx")) { fbxgeo.reset(readFBX(_data, QVariantHash(), _url.path())); } else { @@ -100,40 +100,40 @@ QStringList Animation::getJointNames() const { } QStringList names; if (_geometry) { - foreach (const FBXJoint& joint, _geometry->joints) { + foreach (const HFMJoint& joint, _geometry->joints) { names.append(joint.name); } } return names; } -QVector Animation::getFrames() const { +QVector Animation::getFrames() const { if (QThread::currentThread() != thread()) { - QVector result; + QVector result; BLOCKING_INVOKE_METHOD(const_cast(this), "getFrames", - Q_RETURN_ARG(QVector, result)); + Q_RETURN_ARG(QVector, result)); return result; } if (_geometry) { return _geometry->animationFrames; } else { - return QVector(); + return QVector(); } } -const QVector& Animation::getFramesReference() const { +const QVector& Animation::getFramesReference() const { return _geometry->animationFrames; } void Animation::downloadFinished(const QByteArray& data) { // parse the animation/fbx file on a background thread. AnimationReader* animationReader = new AnimationReader(_url, data); - connect(animationReader, SIGNAL(onSuccess(FBXGeometry::Pointer)), SLOT(animationParseSuccess(FBXGeometry::Pointer))); + connect(animationReader, SIGNAL(onSuccess(HFMGeometry::Pointer)), SLOT(animationParseSuccess(HFMGeometry::Pointer))); connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString))); QThreadPool::globalInstance()->start(animationReader); } -void Animation::animationParseSuccess(FBXGeometry::Pointer geometry) { +void Animation::animationParseSuccess(HFMGeometry::Pointer geometry) { qCDebug(animation) << "Animation parse success" << _url.toDisplayString(); diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h index 483350e2b5..302f23a4e7 100644 --- a/libraries/animation/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -66,7 +66,7 @@ public: QString getType() const override { return "Animation"; } - const FBXGeometry& getGeometry() const { return *_geometry; } + const HFMGeometry& getGeometry() const { return *_geometry; } virtual bool isLoaded() const override; @@ -80,20 +80,20 @@ public: * @function AnimationObject.getFrames * @returns {FBXAnimationFrame[]} */ - Q_INVOKABLE QVector getFrames() const; + Q_INVOKABLE QVector getFrames() const; - const QVector& getFramesReference() const; + const QVector& getFramesReference() const; protected: virtual void downloadFinished(const QByteArray& data) override; protected slots: - void animationParseSuccess(FBXGeometry::Pointer geometry); + void animationParseSuccess(HFMGeometry::Pointer geometry); void animationParseError(int error, QString str); private: - FBXGeometry::Pointer _geometry; + HFMGeometry::Pointer _geometry; }; /// Reads geometry in a worker thread. @@ -105,7 +105,7 @@ public: virtual void run() override; signals: - void onSuccess(FBXGeometry::Pointer geometry); + void onSuccess(HFMGeometry::Pointer geometry); void onError(int error, QString str); private: diff --git a/libraries/animation/src/AnimationObject.cpp b/libraries/animation/src/AnimationObject.cpp index 7f0f35b104..bcbf497199 100644 --- a/libraries/animation/src/AnimationObject.cpp +++ b/libraries/animation/src/AnimationObject.cpp @@ -19,17 +19,17 @@ QStringList AnimationObject::getJointNames() const { return qscriptvalue_cast(thisObject())->getJointNames(); } -QVector AnimationObject::getFrames() const { +QVector AnimationObject::getFrames() const { return qscriptvalue_cast(thisObject())->getFrames(); } QVector AnimationFrameObject::getRotations() const { - return qscriptvalue_cast(thisObject()).rotations; + return qscriptvalue_cast(thisObject()).rotations; } void registerAnimationTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType >(engine); - engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( + qScriptRegisterSequenceMetaType >(engine); + engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( new AnimationFrameObject(), QScriptEngine::ScriptOwnership)); engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( new AnimationObject(), QScriptEngine::ScriptOwnership)); diff --git a/libraries/animation/src/AnimationObject.h b/libraries/animation/src/AnimationObject.h index aa69e78ceb..83880ed2ab 100644 --- a/libraries/animation/src/AnimationObject.h +++ b/libraries/animation/src/AnimationObject.h @@ -23,13 +23,13 @@ class QScriptEngine; class AnimationObject : public QObject, protected QScriptable { Q_OBJECT Q_PROPERTY(QStringList jointNames READ getJointNames) - Q_PROPERTY(QVector frames READ getFrames) + Q_PROPERTY(QVector frames READ getFrames) public: Q_INVOKABLE QStringList getJointNames() const; - Q_INVOKABLE QVector getFrames() const; + Q_INVOKABLE QVector getFrames() const; }; /// Scriptable wrapper for animation frames. diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 33b9569758..2641be92da 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -260,7 +260,7 @@ void Rig::destroyAnimGraph() { _rightEyeJointChildren.clear(); } -void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset) { +void Rig::initJointStates(const HFMGeometry& geometry, const glm::mat4& modelOffset) { _geometryOffset = AnimPose(geometry.offset); _invGeometryOffset = _geometryOffset.inverse(); _geometryToRigTransform = modelOffset * geometry.offset; @@ -307,7 +307,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.rightEyeJointIndex); } -void Rig::reset(const FBXGeometry& geometry) { +void Rig::reset(const HFMGeometry& geometry) { _geometryOffset = AnimPose(geometry.offset); _invGeometryOffset = _geometryOffset.inverse(); _animSkeleton = std::make_shared(geometry); @@ -1253,7 +1253,7 @@ const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = { // returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose. // if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward // such that it lies on the surface of the kdop. -static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const FBXJointShapeInfo& shapeInfo, glm::vec3& displacementOut) { +static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) { // transform point into local space of jointShape. glm::vec3 localPoint = shapePose.inverse().xformPoint(point); @@ -1299,8 +1299,8 @@ static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& sh } } -glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) const { +glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const { glm::vec3 position = handPosition; glm::vec3 displacement; int hipsJoint = indexOfJoint("Hips"); @@ -1349,8 +1349,8 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJoin void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated, bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt, const AnimPose& leftHandPose, const AnimPose& rightHandPose, - const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo, + const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo, const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) { const bool ENABLE_POLE_VECTORS = true; @@ -2008,7 +2008,7 @@ void Rig::computeExternalPoses(const glm::mat4& modelOffsetMat) { } void Rig::computeAvatarBoundingCapsule( - const FBXGeometry& geometry, + const HFMGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& localOffsetOut) const { @@ -2041,7 +2041,7 @@ void Rig::computeAvatarBoundingCapsule( // from the head to the hips when computing the rest of the bounding capsule. int index = indexOfJoint("Head"); while (index != -1) { - const FBXJointShapeInfo& shapeInfo = geometry.joints.at(index).shapeInfo; + const HFMJointShapeInfo& shapeInfo = geometry.joints.at(index).shapeInfo; AnimPose pose = _animSkeleton->getAbsoluteDefaultPose(index); if (shapeInfo.points.size() > 0) { for (auto& point : shapeInfo.points) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 7a090bd7bd..61e8672972 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -86,10 +86,10 @@ public: AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space uint8_t secondaryControllerFlags[NumSecondaryControllerTypes]; bool isTalking; - FBXJointShapeInfo hipsShapeInfo; - FBXJointShapeInfo spineShapeInfo; - FBXJointShapeInfo spine1ShapeInfo; - FBXJointShapeInfo spine2ShapeInfo; + HFMJointShapeInfo hipsShapeInfo; + HFMJointShapeInfo spineShapeInfo; + HFMJointShapeInfo spine1ShapeInfo; + HFMJointShapeInfo spine2ShapeInfo; }; struct EyeParameters { @@ -122,8 +122,8 @@ public: void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreRoleAnimation(const QString& role); - void initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset); - void reset(const FBXGeometry& geometry); + void initJointStates(const HFMGeometry& geometry, const glm::mat4& modelOffset); + void reset(const HFMGeometry& geometry); bool jointStatesEmpty(); int getJointStateCount() const; int indexOfJoint(const QString& jointName) const; @@ -210,7 +210,7 @@ public: void copyJointsFromJointData(const QVector& jointDataVec); void computeExternalPoses(const glm::mat4& modelOffsetMat); - void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; + void computeAvatarBoundingCapsule(const HFMGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; void setEnableInverseKinematics(bool enable); void setEnableAnimations(bool enable); @@ -245,8 +245,8 @@ protected: void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated, bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt, const AnimPose& leftHandPose, const AnimPose& rightHandPose, - const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo, + const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo, const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix); void updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose, @@ -257,8 +257,8 @@ protected: bool calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int oppositeArmIndex, glm::vec3& poleVector) const; glm::vec3 calculateKneePoleVector(int footJointIndex, int kneeJoint, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const; - glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, - const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) const; + glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, + const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const; AnimPose _modelOffset; // model to rig space diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 06dd498af3..7c7c113d31 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1311,7 +1311,7 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { case CAMERA_MATRIX_INDEX: { glm::quat rotation; if (_skeletonModel && _skeletonModel->isActive()) { - int headJointIndex = _skeletonModel->getFBXGeometry().headJointIndex; + int headJointIndex = _skeletonModel->getHFMGeometry().headJointIndex; if (headJointIndex >= 0) { _skeletonModel->getAbsoluteJointRotationInRigFrame(headJointIndex, rotation); } @@ -1360,7 +1360,7 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { case CAMERA_MATRIX_INDEX: { glm::vec3 translation; if (_skeletonModel && _skeletonModel->isActive()) { - int headJointIndex = _skeletonModel->getFBXGeometry().headJointIndex; + int headJointIndex = _skeletonModel->getHFMGeometry().headJointIndex; if (headJointIndex >= 0) { _skeletonModel->getAbsoluteJointTranslationInRigFrame(headJointIndex, translation); } @@ -1416,7 +1416,7 @@ void Avatar::withValidJointIndicesCache(std::function const& worker) con if (!_modelJointsCached) { _modelJointIndicesCache.clear(); if (_skeletonModel && _skeletonModel->isActive()) { - _modelJointIndicesCache = _skeletonModel->getFBXGeometry().jointIndices; + _modelJointIndicesCache = _skeletonModel->getHFMGeometry().jointIndices; _modelJointsCached = true; } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 1ec58fd704..a41cff528b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -54,7 +54,7 @@ void SkeletonModel::setTextures(const QVariantMap& textures) { } void SkeletonModel::initJointStates() { - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); _rig.initJointStates(geometry, modelOffset); @@ -96,7 +96,7 @@ void SkeletonModel::initJointStates() { // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { assert(!_owningAvatar->isMyAvatar()); - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); Head* head = _owningAvatar->getHead(); @@ -259,22 +259,22 @@ bool SkeletonModel::getRightShoulderPosition(glm::vec3& position) const { } bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const { - return isActive() && getJointPositionInWorldFrame(getFBXGeometry().headJointIndex, headPosition); + return isActive() && getJointPositionInWorldFrame(getHFMGeometry().headJointIndex, headPosition); } bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPositionInWorldFrame(getFBXGeometry().neckJointIndex, neckPosition); + return isActive() && getJointPositionInWorldFrame(getHFMGeometry().neckJointIndex, neckPosition); } bool SkeletonModel::getLocalNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPosition(getFBXGeometry().neckJointIndex, neckPosition); + return isActive() && getJointPosition(getHFMGeometry().neckJointIndex, neckPosition); } bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { return false; } - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); if (getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) && getJointPosition(geometry.rightEyeJointIndex, secondEyePosition)) { @@ -330,7 +330,7 @@ void SkeletonModel::computeBoundingShape() { return; } - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) { // rootJointIndex == -1 if the avatar model has no skeleton return; @@ -369,7 +369,7 @@ void SkeletonModel::renderBoundingCollisionShapes(RenderArgs* args, gpu::Batch& } bool SkeletonModel::hasSkeleton() { - return isActive() ? getFBXGeometry().rootJointIndex != -1 : false; + return isActive() ? getHFMGeometry().rootJointIndex != -1 : false; } void SkeletonModel::onInvalidate() { diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index d82fce7412..6c533a5941 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -41,10 +41,10 @@ public: void updateAttitude(const glm::quat& orientation); /// Returns the index of the left hand joint, or -1 if not found. - int getLeftHandJointIndex() const { return isActive() ? getFBXGeometry().leftHandJointIndex : -1; } + int getLeftHandJointIndex() const { return isActive() ? getHFMGeometry().leftHandJointIndex : -1; } /// Returns the index of the right hand joint, or -1 if not found. - int getRightHandJointIndex() const { return isActive() ? getFBXGeometry().rightHandJointIndex : -1; } + int getRightHandJointIndex() const { return isActive() ? getHFMGeometry().rightHandJointIndex : -1; } bool getLeftGrabPosition(glm::vec3& position) const; bool getRightGrabPosition(glm::vec3& position) const; diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index b90082d969..0b76c275d4 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -206,7 +206,7 @@ void FBXBaker::importScene() { } #endif - _geometry = reader.extractFBXGeometry({}, _modelURL.toString()); + _geometry = reader.extractHFMGeometry({}, _modelURL.toString()); _textureContentMap = reader._textureContent; } @@ -329,7 +329,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { for (FBXNode& textureChild : object->children) { if (textureChild.name == "RelativeFilename") { - QString fbxTextureFileName { textureChild.properties.at(0).toString() }; + QString hfmTextureFileName { textureChild.properties.at(0).toString() }; // grab the ID for this texture so we can figure out the // texture type from the loaded materials @@ -337,7 +337,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { auto textureType = textureTypes[textureID]; // Compress the texture information and return the new filename to be added into the FBX scene - auto bakedTextureFile = compressTexture(fbxTextureFileName, textureType); + auto bakedTextureFile = compressTexture(hfmTextureFileName, textureType); // If no errors or warnings have occurred during texture compression add the filename to the FBX scene if (!bakedTextureFile.isNull()) { diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 9d41209d4c..8edaf91c79 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -53,7 +53,7 @@ private: void rewriteAndBakeSceneModels(); void rewriteAndBakeSceneTextures(); - FBXGeometry* _geometry; + HFMGeometry* _geometry; QHash _textureNameMatchCount; QHash _remappedTexturePaths; diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index 75e10c54ab..ca352cebae 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -75,7 +75,7 @@ void ModelBaker::abort() { } } -bool ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback) { +bool ModelBaker::compressMesh(HFMMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback) { if (mesh.wasCompressed) { handleError("Cannot re-bake a file that contains compressed mesh"); return false; diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h index 1fd77ab761..cda4478b1d 100644 --- a/libraries/baking/src/ModelBaker.h +++ b/libraries/baking/src/ModelBaker.h @@ -39,7 +39,7 @@ public: const QString& bakedOutputDirectory, const QString& originalOutputDirectory = ""); virtual ~ModelBaker(); - bool compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback = nullptr); + bool compressMesh(HFMMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback = nullptr); QString compressTexture(QString textureFileName, image::TextureUsage::Type = image::TextureUsage::Type::DEFAULT_TEXTURE); virtual void setWasAborted(bool wasAborted) override; diff --git a/libraries/baking/src/OBJBaker.cpp b/libraries/baking/src/OBJBaker.cpp index cf62bc4fa8..e9130e3fbd 100644 --- a/libraries/baking/src/OBJBaker.cpp +++ b/libraries/baking/src/OBJBaker.cpp @@ -153,7 +153,7 @@ void OBJBaker::bakeOBJ() { checkIfTexturesFinished(); } -void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { +void OBJBaker::createFBXNodeTree(FBXNode& rootNode, HFMGeometry& geometry) { // Generating FBX Header Node FBXNode headerNode; headerNode.name = FBX_HEADER_EXTENSION; @@ -235,7 +235,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { auto size = meshParts.size(); for (int i = 0; i < size; i++) { QString material = meshParts[i].materialID; - FBXMaterial currentMaterial = geometry.materials[material]; + HFMMaterial currentMaterial = geometry.materials[material]; if (!currentMaterial.albedoTexture.filename.isEmpty() || !currentMaterial.specularTexture.filename.isEmpty()) { auto textureID = nextNodeID(); _mapTextureMaterial.emplace_back(textureID, i); @@ -325,12 +325,12 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { } // Set properties for material nodes -void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry) { +void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMGeometry& geometry) { auto materialID = nextNodeID(); _materialIDs.push_back(materialID); materialNode.properties = { materialID, material, MESH }; - FBXMaterial currentMaterial = geometry.materials[material]; + HFMMaterial currentMaterial = geometry.materials[material]; // Setting the hierarchy: Material -> Properties70 -> P -> Properties FBXNode properties70Node; diff --git a/libraries/baking/src/OBJBaker.h b/libraries/baking/src/OBJBaker.h index 8e49692d35..875a500129 100644 --- a/libraries/baking/src/OBJBaker.h +++ b/libraries/baking/src/OBJBaker.h @@ -39,8 +39,8 @@ private slots: private: void loadOBJ(); - void createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry); - void setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry); + void createFBXNodeTree(FBXNode& rootNode, HFMGeometry& geometry); + void setMaterialNodeProperties(FBXNode& materialNode, QString material, HFMGeometry& geometry); NodeID nextNodeID() { return _nodeID++; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index c6337dc872..c36f60600f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -268,7 +268,7 @@ EntityItemProperties RenderableModelEntityItem::getProperties(const EntityProper if (model->isLoaded()) { // TODO: improve naturalDimensions in the future, // for now we've added this hack for setting natural dimensions of models - Extents meshExtents = model->getFBXGeometry().getUnscaledMeshExtents(); + Extents meshExtents = model->getHFMGeometry().getUnscaledMeshExtents(); properties.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); properties.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); } @@ -403,7 +403,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // should never fall in here when collision model not fully loaded // TODO: assert that all geometries exist and are loaded //assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); - const FBXGeometry& collisionGeometry = _compoundShapeResource->getFBXGeometry(); + const HFMGeometry& collisionGeometry = _compoundShapeResource->getHFMGeometry(); ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); pointCollection.clear(); @@ -411,15 +411,15 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect // to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case. - foreach (const FBXMesh& mesh, collisionGeometry.meshes) { + foreach (const HFMMesh& mesh, collisionGeometry.meshes) { // each meshPart is a convex hull - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { pointCollection.push_back(QVector()); ShapeInfo::PointList& pointsInPart = pointCollection[i]; // run through all the triangles and (uniquely) add each point to the hull uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -440,7 +440,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // run through all the quads and (uniquely) add each point to the hull numIndices = (uint32_t)meshPart.quadIndices.size(); - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % QUAD_STRIDE == 0); numIndices -= numIndices % QUAD_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -478,7 +478,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // to the visual model and apply them to the collision model (without regard for the // collision model's extents). - glm::vec3 scaleToFit = dimensions / model->getFBXGeometry().getUnscaledMeshExtents().size(); + glm::vec3 scaleToFit = dimensions / model->getHFMGeometry().getUnscaledMeshExtents().size(); // multiply each point by scale before handing the point-set off to the physics engine. // also determine the extents of the collision model. glm::vec3 registrationOffset = dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()); @@ -498,14 +498,14 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // compute meshPart local transforms QVector localTransforms; - const FBXGeometry& fbxGeometry = model->getFBXGeometry(); - int numFbxMeshes = fbxGeometry.meshes.size(); + const HFMGeometry& hfmGeometry = model->getHFMGeometry(); + int numHFMMeshes = hfmGeometry.meshes.size(); int totalNumVertices = 0; glm::mat4 invRegistraionOffset = glm::translate(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); - for (int i = 0; i < numFbxMeshes; i++) { - const FBXMesh& mesh = fbxGeometry.meshes.at(i); + for (int i = 0; i < numHFMMeshes; i++) { + const HFMMesh& mesh = hfmGeometry.meshes.at(i); if (mesh.clusters.size() > 0) { - const FBXCluster& cluster = mesh.clusters.at(0); + const HFMCluster& cluster = mesh.clusters.at(0); auto jointMatrix = model->getRig().getJointTransform(cluster.jointIndex); // we backtranslate by the registration offset so we can apply that offset to the shapeInfo later localTransforms.push_back(invRegistraionOffset * jointMatrix * cluster.inverseBindMatrix); @@ -524,10 +524,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { std::vector> meshes; if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { - auto& fbxMeshes = _compoundShapeResource->getFBXGeometry().meshes; - meshes.reserve(fbxMeshes.size()); - for (auto& fbxMesh : fbxMeshes) { - meshes.push_back(fbxMesh._mesh); + auto& hfmMeshes = _compoundShapeResource->getHFMGeometry().meshes; + meshes.reserve(hfmMeshes.size()); + for (auto& hfmMesh : hfmMeshes) { + meshes.push_back(hfmMesh._mesh); } } else { meshes = model->getGeometry()->getMeshes(); @@ -594,7 +594,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { while (partItr != parts.cend()) { auto numIndices = partItr->_numIndices; if (partItr->_topology == graphics::Mesh::TRIANGLES) { - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices % TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -605,7 +605,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { ++indexItr; } } else if (partItr->_topology == graphics::Mesh::TRIANGLE_STRIP) { - // TODO: resurrect assert after we start sanitizing FBXMesh higher up + // TODO: resurrect assert after we start sanitizing HFMMesh higher up //assert(numIndices > 2); uint32_t approxNumIndices = TRIANGLE_STRIDE * numIndices; @@ -651,7 +651,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { std::set uniqueIndices; auto numIndices = partItr->_numIndices; if (partItr->_topology == graphics::Mesh::TRIANGLES) { - // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up + // TODO: assert rather than workaround after we start sanitizing HFMMesh higher up //assert(numIndices% TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -662,7 +662,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { ++indexItr; } } else if (partItr->_topology == graphics::Mesh::TRIANGLE_STRIP) { - // TODO: resurrect assert after we start sanitizing FBXMesh higher up + // TODO: resurrect assert after we start sanitizing HFMMesh higher up //assert(numIndices > TRIANGLE_STRIDE - 1); auto indexItr = indices.cbegin() + partItr->_startIndex; @@ -755,7 +755,7 @@ int RenderableModelEntityItem::avatarJointIndex(int modelJointIndex) { bool RenderableModelEntityItem::contains(const glm::vec3& point) const { auto model = getModel(); if (EntityItem::contains(point) && model && _compoundShapeResource && _compoundShapeResource->isLoaded()) { - return _compoundShapeResource->getFBXGeometry().convexHullContains(worldToEntity(point)); + return _compoundShapeResource->getHFMGeometry().convexHullContains(worldToEntity(point)); } return false; @@ -1135,7 +1135,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { QVector jointsData; - const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy + const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy int frameCount = frames.size(); if (frameCount <= 0) { return; @@ -1160,10 +1160,10 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { } QStringList animationJointNames = _animation->getGeometry().getJointNames(); - auto& fbxJoints = _animation->getGeometry().joints; + auto& hfmJoints = _animation->getGeometry().joints; - auto& originalFbxJoints = _model->getFBXGeometry().joints; - auto& originalFbxIndices = _model->getFBXGeometry().jointIndices; + auto& originalHFMJoints = _model->getHFMGeometry().joints; + auto& originalHFMIndices = _model->getHFMGeometry().jointIndices; bool allowTranslation = entity->getAnimationAllowTranslation(); @@ -1182,22 +1182,22 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { translationMat = glm::translate(translations[index]); } } else if (index < animationJointNames.size()) { - QString jointName = fbxJoints[index].name; // Pushing this here so its not done on every entity, with the exceptions of those allowing for translation - if (originalFbxIndices.contains(jointName)) { + QString jointName = hfmJoints[index].name; // Pushing this here so its not done on every entity, with the exceptions of those allowing for translation + if (originalHFMIndices.contains(jointName)) { // Making sure the joint names exist in the original model the animation is trying to apply onto. If they do, then remap and get it's translation. - int remappedIndex = originalFbxIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. - translationMat = glm::translate(originalFbxJoints[remappedIndex].translation); + int remappedIndex = originalHFMIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual. + translationMat = glm::translate(originalHFMJoints[remappedIndex].translation); } } glm::mat4 rotationMat; if (index < rotations.size()) { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * rotations[index] * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * rotations[index] * hfmJoints[index].postRotation); } else { - rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * fbxJoints[index].postRotation); + rotationMat = glm::mat4_cast(hfmJoints[index].preRotation * hfmJoints[index].postRotation); } - glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform * - rotationMat * fbxJoints[index].postTransform); + glm::mat4 finalMat = (translationMat * hfmJoints[index].preTransform * + rotationMat * hfmJoints[index].postTransform); auto& jointData = jointsData[j]; jointData.translation = extractTranslation(finalMat); jointData.translationSet = true; diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index fdebb16bc8..8051dbafea 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -70,8 +70,8 @@ public: }; -/// A single blendshape extracted from an FBX document. -class FBXBlendshape { +/// A single blendshape. +class HFMBlendshape { public: QVector indices; QVector vertices; @@ -79,19 +79,19 @@ public: QVector tangents; }; -struct FBXJointShapeInfo { - // same units and frame as FBXJoint.translation +struct HFMJointShapeInfo { + // same units and frame as HFMJoint.translation glm::vec3 avgPoint; std::vector dots; std::vector points; std::vector debugLines; }; -/// A single joint (transformation node) extracted from an FBX document. -class FBXJoint { +/// A single joint (transformation node). +class HFMJoint { public: - FBXJointShapeInfo shapeInfo; + HFMJointShapeInfo shapeInfo; QVector freeLineage; bool isFree; int parentIndex; @@ -126,8 +126,8 @@ public: }; -/// A single binding to a joint in an FBX document. -class FBXCluster { +/// A single binding to a joint. +class HFMCluster { public: int jointIndex; @@ -137,8 +137,8 @@ public: const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; -/// A texture map in an FBX document. -class FBXTexture { +/// A texture map. +class HFMTexture { public: QString id; QString name; @@ -156,7 +156,7 @@ public: }; /// A single part of a mesh (with the same material). -class FBXMeshPart { +class HFMMeshPart { public: QVector quadIndices; // original indices from the FBX mesh @@ -166,10 +166,10 @@ public: QString materialID; }; -class FBXMaterial { +class HFMMaterial { public: - FBXMaterial() {}; - FBXMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, + HFMMaterial() {}; + HFMMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, float shininess, float opacity) : diffuseColor(diffuseColor), specularColor(specularColor), @@ -203,17 +203,17 @@ public: QString shadingModel; graphics::MaterialPointer _material; - FBXTexture normalTexture; - FBXTexture albedoTexture; - FBXTexture opacityTexture; - FBXTexture glossTexture; - FBXTexture roughnessTexture; - FBXTexture specularTexture; - FBXTexture metallicTexture; - FBXTexture emissiveTexture; - FBXTexture occlusionTexture; - FBXTexture scatteringTexture; - FBXTexture lightmapTexture; + HFMTexture normalTexture; + HFMTexture albedoTexture; + HFMTexture opacityTexture; + HFMTexture glossTexture; + HFMTexture roughnessTexture; + HFMTexture specularTexture; + HFMTexture metallicTexture; + HFMTexture emissiveTexture; + HFMTexture occlusionTexture; + HFMTexture scatteringTexture; + HFMTexture lightmapTexture; glm::vec2 lightmapParams{ 0.0f, 1.0f }; @@ -232,10 +232,10 @@ public: }; /// A single mesh (with optional blendshapes) extracted from an FBX document. -class FBXMesh { +class HFMMesh { public: - QVector parts; + QVector parts; QVector vertices; QVector normals; @@ -247,12 +247,12 @@ public: QVector clusterWeights; QVector originalIndices; - QVector clusters; + QVector clusters; Extents meshExtents; glm::mat4 modelTransform; - QVector blendshapes; + QVector blendshapes; unsigned int meshIndex; // the order the meshes appeared in the object file @@ -265,7 +265,7 @@ public: class ExtractedMesh { public: - FBXMesh mesh; + HFMMesh mesh; QMultiHash newIndices; QVector > blendshapeIndexMaps; QVector > partMaterialTextures; @@ -278,14 +278,14 @@ public: * @property {Vec3[]} translations */ /// A single animation frame extracted from an FBX document. -class FBXAnimationFrame { +class HFMAnimationFrame { public: QVector rotations; QVector translations; }; -/// A light in an FBX document. -class FBXLight { +/// A light. +class HFMLight { public: QString name; Transform transform; @@ -293,7 +293,7 @@ public: float fogValue; glm::vec3 color; - FBXLight() : + HFMLight() : name(), transform(), intensity(1.0f), @@ -302,26 +302,26 @@ public: {} }; -Q_DECLARE_METATYPE(FBXAnimationFrame) -Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(HFMAnimationFrame) +Q_DECLARE_METATYPE(QVector) /// A set of meshes extracted from an FBX document. -class FBXGeometry { +class HFMGeometry { public: - using Pointer = std::shared_ptr; + using Pointer = std::shared_ptr; QString originalURL; QString author; QString applicationName; ///< the name of the application that generated the model - QVector joints; + QVector joints; QHash jointIndices; ///< 1-based, so as to more easily detect missing indices bool hasSkeletonJoints; - QVector meshes; + QVector meshes; QVector scripts; - QHash materials; + QHash materials; glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file @@ -348,7 +348,7 @@ public: Extents bindExtents; Extents meshExtents; - QVector animationFrames; + QVector animationFrames; int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } QStringList getJointNames() const; @@ -368,7 +368,7 @@ public: QList blendshapeChannelNames; }; -Q_DECLARE_METATYPE(FBXGeometry) -Q_DECLARE_METATYPE(FBXGeometry::Pointer) +Q_DECLARE_METATYPE(HFMGeometry) +Q_DECLARE_METATYPE(HFMGeometry::Pointer) #endif // hifi_FBX_h_ diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index dd766f002c..df6abbfdf2 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -40,19 +40,19 @@ using namespace std; -int FBXGeometryPointerMetaTypeId = qRegisterMetaType(); +int HFMGeometryPointerMetaTypeId = qRegisterMetaType(); -QStringList FBXGeometry::getJointNames() const { +QStringList HFMGeometry::getJointNames() const { QStringList names; - foreach (const FBXJoint& joint, joints) { + foreach (const HFMJoint& joint, joints) { names.append(joint.name); } return names; } -bool FBXGeometry::hasBlendedMeshes() const { +bool HFMGeometry::hasBlendedMeshes() const { if (!meshes.isEmpty()) { - foreach (const FBXMesh& mesh, meshes) { + foreach (const HFMMesh& mesh, meshes) { if (!mesh.blendshapes.isEmpty()) { return true; } @@ -61,7 +61,7 @@ bool FBXGeometry::hasBlendedMeshes() const { return false; } -Extents FBXGeometry::getUnscaledMeshExtents() const { +Extents HFMGeometry::getUnscaledMeshExtents() const { const Extents& extents = meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which @@ -74,12 +74,12 @@ Extents FBXGeometry::getUnscaledMeshExtents() const { } // TODO: Move to graphics::Mesh when Sam's ready -bool FBXGeometry::convexHullContains(const glm::vec3& point) const { +bool HFMGeometry::convexHullContains(const glm::vec3& point) const { if (!getUnscaledMeshExtents().containsPoint(point)) { return false; } - auto checkEachPrimitive = [=](FBXMesh& mesh, QVector indices, int primitiveSize) -> bool { + auto checkEachPrimitive = [=](HFMMesh& mesh, QVector indices, int primitiveSize) -> bool { // Check whether the point is "behind" all the primitives. int verticesSize = mesh.vertices.size(); for (int j = 0; @@ -124,16 +124,16 @@ bool FBXGeometry::convexHullContains(const glm::vec3& point) const { return false; } -QString FBXGeometry::getModelNameOfMesh(int meshIndex) const { +QString HFMGeometry::getModelNameOfMesh(int meshIndex) const { if (meshIndicesToModelNames.contains(meshIndex)) { return meshIndicesToModelNames.value(meshIndex); } return QString(); } -int fbxGeometryMetaTypeId = qRegisterMetaType(); -int fbxAnimationFrameMetaTypeId = qRegisterMetaType(); -int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType >(); +int hfmGeometryMetaTypeId = qRegisterMetaType(); +int hfmAnimationFrameMetaTypeId = qRegisterMetaType(); +int hfmAnimationFrameVectorMetaTypeId = qRegisterMetaType >(); glm::vec3 parseVec3(const QString& string) { @@ -303,7 +303,7 @@ glm::mat4 getGlobalTransform(const QMultiMap& _connectionParen class ExtractedBlendshape { public: QString id; - FBXBlendshape blendshape; + HFMBlendshape blendshape; }; void printNode(const FBXNode& node, int indentLevel) { @@ -346,8 +346,8 @@ void appendModelIDs(const QString& parentID, const QMultiMap& } } -FBXBlendshape extractBlendshape(const FBXNode& object) { - FBXBlendshape blendshape; +HFMBlendshape extractBlendshape(const FBXNode& object) { + HFMBlendshape blendshape; foreach (const FBXNode& data, object.children) { if (data.name == "Indexes") { blendshape.indices = FBXReader::getIntVector(data); @@ -362,9 +362,9 @@ FBXBlendshape extractBlendshape(const FBXNode& object) { return blendshape; } -using IndexAccessor = std::function; +using IndexAccessor = std::function; -static void setTangents(const FBXMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex, +static void setTangents(const HFMMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex, const QVector& vertices, const QVector& normals, QVector& tangents) { glm::vec3 vertex[2]; glm::vec3 normal; @@ -381,14 +381,14 @@ static void setTangents(const FBXMesh& mesh, const IndexAccessor& vertexAccessor } } -static void createTangents(const FBXMesh& mesh, bool generateFromTexCoords, +static void createTangents(const HFMMesh& mesh, bool generateFromTexCoords, const QVector& vertices, const QVector& normals, QVector& tangents, IndexAccessor accessor) { // if we have a normal map (and texture coordinates), we must compute tangents if (generateFromTexCoords && !mesh.texCoords.isEmpty()) { tangents.resize(vertices.size()); - foreach(const FBXMeshPart& part, mesh.parts) { + foreach(const HFMMeshPart& part, mesh.parts) { for (int i = 0; i < part.quadIndices.size(); i += 4) { setTangents(mesh, accessor, part.quadIndices.at(i), part.quadIndices.at(i + 1), vertices, normals, tangents); setTangents(mesh, accessor, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2), vertices, normals, tangents); @@ -403,27 +403,27 @@ static void createTangents(const FBXMesh& mesh, bool generateFromTexCoords, setTangents(mesh, accessor, part.triangleIndices.at(i + 2), part.triangleIndices.at(i), vertices, normals, tangents); } if ((part.triangleIndices.size() % 3) != 0) { - qCDebug(modelformat) << "Error in extractFBXGeometry part.triangleIndices.size() is not divisible by three "; + qCDebug(modelformat) << "Error in extractHFMGeometry part.triangleIndices.size() is not divisible by three "; } } } } -static void _createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape); +static void _createBlendShapeTangents(HFMMesh& mesh, bool generateFromTexCoords, HFMBlendshape& blendShape); -void FBXMesh::createBlendShapeTangents(bool generateTangents) { +void HFMMesh::createBlendShapeTangents(bool generateTangents) { for (auto& blendShape : blendshapes) { _createBlendShapeTangents(*this, generateTangents, blendShape); } } -void FBXMesh::createMeshTangents(bool generateFromTexCoords) { - FBXMesh& mesh = *this; +void HFMMesh::createMeshTangents(bool generateFromTexCoords) { + HFMMesh& mesh = *this; // This is the only workaround I've found to trick the compiler into understanding that mesh.tangents isn't // const in the lambda function. auto& tangents = mesh.tangents; createTangents(mesh, generateFromTexCoords, mesh.vertices, mesh.normals, mesh.tangents, - [&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { + [&](const HFMMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { outVertices[0] = mesh.vertices[firstIndex]; outVertices[1] = mesh.vertices[secondIndex]; outNormal = mesh.normals[firstIndex]; @@ -431,7 +431,7 @@ void FBXMesh::createMeshTangents(bool generateFromTexCoords) { }); } -static void _createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape) { +static void _createBlendShapeTangents(HFMMesh& mesh, bool generateFromTexCoords, HFMBlendshape& blendShape) { // Create lookup to get index in blend shape from vertex index in mesh std::vector reverseIndices; reverseIndices.resize(mesh.vertices.size()); @@ -443,7 +443,7 @@ static void _createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, } createTangents(mesh, generateFromTexCoords, blendShape.vertices, blendShape.normals, blendShape.tangents, - [&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { + [&](const HFMMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { const auto index1 = reverseIndices[firstIndex]; const auto index2 = reverseIndices[secondIndex]; @@ -481,7 +481,7 @@ void addBlendshapes(const ExtractedBlendshape& extracted, const QList& blendshapeIndexMap = extractedMesh.blendshapeIndexMaps[index.first]; for (int i = 0; i < extracted.blendshape.indices.size(); i++) { int oldIndex = extracted.blendshape.indices.at(i); @@ -539,7 +539,7 @@ public: QVector values; }; -bool checkMaterialsHaveTextures(const QHash& materials, +bool checkMaterialsHaveTextures(const QHash& materials, const QHash& textureFilenames, const QMultiMap& _connectionChildMap) { foreach (const QString& materialID, materials.keys()) { foreach (const QString& childID, _connectionChildMap.values(materialID)) { @@ -569,8 +569,8 @@ int matchTextureUVSetToAttributeChannel(const QString& texUVSetName, const QHash } -FBXLight extractLight(const FBXNode& object) { - FBXLight light; +HFMLight extractLight(const FBXNode& object) { + HFMLight light; foreach (const FBXNode& subobject, object.children) { QString childname = QString(subobject.name); if (subobject.name == "Properties70") { @@ -615,7 +615,7 @@ QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) { return filepath.mid(filepath.lastIndexOf('/') + 1); } -FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) { +HFMGeometry* FBXReader::extractHFMGeometry(const QVariantHash& mapping, const QString& url) { const FBXNode& node = _rootNode; QMap meshes; QHash modelIDsToNames; @@ -636,7 +636,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QHash yComponents; QHash zComponents; - std::map lights; + std::map lights; QVariantHash joints = mapping.value("joint").toHash(); QString jointEyeLeftName = processID(getString(joints.value("jointEyeLeft", "jointEyeLeft"))); @@ -689,8 +689,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS #if defined(DEBUG_FBXREADER) int unknown = 0; #endif - FBXGeometry* geometryPtr = new FBXGeometry; - FBXGeometry& geometry = *geometryPtr; + HFMGeometry* geometryPtr = new HFMGeometry; + HFMGeometry& geometry = *geometryPtr; geometry.originalURL = url; @@ -944,7 +944,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS lightprop = vprop.toString(); } - FBXLight light = extractLight(object); + HFMLight light = extractLight(object); } } } else { @@ -1102,7 +1102,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS _textureContent.insert(filepath, content); } } else if (object.name == "Material") { - FBXMaterial material; + HFMMaterial material; material.name = (object.properties.at(1).toString()); foreach (const FBXNode& subobject, object.children) { bool properties = false; @@ -1255,7 +1255,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS #endif } material.materialID = getID(object.properties); - _fbxMaterials.insert(material.materialID, material); + _hfmMaterials.insert(material.materialID, material); } else if (object.name == "NodeAttribute") { @@ -1276,7 +1276,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (!attributetype.isEmpty()) { if (attributetype == "Light") { - FBXLight light = extractLight(object); + HFMLight light = extractLight(object); lights[attribID] = light; } } @@ -1345,7 +1345,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QString parentID = getID(connection.properties, 2); ooChildToParent.insert(childID, parentID); if (!hifiGlobalNodeID.isEmpty() && (parentID == hifiGlobalNodeID)) { - std::map< QString, FBXLight >::iterator lightIt = lights.find(childID); + std::map< QString, HFMLight >::iterator lightIt = lights.find(childID); if (lightIt != lights.end()) { _lightmapLevel = (*lightIt).second.intensity; if (_lightmapLevel <= 0.0f) { @@ -1504,7 +1504,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS frameCount = qMax(frameCount, curve.values.size()); } for (int i = 0; i < frameCount; i++) { - FBXAnimationFrame frame; + HFMAnimationFrame frame; frame.rotations.resize(modelIDs.size()); frame.translations.resize(modelIDs.size()); geometry.animationFrames.append(frame); @@ -1515,7 +1515,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS geometry.hasSkeletonJoints = false; foreach (const QString& modelID, modelIDs) { const FBXModel& model = models[modelID]; - FBXJoint joint; + HFMJoint joint; joint.isFree = freeJoints.contains(model.name); joint.parentIndex = model.parentIndex; @@ -1553,7 +1553,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS joint.distanceToParent = 0.0f; } else { - const FBXJoint& parentJoint = geometry.joints.at(joint.parentIndex); + const HFMJoint& parentJoint = geometry.joints.at(joint.parentIndex); joint.transform = parentJoint.transform * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation) * parentJoint.inverseDefaultRotation; @@ -1631,7 +1631,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS geometry.meshExtents.reset(); // Create the Material Library - consolidateFBXMaterials(mapping); + consolidateHFMMaterials(mapping); // We can't allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image // Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key @@ -1643,7 +1643,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // 33 - 128 textures --> 512 // etc... QSet uniqueTextures; - for (auto& material : _fbxMaterials) { + for (auto& material : _hfmMaterials) { material.getTextureNames(uniqueTextures); } int numTextures = uniqueTextures.size(); @@ -1659,15 +1659,15 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } while (numTextureThreshold < numTextures && maxWidth > MIN_MIP_TEXTURE_WIDTH); qCDebug(modelformat) << "Capped square texture width =" << maxWidth << "for model" << url << "with" << numTextures << "textures"; - for (auto& material : _fbxMaterials) { + for (auto& material : _hfmMaterials) { material.setMaxNumPixelsPerTexture(maxWidth * maxWidth); } } #endif - geometry.materials = _fbxMaterials; + geometry.materials = _hfmMaterials; // see if any materials have texture children - bool materialsHaveTextures = checkMaterialsHaveTextures(_fbxMaterials, _textureFilenames, _connectionChildMap); + bool materialsHaveTextures = checkMaterialsHaveTextures(_hfmMaterials, _textureFilenames, _connectionChildMap); for (QMap::iterator it = meshes.begin(); it != meshes.end(); it++) { ExtractedMesh& extracted = it.value(); @@ -1698,13 +1698,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS for (int i = children.size() - 1; i >= 0; i--) { const QString& childID = children.at(i); - if (_fbxMaterials.contains(childID)) { + if (_hfmMaterials.contains(childID)) { // the pure material associated with this part - FBXMaterial material = _fbxMaterials.value(childID); + HFMMaterial material = _hfmMaterials.value(childID); for (int j = 0; j < extracted.partMaterialTextures.size(); j++) { if (extracted.partMaterialTextures.at(j).first == materialIndex) { - FBXMeshPart& part = extracted.mesh.parts[j]; + HFMMeshPart& part = extracted.mesh.parts[j]; part.materialID = material.materialID; generateTangents |= material.needTangentSpace(); } @@ -1713,7 +1713,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS materialIndex++; } else if (_textureFilenames.contains(childID)) { - FBXTexture texture = getTexture(childID); + HFMTexture texture = getTexture(childID); for (int j = 0; j < extracted.partMaterialTextures.size(); j++) { int partTexture = extracted.partMaterialTextures.at(j).second; if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) { @@ -1736,34 +1736,34 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (!clusters.contains(clusterID)) { continue; } - FBXCluster fbxCluster; + HFMCluster hfmCluster; const Cluster& cluster = clusters[clusterID]; clusterIDs.append(clusterID); // see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion // of skinning information in FBX QString jointID = _connectionChildMap.value(clusterID); - fbxCluster.jointIndex = modelIDs.indexOf(jointID); - if (fbxCluster.jointIndex == -1) { + hfmCluster.jointIndex = modelIDs.indexOf(jointID); + if (hfmCluster.jointIndex == -1) { qCDebug(modelformat) << "Joint not in model list: " << jointID; - fbxCluster.jointIndex = 0; + hfmCluster.jointIndex = 0; } - fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; + hfmCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; // slam bottom row to (0, 0, 0, 1), we KNOW this is not a perspective matrix and // sometimes floating point fuzz can be introduced after the inverse. - fbxCluster.inverseBindMatrix[0][3] = 0.0f; - fbxCluster.inverseBindMatrix[1][3] = 0.0f; - fbxCluster.inverseBindMatrix[2][3] = 0.0f; - fbxCluster.inverseBindMatrix[3][3] = 1.0f; + hfmCluster.inverseBindMatrix[0][3] = 0.0f; + hfmCluster.inverseBindMatrix[1][3] = 0.0f; + hfmCluster.inverseBindMatrix[2][3] = 0.0f; + hfmCluster.inverseBindMatrix[3][3] = 1.0f; - fbxCluster.inverseBindTransform = Transform(fbxCluster.inverseBindMatrix); + hfmCluster.inverseBindTransform = Transform(hfmCluster.inverseBindMatrix); - extracted.mesh.clusters.append(fbxCluster); + extracted.mesh.clusters.append(hfmCluster); // override the bind rotation with the transform link - FBXJoint& joint = geometry.joints[fbxCluster.jointIndex]; + HFMJoint& joint = geometry.joints[hfmCluster.jointIndex]; joint.inverseBindRotation = glm::inverse(extractRotation(cluster.transformLink)); joint.bindTransform = cluster.transformLink; joint.bindTransformFoundInCluster = true; @@ -1776,7 +1776,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // if we don't have a skinned joint, parent to the model itself if (extracted.mesh.clusters.isEmpty()) { - FBXCluster cluster; + HFMCluster cluster; cluster.jointIndex = modelIDs.indexOf(modelID); if (cluster.jointIndex == -1) { qCDebug(modelformat) << "Model not in model list: " << modelID; @@ -1786,7 +1786,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } // whether we're skinned depends on how many clusters are attached - const FBXCluster& firstFBXCluster = extracted.mesh.clusters.at(0); + const HFMCluster& firstHFMCluster = extracted.mesh.clusters.at(0); glm::mat4 inverseModelTransform = glm::inverse(modelTransform); if (clusterIDs.size() > 1) { // this is a multi-mesh joint @@ -1799,9 +1799,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS for (int i = 0; i < clusterIDs.size(); i++) { QString clusterID = clusterIDs.at(i); const Cluster& cluster = clusters[clusterID]; - const FBXCluster& fbxCluster = extracted.mesh.clusters.at(i); - int jointIndex = fbxCluster.jointIndex; - FBXJoint& joint = geometry.joints[jointIndex]; + const HFMCluster& hfmCluster = extracted.mesh.clusters.at(i); + int jointIndex = hfmCluster.jointIndex; + HFMJoint& joint = geometry.joints[jointIndex]; glm::mat4 transformJointToMesh = inverseModelTransform * joint.bindTransform; glm::vec3 boneEnd = extractTranslation(transformJointToMesh); glm::vec3 boneBegin = boneEnd; @@ -1881,8 +1881,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } else { // this is a single-mesh joint - int jointIndex = firstFBXCluster.jointIndex; - FBXJoint& joint = geometry.joints[jointIndex]; + int jointIndex = firstHFMCluster.jointIndex; + HFMJoint& joint = geometry.joints[jointIndex]; // transform cluster vertices to joint-frame and save for later glm::mat4 meshToJoint = glm::inverse(joint.bindTransform) * modelTransform; @@ -1924,7 +1924,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // now that all joints have been scanned compute a k-Dop bounding volume of mesh for (int i = 0; i < geometry.joints.size(); ++i) { - FBXJoint& joint = geometry.joints[i]; + HFMJoint& joint = geometry.joints[i]; // NOTE: points are in joint-frame ShapeVertices& points = shapeVertices.at(i); @@ -1994,13 +1994,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS return geometryPtr; } -FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +HFMGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); return readFBX(&buffer, mapping, url, loadLightmaps, lightmapLevel); } -FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { +HFMGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { FBXReader reader; reader._rootNode = FBXReader::parseFBX(device); reader._loadLightmaps = loadLightmaps; @@ -2008,5 +2008,5 @@ FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QStri qCDebug(modelformat) << "Reading FBX: " << url; - return reader.extractFBXGeometry(mapping, url); + return reader.extractHFMGeometry(mapping, url); } diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index c391ea6647..f95ba7fe73 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -36,11 +36,11 @@ class FBXNode; /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +HFMGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); +HFMGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f); class TextureParam { public: @@ -103,20 +103,20 @@ class ExtractedMesh; class FBXReader { public: - FBXGeometry* _fbxGeometry; + HFMGeometry* _hfmGeometry; FBXNode _rootNode; static FBXNode parseFBX(QIODevice* device); - FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url); + HFMGeometry* extractHFMGeometry(const QVariantHash& mapping, const QString& url); static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate = true); QHash meshes; - static void buildModelMesh(FBXMesh& extractedMesh, const QString& url); + static void buildModelMesh(HFMMesh& extractedMesh, const QString& url); static glm::vec3 normalizeDirForPacking(const glm::vec3& dir); - FBXTexture getTexture(const QString& textureID); + HFMTexture getTexture(const QString& textureID); QHash _textureNames; // Hashes the original RelativeFilename of textures @@ -142,9 +142,9 @@ public: QHash ambientFactorTextures; QHash occlusionTextures; - QHash _fbxMaterials; + QHash _hfmMaterials; - void consolidateFBXMaterials(const QVariantHash& mapping); + void consolidateHFMMaterials(const QVariantHash& mapping); bool _loadLightmaps = true; float _lightmapOffset = 0.0f; diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp index d5902962e5..ff1de30b97 100644 --- a/libraries/fbx/src/FBXReader_Material.cpp +++ b/libraries/fbx/src/FBXReader_Material.cpp @@ -27,7 +27,7 @@ #include "ModelFormatLogging.h" -void FBXMaterial::getTextureNames(QSet& textureList) const { +void HFMMaterial::getTextureNames(QSet& textureList) const { if (!normalTexture.isNull()) { textureList.insert(normalTexture.name); } @@ -63,7 +63,7 @@ void FBXMaterial::getTextureNames(QSet& textureList) const { } } -void FBXMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) { +void HFMMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) { normalTexture.maxNumPixels = maxNumPixels; albedoTexture.maxNumPixels = maxNumPixels; opacityTexture.maxNumPixels = maxNumPixels; @@ -77,12 +77,12 @@ void FBXMaterial::setMaxNumPixelsPerTexture(int maxNumPixels) { lightmapTexture.maxNumPixels = maxNumPixels; } -bool FBXMaterial::needTangentSpace() const { +bool HFMMaterial::needTangentSpace() const { return !normalTexture.isNull(); } -FBXTexture FBXReader::getTexture(const QString& textureID) { - FBXTexture texture; +HFMTexture FBXReader::getTexture(const QString& textureID) { + HFMTexture texture; const QByteArray& filepath = _textureFilepaths.value(textureID); texture.content = _textureContent.value(filepath); @@ -123,7 +123,7 @@ FBXTexture FBXReader::getTexture(const QString& textureID) { return texture; } -void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { +void FBXReader::consolidateHFMMaterials(const QVariantHash& mapping) { QString materialMapString = mapping.value("materialMap").toString(); QJsonDocument materialMapDocument = QJsonDocument::fromJson(materialMapString.toUtf8()); @@ -133,16 +133,16 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { qCDebug(modelformat) << "fbx Material Map found but did not produce valid JSON:" << materialMapString; } } - for (QHash::iterator it = _fbxMaterials.begin(); it != _fbxMaterials.end(); it++) { - FBXMaterial& material = (*it); + for (QHash::iterator it = _hfmMaterials.begin(); it != _hfmMaterials.end(); it++) { + HFMMaterial& material = (*it); // Maya is the exporting the shading model and we are trying to use it bool isMaterialLambert = (material.shadingModel.toLower() == "lambert"); // the pure material associated with this part bool detectDifferentUVs = false; - FBXTexture diffuseTexture; - FBXTexture diffuseFactorTexture; + HFMTexture diffuseTexture; + HFMTexture diffuseFactorTexture; QString diffuseTextureID = diffuseTextures.value(material.materialID); QString diffuseFactorTextureID = diffuseFactorTextures.value(material.materialID); @@ -169,7 +169,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity()); } - FBXTexture transparentTexture; + HFMTexture transparentTexture; QString transparentTextureID = transparentTextures.value(material.materialID); // If PBS Material, systematically bind the albedo texture as transparency texture and check for the alpha channel if (material.isPBSMaterial) { @@ -181,7 +181,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (transparentTexture.texcoordSet != 0) || (!transparentTexture.transform.isIdentity()); } - FBXTexture normalTexture; + HFMTexture normalTexture; QString bumpTextureID = bumpTextures.value(material.materialID); QString normalTextureID = normalTextures.value(material.materialID); if (!normalTextureID.isNull()) { @@ -198,7 +198,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (normalTexture.texcoordSet != 0) || (!normalTexture.transform.isIdentity()); } - FBXTexture specularTexture; + HFMTexture specularTexture; QString specularTextureID = specularTextures.value(material.materialID); if (!specularTextureID.isNull()) { specularTexture = getTexture(specularTextureID); @@ -206,7 +206,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { material.specularTexture = specularTexture; } - FBXTexture metallicTexture; + HFMTexture metallicTexture; QString metallicTextureID = metallicTextures.value(material.materialID); if (!metallicTextureID.isNull()) { metallicTexture = getTexture(metallicTextureID); @@ -214,7 +214,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { material.metallicTexture = metallicTexture; } - FBXTexture roughnessTexture; + HFMTexture roughnessTexture; QString roughnessTextureID = roughnessTextures.value(material.materialID); if (!roughnessTextureID.isNull()) { roughnessTexture = getTexture(roughnessTextureID); @@ -222,7 +222,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (roughnessTexture.texcoordSet != 0) || (!roughnessTexture.transform.isIdentity()); } - FBXTexture shininessTexture; + HFMTexture shininessTexture; QString shininessTextureID = shininessTextures.value(material.materialID); if (!shininessTextureID.isNull()) { shininessTexture = getTexture(shininessTextureID); @@ -230,7 +230,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { detectDifferentUVs |= (shininessTexture.texcoordSet != 0) || (!shininessTexture.transform.isIdentity()); } - FBXTexture emissiveTexture; + HFMTexture emissiveTexture; QString emissiveTextureID = emissiveTextures.value(material.materialID); if (!emissiveTextureID.isNull()) { emissiveTexture = getTexture(emissiveTextureID); @@ -245,7 +245,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { } } - FBXTexture occlusionTexture; + HFMTexture occlusionTexture; QString occlusionTextureID = occlusionTextures.value(material.materialID); if (occlusionTextureID.isNull()) { // 2nd chance @@ -265,7 +265,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { lightmapParams.x = _lightmapOffset; lightmapParams.y = _lightmapLevel; - FBXTexture ambientTexture; + HFMTexture ambientTexture; QString ambientTextureID = ambientTextures.value(material.materialID); if (ambientTextureID.isNull()) { // 2nd chance @@ -326,7 +326,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { if (materialOptions.contains("scatteringMap")) { QByteArray scatteringMap = materialOptions.value("scatteringMap").toVariant().toByteArray(); - material.scatteringTexture = FBXTexture(); + material.scatteringTexture = HFMTexture(); material.scatteringTexture.name = material.name + ".scatteringMap"; material.scatteringTexture.filename = scatteringMap; } diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index c9b004c3a8..e098aff99a 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -42,9 +42,9 @@ using vec2h = glm::tvec2; -#define FBX_PACK_COLORS 1 +#define HFM_PACK_COLORS 1 -#if FBX_PACK_COLORS +#if HFM_PACK_COLORS using ColorType = glm::uint32; #define FBX_COLOR_ELEMENT gpu::Element::COLOR_RGBA_32 #else @@ -469,7 +469,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn QPair materialTexture(materialID, 0); - // grab or setup the FBXMeshPart for the part this face belongs to + // grab or setup the HFMMeshPart for the part this face belongs to int& partIndexPlusOne = materialTextureParts[materialTexture]; if (partIndexPlusOne == 0) { data.extracted.partMaterialTextures.append(materialTexture); @@ -478,7 +478,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn } // give the mesh part this index - FBXMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; + HFMMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; part.triangleIndices.append(firstCorner.value()); part.triangleIndices.append(dracoFace[1].value()); part.triangleIndices.append(dracoFace[2].value()); @@ -511,7 +511,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); partIndex = data.extracted.mesh.parts.size(); } - FBXMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; + HFMMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; if (endIndex - beginIndex == 4) { appendIndex(data, part.quadIndices, beginIndex++, deduplicate); @@ -565,9 +565,9 @@ glm::vec3 FBXReader::normalizeDirForPacking(const glm::vec3& dir) { return dir; } -void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { +void FBXReader::buildModelMesh(HFMMesh& extractedMesh, const QString& url) { unsigned int totalSourceIndices = 0; - foreach(const FBXMeshPart& part, extractedMesh.parts) { + foreach(const HFMMeshPart& part, extractedMesh.parts) { totalSourceIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size()); } @@ -583,17 +583,17 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { return; } - FBXMesh& fbxMesh = extractedMesh; + HFMMesh& hfmMesh = extractedMesh; graphics::MeshPointer mesh(new graphics::Mesh()); int numVerts = extractedMesh.vertices.size(); - if (!fbxMesh.normals.empty() && fbxMesh.tangents.empty()) { + if (!hfmMesh.normals.empty() && hfmMesh.tangents.empty()) { // Fill with a dummy value to force tangents to be present if there are normals - fbxMesh.tangents.reserve(fbxMesh.normals.size()); - std::fill_n(std::back_inserter(fbxMesh.tangents), fbxMesh.normals.size(), Vectors::UNIT_X); + hfmMesh.tangents.reserve(hfmMesh.normals.size()); + std::fill_n(std::back_inserter(hfmMesh.tangents), hfmMesh.normals.size(), Vectors::UNIT_X); } // Same thing with blend shapes - for (auto& blendShape : fbxMesh.blendshapes) { + for (auto& blendShape : hfmMesh.blendshapes) { if (!blendShape.normals.empty() && blendShape.tangents.empty()) { // Fill with a dummy value to force tangents to be present if there are normals blendShape.tangents.reserve(blendShape.normals.size()); @@ -609,8 +609,8 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Normal and tangent are always there together packed in normalized xyz32bits word (times 2) const auto normalElement = FBX_NORMAL_ELEMENT; - const int normalsSize = fbxMesh.normals.size() * normalElement.getSize(); - const int tangentsSize = fbxMesh.tangents.size() * normalElement.getSize(); + const int normalsSize = hfmMesh.normals.size() * normalElement.getSize(); + const int tangentsSize = hfmMesh.tangents.size() * normalElement.getSize(); // If there are normals then there should be tangents assert(normalsSize <= tangentsSize); if (tangentsSize > normalsSize) { @@ -620,22 +620,22 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Color attrib const auto colorElement = FBX_COLOR_ELEMENT; - const int colorsSize = fbxMesh.colors.size() * colorElement.getSize(); + const int colorsSize = hfmMesh.colors.size() * colorElement.getSize(); // Texture coordinates are stored in 2 half floats const auto texCoordsElement = gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV); - const int texCoordsSize = fbxMesh.texCoords.size() * texCoordsElement.getSize(); - const int texCoords1Size = fbxMesh.texCoords1.size() * texCoordsElement.getSize(); + const int texCoordsSize = hfmMesh.texCoords.size() * texCoordsElement.getSize(); + const int texCoords1Size = hfmMesh.texCoords1.size() * texCoordsElement.getSize(); // Support for 4 skinning clusters: // 4 Indices are uint8 ideally, uint16 if more than 256. - const auto clusterIndiceElement = (fbxMesh.clusters.size() < UINT8_MAX ? gpu::Element(gpu::VEC4, gpu::UINT8, gpu::XYZW) : gpu::Element(gpu::VEC4, gpu::UINT16, gpu::XYZW)); + const auto clusterIndiceElement = (hfmMesh.clusters.size() < UINT8_MAX ? gpu::Element(gpu::VEC4, gpu::UINT8, gpu::XYZW) : gpu::Element(gpu::VEC4, gpu::UINT16, gpu::XYZW)); // 4 Weights are normalized 16bits const auto clusterWeightElement = gpu::Element(gpu::VEC4, gpu::NUINT16, gpu::XYZW); // Cluster indices and weights must be the same sizes const int NUM_CLUSTERS_PER_VERT = 4; - const int numVertClusters = (fbxMesh.clusterIndices.size() == fbxMesh.clusterWeights.size() ? fbxMesh.clusterIndices.size() / NUM_CLUSTERS_PER_VERT : 0); + const int numVertClusters = (hfmMesh.clusterIndices.size() == hfmMesh.clusterWeights.size() ? hfmMesh.clusterIndices.size() / NUM_CLUSTERS_PER_VERT : 0); const int clusterIndicesSize = numVertClusters * clusterIndiceElement.getSize(); const int clusterWeightsSize = numVertClusters * clusterWeightElement.getSize(); @@ -660,9 +660,9 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { if (normalsSize > 0) { std::vector normalsAndTangents; - normalsAndTangents.reserve(fbxMesh.normals.size() + fbxMesh.tangents.size()); - for (auto normalIt = fbxMesh.normals.constBegin(), tangentIt = fbxMesh.tangents.constBegin(); - normalIt != fbxMesh.normals.constEnd(); + normalsAndTangents.reserve(hfmMesh.normals.size() + hfmMesh.tangents.size()); + for (auto normalIt = hfmMesh.normals.constBegin(), tangentIt = hfmMesh.tangents.constBegin(); + normalIt != hfmMesh.normals.constEnd(); ++normalIt, ++tangentIt) { #if FBX_PACK_NORMALS const auto normal = normalizeDirForPacking(*normalIt); @@ -681,24 +681,24 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Pack colors if (colorsSize > 0) { -#if FBX_PACK_COLORS +#if HFM_PACK_COLORS std::vector colors; - colors.reserve(fbxMesh.colors.size()); - for (const auto& color : fbxMesh.colors) { + colors.reserve(hfmMesh.colors.size()); + for (const auto& color : hfmMesh.colors) { colors.push_back(glm::packUnorm4x8(glm::vec4(color, 1.0f))); } vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) colors.data()); #else - vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) fbxMesh.colors.constData()); + vertBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) hfmMesh.colors.constData()); #endif } // Pack Texcoords 0 and 1 (if exists) if (texCoordsSize > 0) { QVector texCoordData; - texCoordData.reserve(fbxMesh.texCoords.size()); - for (auto& texCoordVec2f : fbxMesh.texCoords) { + texCoordData.reserve(hfmMesh.texCoords.size()); + for (auto& texCoordVec2f : hfmMesh.texCoords) { vec2h texCoordVec2h; texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x); @@ -709,8 +709,8 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { } if (texCoords1Size > 0) { QVector texCoordData; - texCoordData.reserve(fbxMesh.texCoords1.size()); - for (auto& texCoordVec2f : fbxMesh.texCoords1) { + texCoordData.reserve(hfmMesh.texCoords1.size()); + for (auto& texCoordVec2f : hfmMesh.texCoords1) { vec2h texCoordVec2h; texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x); @@ -722,22 +722,22 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Clusters data if (clusterIndicesSize > 0) { - if (fbxMesh.clusters.size() < UINT8_MAX) { + if (hfmMesh.clusters.size() < UINT8_MAX) { // yay! we can fit the clusterIndices within 8-bits - int32_t numIndices = fbxMesh.clusterIndices.size(); + int32_t numIndices = hfmMesh.clusterIndices.size(); QVector clusterIndices; clusterIndices.resize(numIndices); for (int32_t i = 0; i < numIndices; ++i) { - assert(fbxMesh.clusterIndices[i] <= UINT8_MAX); - clusterIndices[i] = (uint8_t)(fbxMesh.clusterIndices[i]); + assert(hfmMesh.clusterIndices[i] <= UINT8_MAX); + clusterIndices[i] = (uint8_t)(hfmMesh.clusterIndices[i]); } vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) clusterIndices.constData()); } else { - vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) fbxMesh.clusterIndices.constData()); + vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) hfmMesh.clusterIndices.constData()); } } if (clusterWeightsSize > 0) { - vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) fbxMesh.clusterWeights.constData()); + vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) hfmMesh.clusterWeights.constData()); } @@ -856,7 +856,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { // Index and Part Buffers unsigned int totalIndices = 0; - foreach(const FBXMeshPart& part, extractedMesh.parts) { + foreach(const HFMMeshPart& part, extractedMesh.parts) { totalIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size()); } @@ -875,7 +875,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { if (extractedMesh.parts.size() > 1) { indexNum = 0; } - foreach(const FBXMeshPart& part, extractedMesh.parts) { + foreach(const HFMMeshPart& part, extractedMesh.parts) { graphics::Mesh::Part modelPart(indexNum, 0, 0, graphics::Mesh::TRIANGLES); if (part.quadTrianglesIndices.size()) { diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index b93dc3541b..7ee13c5cdf 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -697,7 +697,7 @@ glm::mat4 GLTFReader::getModelTransform(const GLTFNode& node) { return tmat; } -bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { +bool GLTFReader::buildGeometry(HFMGeometry& geometry, const QUrl& url) { //Build dependencies QVector> nodeDependencies(_file.nodes.size()); @@ -750,10 +750,10 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { for (int i = 0; i < materialIDs.size(); i++) { QString& matid = materialIDs[i]; - geometry.materials[matid] = FBXMaterial(); - FBXMaterial& fbxMaterial = geometry.materials[matid]; - fbxMaterial._material = std::make_shared(); - setFBXMaterial(fbxMaterial, _file.materials[i]); + geometry.materials[matid] = HFMMaterial(); + HFMMaterial& hfmMaterial = geometry.materials[matid]; + hfmMaterial._material = std::make_shared(); + setHFMMaterial(hfmMaterial, _file.materials[i]); } @@ -765,9 +765,9 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { if (node.defined["mesh"]) { qCDebug(modelformat) << "node_transforms" << node.transforms; foreach(auto &primitive, _file.meshes[node.mesh].primitives) { - geometry.meshes.append(FBXMesh()); - FBXMesh& mesh = geometry.meshes[geometry.meshes.size() - 1]; - FBXCluster cluster; + geometry.meshes.append(HFMMesh()); + HFMMesh& mesh = geometry.meshes[geometry.meshes.size() - 1]; + HFMCluster cluster; cluster.jointIndex = 0; cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, 0, 1, 0, 0, @@ -775,7 +775,7 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { 0, 0, 0, 1); mesh.clusters.append(cluster); - FBXMeshPart part = FBXMeshPart(); + HFMMeshPart part = HFMMeshPart(); int indicesAccessorIdx = primitive.indices; @@ -910,7 +910,7 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { return true; } -FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping, +HFMGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping, const QUrl& url, bool loadLightmaps, float lightmapLevel) { _url = url; @@ -924,12 +924,12 @@ FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping parseGLTF(model); //_file.dump(); - FBXGeometry* geometryPtr = new FBXGeometry(); - FBXGeometry& geometry = *geometryPtr; + HFMGeometry* geometryPtr = new HFMGeometry(); + HFMGeometry& geometry = *geometryPtr; buildGeometry(geometry, url); - //fbxDebugDump(geometry); + //hfmDebugDump(geometry); return geometryPtr; } @@ -997,8 +997,8 @@ QNetworkReply* GLTFReader::request(QUrl& url, bool isTest) { return netReply; // trying to sync later on. } -FBXTexture GLTFReader::getFBXTexture(const GLTFTexture& texture) { - FBXTexture fbxtex = FBXTexture(); +HFMTexture GLTFReader::getHFMTexture(const GLTFTexture& texture) { + HFMTexture fbxtex = HFMTexture(); fbxtex.texcoordSet = 0; if (texture.defined["source"]) { @@ -1014,7 +1014,7 @@ FBXTexture GLTFReader::getFBXTexture(const GLTFTexture& texture) { return fbxtex; } -void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& material) { +void GLTFReader::setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& material) { if (material.defined["name"]) { @@ -1029,17 +1029,17 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia } if (material.defined["emissiveTexture"]) { - fbxmat.emissiveTexture = getFBXTexture(_file.textures[material.emissiveTexture]); + fbxmat.emissiveTexture = getHFMTexture(_file.textures[material.emissiveTexture]); fbxmat.useEmissiveMap = true; } if (material.defined["normalTexture"]) { - fbxmat.normalTexture = getFBXTexture(_file.textures[material.normalTexture]); + fbxmat.normalTexture = getHFMTexture(_file.textures[material.normalTexture]); fbxmat.useNormalMap = true; } if (material.defined["occlusionTexture"]) { - fbxmat.occlusionTexture = getFBXTexture(_file.textures[material.occlusionTexture]); + fbxmat.occlusionTexture = getHFMTexture(_file.textures[material.occlusionTexture]); fbxmat.useOcclusionMap = true; } @@ -1050,14 +1050,14 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia fbxmat.metallic = material.pbrMetallicRoughness.metallicFactor; } if (material.pbrMetallicRoughness.defined["baseColorTexture"]) { - fbxmat.opacityTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); - fbxmat.albedoTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); + fbxmat.opacityTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); + fbxmat.albedoTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); fbxmat.useAlbedoMap = true; } if (material.pbrMetallicRoughness.defined["metallicRoughnessTexture"]) { - fbxmat.roughnessTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + fbxmat.roughnessTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); fbxmat.useRoughnessMap = true; - fbxmat.metallicTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + fbxmat.metallicTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); fbxmat.useMetallicMap = true; } if (material.pbrMetallicRoughness.defined["roughnessFactor"]) { @@ -1181,37 +1181,37 @@ void GLTFReader::retriangulate(const QVector& inIndices, const QVector materialMeshIdMap; - QVector fbxMeshParts; + QVector hfmMeshParts; for (int i = 0, meshPartCount = 0; i < mesh.parts.count(); i++, meshPartCount++) { - FBXMeshPart& meshPart = mesh.parts[i]; + HFMMeshPart& meshPart = mesh.parts[i]; FaceGroup faceGroup = faceGroups[meshPartCount]; bool specifiesUV = false; foreach(OBJFace face, faceGroup) { // Go through all of the OBJ faces and determine the number of different materials necessary (each different material will be a unique mesh). // NOTE (trent/mittens 3/30/17): this seems hardcore wasteful and is slowed down a bit by iterating through the face group twice, but it's the best way I've thought of to hack multi-material support in an OBJ into this pipeline. if (!materialMeshIdMap.contains(face.materialName)) { - // Create a new FBXMesh for this material mapping. + // Create a new HFMMesh for this material mapping. materialMeshIdMap.insert(face.materialName, materialMeshIdMap.count()); - fbxMeshParts.append(FBXMeshPart()); - FBXMeshPart& meshPartNew = fbxMeshParts.last(); + hfmMeshParts.append(HFMMeshPart()); + HFMMeshPart& meshPartNew = hfmMeshParts.last(); meshPartNew.quadIndices = QVector(meshPart.quadIndices); // Copy over quad indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. meshPartNew.quadTrianglesIndices = QVector(meshPart.quadTrianglesIndices); // Copy over quad triangulated indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. meshPartNew.triangleIndices = QVector(meshPart.triangleIndices); // Copy over triangle indices. @@ -745,14 +745,14 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m // clean up old mesh parts. int unmodifiedMeshPartCount = mesh.parts.count(); mesh.parts.clear(); - mesh.parts = QVector(fbxMeshParts); + mesh.parts = QVector(hfmMeshParts); for (int i = 0, meshPartCount = 0; i < unmodifiedMeshPartCount; i++, meshPartCount++) { FaceGroup faceGroup = faceGroups[meshPartCount]; // Now that each mesh has been created with its own unique material mappings, fill them with data (vertex data is duplicated, face data is not). foreach(OBJFace face, faceGroup) { - FBXMeshPart& meshPart = mesh.parts[materialMeshIdMap[face.materialName]]; + HFMMeshPart& meshPart = mesh.parts[materialMeshIdMap[face.materialName]]; glm::vec3 v0 = checked_at(vertices, face.vertexIndices[0]); glm::vec3 v1 = checked_at(vertices, face.vertexIndices[1]); @@ -824,7 +824,7 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m // Build the single mesh. FBXReader::buildModelMesh(mesh, url.toString()); - // fbxDebugDump(geometry); + // hfmDebugDump(geometry); } catch(const std::exception& e) { qCDebug(modelformat) << "OBJ reader fail: " << e.what(); } @@ -885,38 +885,38 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m if (!objMaterial.used) { continue; } - geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor, + geometry.materials[materialID] = HFMMaterial(objMaterial.diffuseColor, objMaterial.specularColor, objMaterial.emissiveColor, objMaterial.shininess, objMaterial.opacity); - FBXMaterial& fbxMaterial = geometry.materials[materialID]; - fbxMaterial.materialID = materialID; - fbxMaterial._material = std::make_shared(); - graphics::MaterialPointer modelMaterial = fbxMaterial._material; + HFMMaterial& hfmMaterial = geometry.materials[materialID]; + hfmMaterial.materialID = materialID; + hfmMaterial._material = std::make_shared(); + graphics::MaterialPointer modelMaterial = hfmMaterial._material; if (!objMaterial.diffuseTextureFilename.isEmpty()) { - fbxMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; + hfmMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; } if (!objMaterial.specularTextureFilename.isEmpty()) { - fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename; + hfmMaterial.specularTexture.filename = objMaterial.specularTextureFilename; } if (!objMaterial.emissiveTextureFilename.isEmpty()) { - fbxMaterial.emissiveTexture.filename = objMaterial.emissiveTextureFilename; + hfmMaterial.emissiveTexture.filename = objMaterial.emissiveTextureFilename; } if (!objMaterial.bumpTextureFilename.isEmpty()) { - fbxMaterial.normalTexture.filename = objMaterial.bumpTextureFilename; - fbxMaterial.normalTexture.isBumpmap = true; - fbxMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier; + hfmMaterial.normalTexture.filename = objMaterial.bumpTextureFilename; + hfmMaterial.normalTexture.isBumpmap = true; + hfmMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier; } if (!objMaterial.opacityTextureFilename.isEmpty()) { - fbxMaterial.opacityTexture.filename = objMaterial.opacityTextureFilename; + hfmMaterial.opacityTexture.filename = objMaterial.opacityTextureFilename; } - modelMaterial->setEmissive(fbxMaterial.emissiveColor); - modelMaterial->setAlbedo(fbxMaterial.diffuseColor); - modelMaterial->setMetallic(glm::length(fbxMaterial.specularColor)); - modelMaterial->setRoughness(graphics::Material::shininessToRoughness(fbxMaterial.shininess)); + modelMaterial->setEmissive(hfmMaterial.emissiveColor); + modelMaterial->setAlbedo(hfmMaterial.diffuseColor); + modelMaterial->setMetallic(glm::length(hfmMaterial.specularColor)); + modelMaterial->setRoughness(graphics::Material::shininessToRoughness(hfmMaterial.shininess)); bool applyTransparency = false; bool applyShininess = false; @@ -971,7 +971,7 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m } if (applyTransparency) { - fbxMaterial.opacity = std::max(fbxMaterial.opacity, ILLUMINATION_MODEL_MIN_OPACITY); + hfmMaterial.opacity = std::max(hfmMaterial.opacity, ILLUMINATION_MODEL_MIN_OPACITY); } if (applyShininess) { modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_SHININESS); @@ -985,18 +985,18 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m modelMaterial->setFresnel(glm::vec3(1.0f)); } - modelMaterial->setOpacity(fbxMaterial.opacity); + modelMaterial->setOpacity(hfmMaterial.opacity); } return geometryPtr; } -void fbxDebugDump(const FBXGeometry& fbxgeo) { - qCDebug(modelformat) << "---------------- fbxGeometry ----------------"; - qCDebug(modelformat) << " hasSkeletonJoints =" << fbxgeo.hasSkeletonJoints; - qCDebug(modelformat) << " offset =" << fbxgeo.offset; - qCDebug(modelformat) << " meshes.count() =" << fbxgeo.meshes.count(); - foreach (FBXMesh mesh, fbxgeo.meshes) { +void hfmDebugDump(const HFMGeometry& hfmgeo) { + qCDebug(modelformat) << "---------------- hfmGeometry ----------------"; + qCDebug(modelformat) << " hasSkeletonJoints =" << hfmgeo.hasSkeletonJoints; + qCDebug(modelformat) << " offset =" << hfmgeo.offset; + qCDebug(modelformat) << " meshes.count() =" << hfmgeo.meshes.count(); + foreach (HFMMesh mesh, hfmgeo.meshes) { qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.count(); qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); qCDebug(modelformat) << " normals.count() =" << mesh.normals.count(); @@ -1014,7 +1014,7 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << " meshExtents =" << mesh.meshExtents; qCDebug(modelformat) << " modelTransform =" << mesh.modelTransform; qCDebug(modelformat) << " parts.count() =" << mesh.parts.count(); - foreach (FBXMeshPart meshPart, mesh.parts) { + foreach (HFMMeshPart meshPart, mesh.parts) { qCDebug(modelformat) << " quadIndices.count() =" << meshPart.quadIndices.count(); qCDebug(modelformat) << " triangleIndices.count() =" << meshPart.triangleIndices.count(); /* @@ -1031,16 +1031,16 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { */ } qCDebug(modelformat) << " clusters.count() =" << mesh.clusters.count(); - foreach (FBXCluster cluster, mesh.clusters) { + foreach (HFMCluster cluster, mesh.clusters) { qCDebug(modelformat) << " jointIndex =" << cluster.jointIndex; qCDebug(modelformat) << " inverseBindMatrix =" << cluster.inverseBindMatrix; } } - qCDebug(modelformat) << " jointIndices =" << fbxgeo.jointIndices; - qCDebug(modelformat) << " joints.count() =" << fbxgeo.joints.count(); + qCDebug(modelformat) << " jointIndices =" << hfmgeo.jointIndices; + qCDebug(modelformat) << " joints.count() =" << hfmgeo.joints.count(); - foreach (FBXJoint joint, fbxgeo.joints) { + foreach (HFMJoint joint, hfmgeo.joints) { qCDebug(modelformat) << " isFree =" << joint.isFree; qCDebug(modelformat) << " freeLineage" << joint.freeLineage; qCDebug(modelformat) << " parentIndex" << joint.parentIndex; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index e432a3ea51..2eb039eba2 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -42,7 +42,7 @@ public: bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices, const QVector& vertexColors); // Return a set of one or more OBJFaces from this one, in which each is just a triangle. - // Even though FBXMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. + // Even though HFMMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. QVector triangulate(); private: void addFrom(const OBJFace* face, int index); @@ -54,7 +54,7 @@ public: } ; // Materials and references to material names can come in any order, and different mesh parts can refer to the same material. -// Therefore it would get pretty hacky to try to use FBXMeshPart to store these as we traverse the files. +// Therefore it would get pretty hacky to try to use HFMMeshPart to store these as we traverse the files. class OBJMaterial { public: float shininess; @@ -87,13 +87,13 @@ public: QString currentMaterialName; QHash materials; - FBXGeometry::Pointer readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); + HFMGeometry::Pointer readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); private: QUrl _url; QHash librariesSeen; - bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, + bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, HFMGeometry& geometry, float& scaleGuess, bool combineParts); void parseMaterialLibrary(QIODevice* device); void parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions); @@ -103,5 +103,5 @@ private: }; // What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility. -void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID); -void fbxDebugDump(const FBXGeometry& fbxgeo); +void setMeshPartDefaults(HFMMeshPart& meshPart, QString materialID); +void hfmDebugDump(const HFMGeometry& hfmgeo); diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index e96815d391..a950e1df3c 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -128,7 +128,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) { void GeometryMappingResource::onGeometryMappingLoaded(bool success) { if (success && _geometryResource) { - _fbxGeometry = _geometryResource->_fbxGeometry; + _hfmGeometry = _geometryResource->_hfmGeometry; _meshParts = _geometryResource->_meshParts; _meshes = _geometryResource->_meshes; _materials = _geometryResource->_materials; @@ -193,38 +193,38 @@ void GeometryReader::run() { _url.path().toLower().endsWith(".obj.gz") || _url.path().toLower().endsWith(".gltf"))) { - FBXGeometry::Pointer fbxGeometry; + HFMGeometry::Pointer hfmGeometry; if (_url.path().toLower().endsWith(".fbx")) { - fbxGeometry.reset(readFBX(_data, _mapping, _url.path())); - if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) { + hfmGeometry.reset(readFBX(_data, _mapping, _url.path())); + if (hfmGeometry->meshes.size() == 0 && hfmGeometry->joints.size() == 0) { throw QString("empty geometry, possibly due to an unsupported FBX version"); } } else if (_url.path().toLower().endsWith(".obj")) { - fbxGeometry = OBJReader().readOBJ(_data, _mapping, _combineParts, _url); + hfmGeometry = OBJReader().readOBJ(_data, _mapping, _combineParts, _url); } else if (_url.path().toLower().endsWith(".obj.gz")) { QByteArray uncompressedData; if (gunzip(_data, uncompressedData)){ - fbxGeometry = OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url); + hfmGeometry = OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url); } else { throw QString("failed to decompress .obj.gz"); } } else if (_url.path().toLower().endsWith(".gltf")) { std::shared_ptr glreader = std::make_shared(); - fbxGeometry.reset(glreader->readGLTF(_data, _mapping, _url)); - if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) { + hfmGeometry.reset(glreader->readGLTF(_data, _mapping, _url)); + if (hfmGeometry->meshes.size() == 0 && hfmGeometry->joints.size() == 0) { throw QString("empty geometry, possibly due to an unsupported GLTF version"); } } else { throw QString("unsupported format"); } - // Add scripts to fbxgeometry + // Add scripts to hfmGeometry if (!_mapping.value(SCRIPT_FIELD).isNull()) { QVariantList scripts = _mapping.values(SCRIPT_FIELD); for (auto &script : scripts) { - fbxGeometry->scripts.push_back(script.toString()); + hfmGeometry->scripts.push_back(script.toString()); } } @@ -234,7 +234,7 @@ void GeometryReader::run() { qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; } else { QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", - Q_ARG(FBXGeometry::Pointer, fbxGeometry)); + Q_ARG(HFMGeometry::Pointer, hfmGeometry)); } } else { throw QString("url is invalid"); @@ -262,7 +262,7 @@ public: virtual void downloadFinished(const QByteArray& data) override; protected: - Q_INVOKABLE void setGeometryDefinition(FBXGeometry::Pointer fbxGeometry); + Q_INVOKABLE void setGeometryDefinition(HFMGeometry::Pointer hfmGeometry); private: QVariantHash _mapping; @@ -277,13 +277,13 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { QThreadPool::globalInstance()->start(new GeometryReader(_self, _effectiveBaseURL, _mapping, data, _combineParts)); } -void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxGeometry) { +void GeometryDefinitionResource::setGeometryDefinition(HFMGeometry::Pointer hfmGeometry) { // Assume ownership of the geometry pointer - _fbxGeometry = fbxGeometry; + _hfmGeometry = hfmGeometry; // Copy materials QHash materialIDAtlas; - for (const FBXMaterial& material : _fbxGeometry->materials) { + for (const HFMMaterial& material : _hfmGeometry->materials) { materialIDAtlas[material.materialID] = _materials.size(); _materials.push_back(std::make_shared(material, _textureBaseUrl)); } @@ -291,11 +291,11 @@ void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxG std::shared_ptr meshes = std::make_shared(); std::shared_ptr parts = std::make_shared(); int meshID = 0; - for (const FBXMesh& mesh : _fbxGeometry->meshes) { + for (const HFMMesh& mesh : _hfmGeometry->meshes) { // Copy mesh pointers meshes->emplace_back(mesh._mesh); int partID = 0; - for (const FBXMeshPart& part : mesh.parts) { + for (const HFMMeshPart& part : mesh.parts) { // Construct local parts parts->push_back(std::make_shared(meshID, partID, (int)materialIDAtlas[part.materialID])); partID++; @@ -371,7 +371,7 @@ const QVariantMap Geometry::getTextures() const { // FIXME: The materials should only be copied when modified, but the Model currently caches the original Geometry::Geometry(const Geometry& geometry) { - _fbxGeometry = geometry._fbxGeometry; + _hfmGeometry = geometry._hfmGeometry; _meshes = geometry._meshes; _meshParts = geometry._meshParts; @@ -444,8 +444,8 @@ void GeometryResource::deleter() { } void GeometryResource::setTextures() { - if (_fbxGeometry) { - for (const FBXMaterial& material : _fbxGeometry->materials) { + if (_hfmGeometry) { + for (const HFMMaterial& material : _hfmGeometry->materials) { _materials.push_back(std::make_shared(material, _textureBaseUrl)); } } @@ -512,7 +512,7 @@ const QString& NetworkMaterial::getTextureName(MapChannel channel) { return NO_TEXTURE; } -QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& texture) { +QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const HFMTexture& texture) { if (texture.content.isEmpty()) { // External file: search relative to the baseUrl, in case filename is relative return baseUrl.resolved(QUrl(texture.filename)); @@ -529,22 +529,22 @@ QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& textu } } -graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, +graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture, image::TextureUsage::Type type, MapChannel channel) { if (baseUrl.isEmpty()) { return nullptr; } - const auto url = getTextureUrl(baseUrl, fbxTexture); - const auto texture = DependencyManager::get()->getTexture(url, type, fbxTexture.content, fbxTexture.maxNumPixels); - _textures[channel] = Texture { fbxTexture.name, texture }; + const auto url = getTextureUrl(baseUrl, hfmTexture); + const auto texture = DependencyManager::get()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels); + _textures[channel] = Texture { hfmTexture.name, texture }; auto map = std::make_shared(); if (texture) { map->setTextureSource(texture->_textureSource); } - map->setTextureTransform(fbxTexture.transform); + map->setTextureTransform(hfmTexture.transform); return map; } @@ -624,7 +624,7 @@ void NetworkMaterial::setLightmapMap(const QUrl& url) { } } -NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) : +NetworkMaterial::NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl) : graphics::Material(*material._material), _textures(MapChannel::NUM_MAP_CHANNELS) { diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 5cbe96ea03..2283c355d8 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -45,9 +45,9 @@ public: // Mutable, but must retain structure of vector using NetworkMaterials = std::vector>; - bool isGeometryLoaded() const { return (bool)_fbxGeometry; } + bool isGeometryLoaded() const { return (bool)_hfmGeometry; } - const FBXGeometry& getFBXGeometry() const { return *_fbxGeometry; } + const HFMGeometry& getHFMGeometry() const { return *_hfmGeometry; } const GeometryMeshes& getMeshes() const { return *_meshes; } const std::shared_ptr getShapeMaterial(int shapeID) const; @@ -62,7 +62,7 @@ protected: friend class GeometryMappingResource; // Shared across all geometries, constant throughout lifetime - std::shared_ptr _fbxGeometry; + std::shared_ptr _hfmGeometry; std::shared_ptr _meshes; std::shared_ptr _meshParts; @@ -94,7 +94,7 @@ protected: // Geometries may not hold onto textures while cached - that is for the texture cache // Instead, these methods clear and reset textures from the geometry when caching/loading - bool shouldSetTextures() const { return _fbxGeometry && _materials.empty(); } + bool shouldSetTextures() const { return _hfmGeometry && _materials.empty(); } void setTextures(); void resetTextures(); @@ -165,7 +165,7 @@ public: using MapChannel = graphics::Material::MapChannel; NetworkMaterial() : _textures(MapChannel::NUM_MAP_CHANNELS) {} - NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl); + NetworkMaterial(const HFMMaterial& material, const QUrl& textureBaseUrl); NetworkMaterial(const NetworkMaterial& material); void setAlbedoMap(const QUrl& url, bool useAlphaChannel); @@ -201,8 +201,8 @@ protected: private: // Helpers for the ctors - QUrl getTextureUrl(const QUrl& baseUrl, const FBXTexture& fbxTexture); - graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, + QUrl getTextureUrl(const QUrl& baseUrl, const HFMTexture& hfmTexture); + graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const HFMTexture& hfmTexture, image::TextureUsage::Type type, MapChannel channel); graphics::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 81a017a46d..31d6cef060 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -32,8 +32,8 @@ bool CauterizedModel::updateGeometry() { bool needsFullUpdate = Model::updateGeometry(); if (_isCauterized && needsFullUpdate) { assert(_cauterizeMeshStates.empty()); - const FBXGeometry& fbxGeometry = getFBXGeometry(); - foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + const HFMGeometry& hfmGeometry = getHFMGeometry(); + foreach (const HFMMesh& mesh, hfmGeometry.meshes) { Model::MeshState state; if (_useDualQuaternionSkinning) { state.clusterDualQuaternions.resize(mesh.clusters.size()); @@ -76,7 +76,7 @@ void CauterizedModel::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); - const FBXGeometry& fbxGeometry = getFBXGeometry(); + const HFMGeometry& hfmGeometry = getHFMGeometry(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -86,7 +86,7 @@ void CauterizedModel::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - initializeBlendshapes(fbxGeometry.meshes[i], i); + initializeBlendshapes(hfmGeometry.meshes[i], i); auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast(ptr); @@ -109,13 +109,13 @@ void CauterizedModel::updateClusterMatrices() { return; } _needsUpdateClusterMatrices = false; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); @@ -145,10 +145,10 @@ void CauterizedModel::updateClusterMatrices() { for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); if (_useDualQuaternionSkinning) { if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 4ebd92bb05..8e2541fdda 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -260,8 +260,8 @@ void ModelMeshPartPayload::initCache(const ModelPointer& model) { _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); _isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX); - const FBXGeometry& geometry = model->getFBXGeometry(); - const FBXMesh& mesh = geometry.meshes.at(_meshIndex); + const HFMGeometry& geometry = model->getHFMGeometry(); + const HFMMesh& mesh = geometry.meshes.at(_meshIndex); _isBlendShaped = !mesh.blendshapes.isEmpty(); _hasTangents = !mesh.tangents.isEmpty(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 53009e8bfa..65b3fef7c0 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -183,7 +183,7 @@ bool Model::shouldInvalidatePayloadShapeKey(int meshIndex) { return true; } - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); const auto& networkMeshes = getGeometry()->getMeshes(); // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown // to false to rebuild out mesh groups. @@ -278,7 +278,7 @@ void Model::setRenderItemsNeedUpdate() { void Model::reset() { if (isLoaded()) { - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); _rig.reset(geometry); emit rigReset(); emit rigReady(); @@ -295,13 +295,13 @@ bool Model::updateGeometry() { _needsReload = false; // TODO: should all Models have a valid _rig? - if (_rig.jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { + if (_rig.jointStatesEmpty() && getHFMGeometry().joints.size() > 0) { initJointStates(); assert(_meshStates.empty()); - const FBXGeometry& fbxGeometry = getFBXGeometry(); + const HFMGeometry& hfmGeometry = getHFMGeometry(); int i = 0; - foreach (const FBXMesh& mesh, fbxGeometry.meshes) { + foreach (const HFMMesh& mesh, hfmGeometry.meshes) { MeshState state; state.clusterDualQuaternions.resize(mesh.clusters.size()); state.clusterMatrices.resize(mesh.clusters.size()); @@ -319,7 +319,7 @@ bool Model::updateGeometry() { // virtual void Model::initJointStates() { - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); _rig.initJointStates(geometry, modelOffset); @@ -363,7 +363,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g int bestShapeID = 0; int bestSubMeshIndex = 0; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); if (!_triangleSetsValid) { calculateTriangleSets(geometry); } @@ -506,7 +506,7 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co int bestShapeID = 0; int bestSubMeshIndex = 0; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); if (!_triangleSetsValid) { calculateTriangleSets(geometry); } @@ -641,7 +641,7 @@ bool Model::convexHullContains(glm::vec3 point) { QMutexLocker locker(&_mutex); if (!_triangleSetsValid) { - calculateTriangleSets(getFBXGeometry()); + calculateTriangleSets(getHFMGeometry()); } // If we are inside the models box, then consider the submeshes... @@ -753,14 +753,14 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe } // update triangles for picking { - FBXGeometry geometry; + HFMGeometry geometry; for (const auto& newMesh : meshes) { - FBXMesh mesh; + HFMMesh mesh; mesh._mesh = newMesh.getMeshPointer(); mesh.vertices = buffer_helpers::mesh::attributeToVector(mesh._mesh, gpu::Stream::POSITION); int numParts = (int)newMesh.getMeshPointer()->getNumParts(); for (int partID = 0; partID < numParts; partID++) { - FBXMeshPart part; + HFMMeshPart part; part.triangleIndices = buffer_helpers::bufferToVector(mesh._mesh->getIndexBuffer(), "part.triangleIndices"); mesh.parts << part; } @@ -789,12 +789,12 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { return result; } - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); int numberOfMeshes = geometry.meshes.size(); int shapeID = 0; for (int i = 0; i < numberOfMeshes; i++) { - const FBXMesh& fbxMesh = geometry.meshes.at(i); - if (auto mesh = fbxMesh._mesh) { + const HFMMesh& hfmMesh = geometry.meshes.at(i); + if (auto mesh = hfmMesh._mesh) { result.append(mesh); int numParts = (int)mesh->getNumParts(); @@ -808,7 +808,7 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { return result; } -void Model::calculateTriangleSets(const FBXGeometry& geometry) { +void Model::calculateTriangleSets(const HFMGeometry& geometry) { PROFILE_RANGE(render, __FUNCTION__); int numberOfMeshes = geometry.meshes.size(); @@ -818,14 +818,14 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) { _modelSpaceMeshTriangleSets.resize(numberOfMeshes); for (int i = 0; i < numberOfMeshes; i++) { - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = geometry.meshes.at(i); const int numberOfParts = mesh.parts.size(); auto& meshTriangleSets = _modelSpaceMeshTriangleSets[i]; meshTriangleSets.resize(numberOfParts); for (int j = 0; j < numberOfParts; j++) { - const FBXMeshPart& part = mesh.parts.at(j); + const HFMMeshPart& part = mesh.parts.at(j); auto& partTriangleSet = meshTriangleSets[j]; @@ -1114,7 +1114,7 @@ Extents Model::getBindExtents() const { if (!isActive()) { return Extents(); } - const Extents& bindExtents = getFBXGeometry().bindExtents; + const Extents& bindExtents = getHFMGeometry().bindExtents; Extents scaledExtents = { bindExtents.minimum * _scale, bindExtents.maximum * _scale }; return scaledExtents; } @@ -1128,12 +1128,12 @@ Extents Model::getMeshExtents() const { if (!isActive()) { return Extents(); } - const Extents& extents = getFBXGeometry().meshExtents; + const Extents& extents = getHFMGeometry().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which // is captured in the offset matrix - glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); - glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + glm::vec3 minimum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum * _scale, maximum * _scale }; return scaledExtents; } @@ -1143,12 +1143,12 @@ Extents Model::getUnscaledMeshExtents() const { return Extents(); } - const Extents& extents = getFBXGeometry().meshExtents; + const Extents& extents = getHFMGeometry().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which // is captured in the offset matrix - glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); - glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + glm::vec3 minimum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(getHFMGeometry().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum, maximum }; return scaledExtents; @@ -1171,11 +1171,11 @@ void Model::setJointTranslation(int index, bool valid, const glm::vec3& translat } int Model::getParentJointIndex(int jointIndex) const { - return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).parentIndex : -1; + return (isActive() && jointIndex != -1) ? getHFMGeometry().joints.at(jointIndex).parentIndex : -1; } int Model::getLastFreeJointIndex(int jointIndex) const { - return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1; + return (isActive() && jointIndex != -1) ? getHFMGeometry().joints.at(jointIndex).freeLineage.last() : -1; } void Model::setTextures(const QVariantMap& textures) { @@ -1275,7 +1275,7 @@ QStringList Model::getJointNames() const { Q_RETURN_ARG(QStringList, result)); return result; } - return isActive() ? getFBXGeometry().getJointNames() : QStringList(); + return isActive() ? getHFMGeometry().getJointNames() : QStringList(); } void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) { @@ -1415,12 +1415,12 @@ void Model::updateClusterMatrices() { } _needsUpdateClusterMatrices = false; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); if (_useDualQuaternionSkinning) { auto jointPose = _rig.getJointPose(cluster.jointIndex); Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); @@ -1505,7 +1505,7 @@ void Model::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); - auto& fbxGeometry = getFBXGeometry(); + auto& hfmGeometry = getHFMGeometry(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -1515,7 +1515,7 @@ void Model::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - initializeBlendshapes(fbxGeometry.meshes[i], i); + initializeBlendshapes(hfmGeometry.meshes[i], i); _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); auto material = getGeometry()->getShapeMaterial(shapeID); _modelMeshMaterialNames.push_back(material ? material->getName() : ""); @@ -1600,7 +1600,7 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string class CollisionRenderGeometry : public Geometry { public: CollisionRenderGeometry(graphics::MeshPointer mesh) { - _fbxGeometry = std::make_shared(); + _hfmGeometry = std::make_shared(); std::shared_ptr meshes = std::make_shared(); meshes->push_back(mesh); _meshes = meshes; @@ -1656,9 +1656,9 @@ void Blender::run() { if (_model && _model->isLoaded()) { DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); int offset = 0; - auto meshes = _model->getFBXGeometry().meshes; + auto meshes = _model->getHFMGeometry().meshes; int meshIndex = 0; - foreach(const FBXMesh& mesh, meshes) { + foreach(const HFMMesh& mesh, meshes) { auto modelMeshBlendshapeOffsets = _model->_blendshapeOffsets.find(meshIndex++); if (mesh.blendshapes.isEmpty() || modelMeshBlendshapeOffsets == _model->_blendshapeOffsets.end()) { // Not blendshaped or not initialized @@ -1688,7 +1688,7 @@ void Blender::run() { } float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; - const FBXBlendshape& blendshape = mesh.blendshapes.at(i); + const HFMBlendshape& blendshape = mesh.blendshapes.at(i); tbb::parallel_for(tbb::blocked_range(0, blendshape.indices.size()), [&](const tbb::blocked_range& range) { for (auto j = range.begin(); j < range.end(); j++) { @@ -1731,7 +1731,7 @@ bool Model::maybeStartBlender() { return false; } -void Model::initializeBlendshapes(const FBXMesh& mesh, int index) { +void Model::initializeBlendshapes(const HFMMesh& mesh, int index) { if (mesh.blendshapes.empty()) { // mesh doesn't have blendshape, did we allocate one though ? if (_blendshapeOffsets.find(index) != _blendshapeOffsets.end()) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 71809821eb..db5625b3d8 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -185,7 +185,7 @@ public: /// Provided as a convenience, will crash if !isLoaded() // And so that getGeometry() isn't chained everywhere - const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return _renderGeometry->getFBXGeometry(); } + const HFMGeometry& getHFMGeometry() const { assert(isLoaded()); return _renderGeometry->getHFMGeometry(); } bool isActive() const { return isLoaded(); } @@ -450,7 +450,7 @@ protected: bool _overrideModelTransform { false }; bool _triangleSetsValid { false }; - void calculateTriangleSets(const FBXGeometry& geometry); + void calculateTriangleSets(const HFMGeometry& geometry); std::vector> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes virtual void createRenderItemSet(); @@ -506,7 +506,7 @@ protected: bool shouldInvalidatePayloadShapeKey(int meshIndex); - void initializeBlendshapes(const FBXMesh& mesh, int index); + void initializeBlendshapes(const HFMMesh& mesh, int index); private: float _loadingPriority { 0.0f }; diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 90015768d0..77b09caa1d 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -41,14 +41,14 @@ void SoftAttachmentModel::updateClusterMatrices() { _needsUpdateClusterMatrices = false; - const FBXGeometry& geometry = getFBXGeometry(); + const HFMGeometry& geometry = getHFMGeometry(); for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; - const FBXMesh& mesh = geometry.meshes.at(i); + const HFMMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { - const FBXCluster& cluster = mesh.clusters.at(j); + const HFMCluster& cluster = mesh.clusters.at(j); // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 4fb7ddd40b..f9857992bb 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -18,7 +18,7 @@ Script.include("/~/system/libraries/accountUtils.js"); Script.include("/~/system/libraries/connectionUtils.js"); var AppUi = Script.require('appUi'); - var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace";; +var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; // BEGIN AVATAR SELECTOR LOGIC var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; @@ -607,13 +607,13 @@ function notificationPollCallbackHistory(historyArray) { if (notificationCount > 0) { var message; if (!ui.notificationInitialCallbackMade[1]) { - message = "You have " + notificationCount + " unread wallet " + - "transaction" + (notificationCount === 1 ? "" : "s") + ". Open WALLET to see all activity."; + message = "You have " + notificationCount + " unread recent " + + "transaction" + (notificationCount === 1 ? "" : "s") + ". Open ASSETS to see all activity."; ui.notificationDisplayBanner(message); } else { for (var i = 0; i < notificationCount; i++) { message = '"' + (historyArray[i].message) + '" ' + - "Open WALLET to see all activity."; + "Open ASSETS to see all activity."; ui.notificationDisplayBanner(message); } } diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index ed8ed9f288..8bfd776971 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -527,7 +527,13 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { Window.location = "hifi://BankOfHighFidelity"; } break; - case 'checkout_openWallet': + case 'checkout_openRecentActivity': + ui.open(MARKETPLACE_WALLET_QML_PATH); + wireQmlEventBridge(true); + ui.tablet.sendToQml({ + method: 'checkout_openRecentActivity' + }); + break; case 'checkout_setUpClicked': openWallet(); break; @@ -589,9 +595,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { break; case 'updateItemClicked': openMarketplace(message.upgradeUrl + "?edition=" + message.itemEdition); - break; - case 'giftAsset': - break; case 'passphrasePopup_cancelClicked': case 'needsLogIn_cancelClicked': @@ -635,6 +638,20 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { case 'http.request': // Handled elsewhere, don't log. break; + // All of these are handled by wallet.js + case 'purchases_updateWearables': + case 'enable_ChooseRecipientNearbyMode': + case 'disable_ChooseRecipientNearbyMode': + case 'sendAsset_sendPublicly': + case 'refreshConnections': + case 'transactionHistory_goToBank': + case 'purchases_walletNotSetUp': + case 'purchases_openGoTo': + case 'purchases_itemInfoClicked': + case 'purchases_itemCertificateClicked': + case 'clearShouldShowDotHistory': + case 'giftAsset': + break; default: print('Unrecognized message from Checkout.qml: ' + JSON.stringify(message)); } @@ -705,6 +722,8 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { referrerURL: referrerURL, filterText: filterText }); + referrerURL = ""; + filterText = ""; } ui.isOpen = (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen; diff --git a/tests-manual/gpu/src/TestFbx.cpp b/tests-manual/gpu/src/TestFbx.cpp index 538bb0a973..9890e4fbe7 100644 --- a/tests-manual/gpu/src/TestFbx.cpp +++ b/tests-manual/gpu/src/TestFbx.cpp @@ -100,12 +100,12 @@ bool TestFbx::isReady() const { void TestFbx::parseFbx(const QByteArray& fbxData) { QVariantHash mapping; - FBXGeometry* fbx = readFBX(fbxData, mapping); + HFMGeometry* geometry = readFBX(fbxData, mapping); size_t totalVertexCount = 0; size_t totalIndexCount = 0; size_t totalPartCount = 0; size_t highestIndex = 0; - for (const auto& mesh : fbx->meshes) { + for (const auto& mesh : geometry->meshes) { size_t vertexCount = mesh.vertices.size(); totalVertexCount += mesh.vertices.size(); highestIndex = std::max(highestIndex, vertexCount); @@ -123,7 +123,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { std::vector parts; parts.reserve(totalPartCount); _partCount = totalPartCount; - for (const auto& mesh : fbx->meshes) { + for (const auto& mesh : geometry->meshes) { baseVertex = vertices.size(); vec3 color; @@ -133,7 +133,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { partIndirect.firstIndex = (uint)indices.size(); partIndirect.baseInstance = (uint)parts.size(); _partTransforms.push_back(mesh.modelTransform); - auto material = fbx->materials[part.materialID]; + auto material = geometry->materials[part.materialID]; color = material.diffuseColor; for (auto index : part.quadTrianglesIndices) { indices.push_back(index); @@ -163,7 +163,7 @@ void TestFbx::parseFbx(const QByteArray& fbxData) { _vertexBuffer->append(vertices); _indexBuffer->append(indices); _indirectBuffer->append(parts); - delete fbx; + delete geometry; } void TestFbx::renderTest(size_t testId, RenderArgs* args) { diff --git a/tests-manual/gpu/src/TestFbx.h b/tests-manual/gpu/src/TestFbx.h index 391fff1091..4e22928460 100644 --- a/tests-manual/gpu/src/TestFbx.h +++ b/tests-manual/gpu/src/TestFbx.h @@ -11,7 +11,7 @@ #include -class FBXGeometry; +class HFMGeometry; class TestFbx : public GpuTestBase { size_t _partCount { 0 }; diff --git a/tests/animation/src/AnimInverseKinematicsTests.cpp b/tests/animation/src/AnimInverseKinematicsTests.cpp index f5d3597f56..f51fe12ecb 100644 --- a/tests/animation/src/AnimInverseKinematicsTests.cpp +++ b/tests/animation/src/AnimInverseKinematicsTests.cpp @@ -28,8 +28,8 @@ const glm::quat identity = glm::quat(); const glm::quat quaterTurnAroundZ = glm::angleAxis(0.5f * PI, zAxis); -void makeTestFBXJoints(FBXGeometry& geometry) { - FBXJoint joint; +void makeTestFBXJoints(HFMGeometry& geometry) { + HFMJoint joint; joint.isFree = false; joint.freeLineage.clear(); joint.parentIndex = -1; @@ -79,7 +79,7 @@ void makeTestFBXJoints(FBXGeometry& geometry) { // compute each joint's transform for (int i = 1; i < (int)geometry.joints.size(); ++i) { - FBXJoint& j = geometry.joints[i]; + HFMJoint& j = geometry.joints[i]; int parentIndex = j.parentIndex; // World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1) j.transform = geometry.joints[parentIndex].transform * @@ -96,7 +96,7 @@ void AnimInverseKinematicsTests::testSingleChain() { AnimContext context(false, false, false, glm::mat4(), glm::mat4()); - FBXGeometry geometry; + HFMGeometry geometry; makeTestFBXJoints(geometry); // create a skeleton and doll diff --git a/tools/skeleton-dump/src/SkeletonDumpApp.cpp b/tools/skeleton-dump/src/SkeletonDumpApp.cpp index e9d8243e38..5107931da1 100644 --- a/tools/skeleton-dump/src/SkeletonDumpApp.cpp +++ b/tools/skeleton-dump/src/SkeletonDumpApp.cpp @@ -54,8 +54,8 @@ SkeletonDumpApp::SkeletonDumpApp(int argc, char* argv[]) : QCoreApplication(argc return; } QByteArray blob = file.readAll(); - std::unique_ptr fbxGeometry(readFBX(blob, QVariantHash())); - std::unique_ptr skeleton(new AnimSkeleton(*fbxGeometry)); + std::unique_ptr geometry(readFBX(blob, QVariantHash())); + std::unique_ptr skeleton(new AnimSkeleton(*geometry)); skeleton->dump(verbose); } diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index a52e948f01..bb2958e11d 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -19,16 +19,16 @@ // FBXReader jumbles the order of the meshes by reading them back out of a hashtable. This will put // them back in the order in which they appeared in the file. -bool FBXGeometryLessThan(const FBXMesh& e1, const FBXMesh& e2) { +bool HFMGeometryLessThan(const HFMMesh& e1, const HFMMesh& e2) { return e1.meshIndex < e2.meshIndex; } -void reSortFBXGeometryMeshes(FBXGeometry& geometry) { - qSort(geometry.meshes.begin(), geometry.meshes.end(), FBXGeometryLessThan); +void reSortHFMGeometryMeshes(HFMGeometry& geometry) { + qSort(geometry.meshes.begin(), geometry.meshes.end(), HFMGeometryLessThan); } // Read all the meshes from provided FBX file -bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { +bool vhacd::VHACDUtil::loadFBX(const QString filename, HFMGeometry& result) { if (_verbose) { qDebug() << "reading FBX file =" << filename << "..."; } @@ -41,7 +41,7 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { } try { QByteArray fbxContents = fbx.readAll(); - FBXGeometry::Pointer geom; + HFMGeometry::Pointer geom; if (filename.toLower().endsWith(".obj")) { bool combineParts = false; geom = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts); @@ -53,7 +53,7 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { } result = *geom; - reSortFBXGeometryMeshes(result); + reSortHFMGeometryMeshes(result); } catch (const QString& error) { qWarning() << "error reading" << filename << ":" << error; return false; @@ -63,7 +63,7 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { } -void getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector& triangleIndices) { +void getTrianglesInMeshPart(const HFMMeshPart &meshPart, std::vector& triangleIndices) { // append triangle indices triangleIndices.reserve(triangleIndices.size() + (size_t)meshPart.triangleIndices.size()); for (auto index : meshPart.triangleIndices) { @@ -88,12 +88,12 @@ void getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector& trian } } -void vhacd::VHACDUtil::fattenMesh(const FBXMesh& mesh, const glm::mat4& geometryOffset, FBXMesh& result) const { +void vhacd::VHACDUtil::fattenMesh(const HFMMesh& mesh, const glm::mat4& geometryOffset, HFMMesh& result) const { // this is used to make meshes generated from a highfield collidable. each triangle // is converted into a tetrahedron and made into its own mesh-part. std::vector triangleIndices; - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { getTrianglesInMeshPart(meshPart, triangleIndices); } @@ -145,7 +145,7 @@ void vhacd::VHACDUtil::fattenMesh(const FBXMesh& mesh, const glm::mat4& geometry int index3 = result.vertices.size(); result.vertices << p3; // add the new point to the result mesh - FBXMeshPart newMeshPart; + HFMMeshPart newMeshPart; setMeshPartDefaults(newMeshPart, "unknown"); newMeshPart.triangleIndices << index0 << index1 << index2; newMeshPart.triangleIndices << index0 << index3 << index1; @@ -155,7 +155,7 @@ void vhacd::VHACDUtil::fattenMesh(const FBXMesh& mesh, const glm::mat4& geometry } } -AABox getAABoxForMeshPart(const FBXMesh& mesh, const FBXMeshPart &meshPart) { +AABox getAABoxForMeshPart(const HFMMesh& mesh, const HFMMeshPart &meshPart) { AABox aaBox; const int TRIANGLE_STRIDE = 3; for (int i = 0; i < meshPart.triangleIndices.size(); i += TRIANGLE_STRIDE) { @@ -242,7 +242,7 @@ bool isClosedManifold(const std::vector& triangleIndices) { return true; } -void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) const { +void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, HFMMesh& resultMesh) const { // Number of hulls for this input meshPart uint32_t numHulls = convexifier->GetNConvexHulls(); if (_verbose) { @@ -256,8 +256,8 @@ void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& res VHACD::IVHACD::ConvexHull hull; convexifier->GetConvexHull(j, hull); - resultMesh.parts.append(FBXMeshPart()); - FBXMeshPart& resultMeshPart = resultMesh.parts.last(); + resultMesh.parts.append(HFMMeshPart()); + HFMMeshPart& resultMeshPart = resultMesh.parts.last(); int hullIndexStart = resultMesh.vertices.size(); resultMesh.vertices.reserve(hullIndexStart + hull.m_nPoints); @@ -288,9 +288,9 @@ float computeDt(uint64_t start) { return (float)(usecTimestampNow() - start) / (float)USECS_PER_SECOND; } -bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, +bool vhacd::VHACDUtil::computeVHACD(HFMGeometry& geometry, VHACD::IVHACD::Parameters params, - FBXGeometry& result, + HFMGeometry& result, float minimumMeshSize, float maximumMeshSize) { if (_verbose) { qDebug() << "meshes =" << geometry.meshes.size(); @@ -298,7 +298,7 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, // count the mesh-parts int numParts = 0; - foreach (const FBXMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, geometry.meshes) { numParts += mesh.parts.size(); } if (_verbose) { @@ -308,15 +308,15 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, VHACD::IVHACD * convexifier = VHACD::CreateVHACD(); result.meshExtents.reset(); - result.meshes.append(FBXMesh()); - FBXMesh &resultMesh = result.meshes.last(); + result.meshes.append(HFMMesh()); + HFMMesh &resultMesh = result.meshes.last(); const uint32_t POINT_STRIDE = 3; const uint32_t TRIANGLE_STRIDE = 3; int meshIndex = 0; int validPartsFound = 0; - foreach (const FBXMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, geometry.meshes) { // find duplicate points int numDupes = 0; @@ -354,7 +354,7 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, int partIndex = 0; std::vector triangleIndices; - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { triangleIndices.clear(); getTrianglesInMeshPart(meshPart, triangleIndices); @@ -421,7 +421,7 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, triangleIndices.clear(); for (auto index : openParts) { - const FBXMeshPart &meshPart = mesh.parts[index]; + const HFMMeshPart &meshPart = mesh.parts[index]; getTrianglesInMeshPart(meshPart, triangleIndices); } diff --git a/tools/vhacd-util/src/VHACDUtil.h b/tools/vhacd-util/src/VHACDUtil.h index 35ec3ef56b..64e86ed7df 100644 --- a/tools/vhacd-util/src/VHACDUtil.h +++ b/tools/vhacd-util/src/VHACDUtil.h @@ -27,16 +27,16 @@ namespace vhacd { public: void setVerbose(bool verbose) { _verbose = verbose; } - bool loadFBX(const QString filename, FBXGeometry& result); + bool loadFBX(const QString filename, HFMGeometry& result); - void fattenMesh(const FBXMesh& mesh, const glm::mat4& gometryOffset, FBXMesh& result) const; + void fattenMesh(const HFMMesh& mesh, const glm::mat4& gometryOffset, HFMMesh& result) const; - bool computeVHACD(FBXGeometry& geometry, + bool computeVHACD(HFMGeometry& geometry, VHACD::IVHACD::Parameters params, - FBXGeometry& result, + HFMGeometry& result, float minimumMeshSize, float maximumMeshSize); - void getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) const; + void getConvexResults(VHACD::IVHACD* convexifier, HFMMesh& resultMesh) const; ~VHACDUtil(); @@ -55,6 +55,6 @@ namespace vhacd { }; } -AABox getAABoxForMeshPart(const FBXMeshPart &meshPart); +AABox getAABoxForMeshPart(const HFMMeshPart &meshPart); #endif //hifi_VHACDUtil_h diff --git a/tools/vhacd-util/src/VHACDUtilApp.cpp b/tools/vhacd-util/src/VHACDUtilApp.cpp index c263dce609..0941198234 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.cpp +++ b/tools/vhacd-util/src/VHACDUtilApp.cpp @@ -36,7 +36,7 @@ QString formatFloat(double n) { } -bool VHACDUtilApp::writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart) { +bool VHACDUtilApp::writeOBJ(QString outFileName, HFMGeometry& geometry, bool outputCentimeters, int whichMeshPart) { QFile file(outFileName); if (!file.open(QIODevice::WriteOnly)) { qWarning() << "unable to write to" << outFileName; @@ -56,9 +56,9 @@ bool VHACDUtilApp::writeOBJ(QString outFileName, FBXGeometry& geometry, bool out int vertexIndexOffset = 0; - foreach (const FBXMesh& mesh, geometry.meshes) { + foreach (const HFMMesh& mesh, geometry.meshes) { bool verticesHaveBeenOutput = false; - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { if (whichMeshPart >= 0 && nth != (unsigned int) whichMeshPart) { nth++; continue; @@ -297,7 +297,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } // load the mesh - FBXGeometry fbx; + HFMGeometry fbx; auto begin = std::chrono::high_resolution_clock::now(); if (!vUtil.loadFBX(inputFilename, fbx)){ _returnCode = VHACD_RETURN_CODE_FAILURE_TO_READ; @@ -315,8 +315,8 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : QVector infileExtensions = {"fbx", "obj"}; QString baseFileName = fileNameWithoutExtension(outputFilename, infileExtensions); int count = 0; - foreach (const FBXMesh& mesh, fbx.meshes) { - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMesh& mesh, fbx.meshes) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { QString outputFileName = baseFileName + "-" + QString::number(count) + ".obj"; writeOBJ(outputFileName, fbx, outputCentimeters, count); count++; @@ -358,7 +358,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } begin = std::chrono::high_resolution_clock::now(); - FBXGeometry result; + HFMGeometry result; bool success = vUtil.computeVHACD(fbx, params, result, minimumMeshSize, maximumMeshSize); end = std::chrono::high_resolution_clock::now(); @@ -377,9 +377,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : int totalVertices = 0; int totalTriangles = 0; - foreach (const FBXMesh& mesh, result.meshes) { + foreach (const HFMMesh& mesh, result.meshes) { totalVertices += mesh.vertices.size(); - foreach (const FBXMeshPart &meshPart, mesh.parts) { + foreach (const HFMMeshPart &meshPart, mesh.parts) { totalTriangles += meshPart.triangleIndices.size() / 3; // each quad was made into two triangles totalTriangles += 2 * meshPart.quadIndices.size() / 4; @@ -398,17 +398,17 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : } if (fattenFaces) { - FBXGeometry newFbx; - FBXMesh result; + HFMGeometry newFbx; + HFMMesh result; // count the mesh-parts unsigned int meshCount = 0; - foreach (const FBXMesh& mesh, fbx.meshes) { + foreach (const HFMMesh& mesh, fbx.meshes) { meshCount += mesh.parts.size(); } result.modelTransform = glm::mat4(); // Identity matrix - foreach (const FBXMesh& mesh, fbx.meshes) { + foreach (const HFMMesh& mesh, fbx.meshes) { vUtil.fattenMesh(mesh, fbx.offset, result); } diff --git a/tools/vhacd-util/src/VHACDUtilApp.h b/tools/vhacd-util/src/VHACDUtilApp.h index 0d75275802..3db49456a0 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.h +++ b/tools/vhacd-util/src/VHACDUtilApp.h @@ -28,7 +28,7 @@ public: VHACDUtilApp(int argc, char* argv[]); ~VHACDUtilApp(); - bool writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart = -1); + bool writeOBJ(QString outFileName, HFMGeometry& geometry, bool outputCentimeters, int whichMeshPart = -1); int getReturnCode() const { return _returnCode; } From 3631e304badd027966f363c54089f2eadbd80e9b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 5 Nov 2018 14:58:12 -0800 Subject: [PATCH 101/125] Acquisition --- .../InspectionCertificate.qml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index 734292d774..885838a26e 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -103,8 +103,8 @@ Rectangle { showInMarketplaceButton.visible = false; // "Edition" text previously set above in this function // "Owner" text previously set above in this function - // "Date Acquired" text previously set above in this function - // "Original Price" text previously set above in this function + // "Acquisition Date" text previously set above in this function + // "Acquisition Price" text previously set above in this function if (result.data.invalid_reason) { errorText.text = result.data.invalid_reason; } @@ -118,8 +118,8 @@ Rectangle { showInMarketplaceButton.visible = true; // "Edition" text previously set above in this function // "Owner" text previously set above in this function - // "Date Acquired" text previously set above in this function - // "Original Price" text previously set above in this function + // "Acquisition Date" text previously set above in this function + // "Acquisition Price" text previously set above in this function errorText.text = "The status of this item is still pending confirmation. If the purchase is not confirmed, " + "this entity will be cleaned up by the domain."; } @@ -146,8 +146,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" // "Edition" text will be set in "onCertificateInfoResult()" // "Owner" text will be set in "onCertificateInfoResult()" - // "Date Acquired" text will be set in "onCertificateInfoResult()" - // "Original Price" text will be set in "onCertificateInfoResult()" + // "Acquisition Date" text will be set in "onCertificateInfoResult()" + // "Acquisition Price" text will be set in "onCertificateInfoResult()" errorText.text = ""; } else if (root.certificateStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT root.useGoldCert = false; @@ -176,8 +176,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" // "Edition" text will be set in "onCertificateInfoResult()" // "Owner" text will be set in "onCertificateInfoResult()" - // "Date Acquired" text will be set in "onCertificateInfoResult()" - // "Original Price" text will be set in "onCertificateInfoResult()" + // "Acquisition Date" text will be set in "onCertificateInfoResult()" + // "Acquisition Price" text will be set in "onCertificateInfoResult()" errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item."; } else if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED root.useGoldCert = false; @@ -191,8 +191,8 @@ Rectangle { // "Item Name" text will be set in "onCertificateInfoResult()" root.itemEdition = "Uncertified Copy" // "Owner" text will be set in "onCertificateInfoResult()" - // "Date Acquired" text will be set in "onCertificateInfoResult()" - // "Original Price" text will be set in "onCertificateInfoResult()" + // "Acquisition Date" text will be set in "onCertificateInfoResult()" + // "Acquisition Price" text will be set in "onCertificateInfoResult()" // "Error Text" text will be set in "onCertificateInfoResult()" } else { console.log("Unknown certificate status received from ledger signal!"); @@ -487,7 +487,7 @@ Rectangle { RalewayRegular { id: dateAcquiredHeader; - text: "DATE ACQUIRED"; + text: "ACQUISITION DATE"; // Text size size: 16; // Anchors @@ -517,7 +517,7 @@ Rectangle { RalewayRegular { id: priceHeader; - text: "ORIGINAL PRICE"; + text: "ACQUISITION PRICE"; // Text size size: 16; // Anchors From 5b042ec3dfbd846f8a26d09ec4284ffe1e7bdf83 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 5 Nov 2018 15:23:25 -0800 Subject: [PATCH 102/125] My items to my submissions --- .../qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml | 4 ++-- interface/resources/qml/hifi/commerce/purchases/Purchases.qml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml index 4b0166425a..99c2d89da8 100644 --- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml +++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml @@ -213,7 +213,7 @@ Item { anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin; anchors.right: buttonAndUsernameContainer.right; height: childrenRect.height; - width: 100; + width: 150; Rectangle { id: myItemsButton; @@ -225,7 +225,7 @@ Item { RalewaySemiBold { anchors.fill: parent; - text: "My Items" + text: "My Submissions" color: hifi.colors.baseGray; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index ced5450290..df4d7f6175 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -882,7 +882,7 @@ Rectangle { // Explanitory text RalewayRegular { id: noItemsYet; - text: "You haven't submitted anything to the Marketplace yet!

Submit an item to the Marketplace to add it to My Items."; + text: "You haven't submitted anything to the Marketplace yet!

Submit an item to the Marketplace to add it to My Submissions."; // Text size size: 22; // Anchors From 89656ea3f6e44835bfca84fb1f63e727cab364b3 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 5 Nov 2018 16:43:06 -0800 Subject: [PATCH 103/125] activity notifications also change ASSETS to INVENTORY --- scripts/system/commerce/wallet.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index f9857992bb..9fb336f79c 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -608,12 +608,12 @@ function notificationPollCallbackHistory(historyArray) { var message; if (!ui.notificationInitialCallbackMade[1]) { message = "You have " + notificationCount + " unread recent " + - "transaction" + (notificationCount === 1 ? "" : "s") + ". Open ASSETS to see all activity."; + "transaction" + (notificationCount === 1 ? "" : "s") + ". Open INVENTORY to see all activity."; ui.notificationDisplayBanner(message); } else { for (var i = 0; i < notificationCount; i++) { message = '"' + (historyArray[i].message) + '" ' + - "Open ASSETS to see all activity."; + "Open INVENTORY to see all activity."; ui.notificationDisplayBanner(message); } } From 27bb8a0de7cc460576125c6a2d529c8fa40836d6 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 15 Jun 2018 21:44:12 +0300 Subject: [PATCH 104/125] rename stylues-uit => stylesUit & controls-uit => controlsUit note: the idea is to make imports like 'import controlsUit 1.0' to work with 'styles-uit'/'controls-uit' it is not possible because of two reasons: 1. import controls-uit 1.0 is invalid syntax 2. qmldir inside controls-uit is 'module controlsUit' --- interface/resources/qml/AudioScopeUI.qml | 4 ++-- interface/resources/qml/Browser.qml | 4 ++-- interface/resources/qml/CurrentAPI.qml | 4 ++-- interface/resources/qml/InfoView.qml | 2 +- interface/resources/qml/LoginDialog.qml | 4 ++-- .../qml/LoginDialog/+android/LinkAccountBody.qml | 4 ++-- .../resources/qml/LoginDialog/+android/SignUpBody.qml | 4 ++-- .../resources/qml/LoginDialog/CompleteProfileBody.qml | 4 ++-- .../resources/qml/LoginDialog/LinkAccountBody.qml | 5 +++-- interface/resources/qml/LoginDialog/SignInBody.qml | 4 ++-- interface/resources/qml/LoginDialog/SignUpBody.qml | 4 ++-- .../qml/LoginDialog/UsernameCollisionBody.qml | 4 ++-- interface/resources/qml/LoginDialog/WelcomeBody.qml | 4 ++-- interface/resources/qml/QmlWebWindow.qml | 4 ++-- interface/resources/qml/QmlWindow.qml | 4 ++-- interface/resources/qml/TabletBrowser.qml | 4 ++-- interface/resources/qml/UpdateDialog.qml | 4 ++-- interface/resources/qml/controls/Button.qml | 1 - .../resources/qml/controls/FlickableWebViewCore.qml | 2 +- interface/resources/qml/controls/TabletWebButton.qml | 2 +- interface/resources/qml/controls/TabletWebScreen.qml | 2 +- interface/resources/qml/controls/TabletWebView.qml | 4 ++-- interface/resources/qml/controls/WebView.qml | 2 +- .../+android/ImageButton.qml | 6 +++--- .../AttachmentsTable.qml | 4 ++-- .../qml/{controls-uit => controlsUit}/BaseWebView.qml | 0 .../qml/{controls-uit => controlsUit}/Button.qml | 2 +- .../qml/{controls-uit => controlsUit}/CheckBox.qml | 2 +- .../{controls-uit => controlsUit}/CheckBoxQQC2.qml | 4 ++-- .../qml/{controls-uit => controlsUit}/ComboBox.qml | 4 ++-- .../{controls-uit => controlsUit}/ContentSection.qml | 2 +- .../qml/{controls-uit => controlsUit}/FilterBar.qml | 4 ++-- .../qml/{controls-uit => controlsUit}/GlyphButton.qml | 2 +- .../{controls-uit => controlsUit}/HorizontalRule.qml | 0 .../HorizontalSpacer.qml | 2 +- .../{controls-uit => controlsUit}/ImageMessageBox.qml | 2 +- .../qml/{controls-uit => controlsUit}/Key.qml | 0 .../qml/{controls-uit => controlsUit}/Keyboard.qml | 0 .../qml/{controls-uit => controlsUit}/Label.qml | 2 +- .../{controls-uit => controlsUit}/QueuedButton.qml | 2 +- .../qml/{controls-uit => controlsUit}/RadioButton.qml | 4 ++-- .../qml/{controls-uit => controlsUit}/ScrollBar.qml | 2 +- .../qml/{controls-uit => controlsUit}/Separator.qml | 2 +- .../qml/{controls-uit => controlsUit}/Slider.qml | 4 ++-- .../qml/{controls-uit => controlsUit}/SpinBox.qml | 4 ++-- .../qml/{controls-uit => controlsUit}/Switch.qml | 2 +- .../qml/{controls-uit => controlsUit}/Table.qml | 2 +- .../TabletContentSection.qml | 2 +- .../{controls-uit => controlsUit}/TabletHeader.qml | 2 +- .../qml/{controls-uit => controlsUit}/TextAction.qml | 4 ++-- .../qml/{controls-uit => controlsUit}/TextEdit.qml | 2 +- .../qml/{controls-uit => controlsUit}/TextField.qml | 4 ++-- .../qml/{controls-uit => controlsUit}/ToolTip.qml | 0 .../qml/{controls-uit => controlsUit}/Tree.qml | 2 +- .../{controls-uit => controlsUit}/VerticalSpacer.qml | 2 +- .../{controls-uit => controlsUit}/WebGlyphButton.qml | 2 +- .../qml/{controls-uit => controlsUit}/WebSpinner.qml | 0 .../qml/{controls-uit => controlsUit}/WebView.qml | 0 .../qml/{controls-uit => controlsUit}/qmldir | 0 interface/resources/qml/dialogs/AssetDialog.qml | 2 +- interface/resources/qml/dialogs/CustomQueryDialog.qml | 4 ++-- interface/resources/qml/dialogs/FileDialog.qml | 4 ++-- interface/resources/qml/dialogs/MessageDialog.qml | 4 ++-- interface/resources/qml/dialogs/PreferencesDialog.qml | 4 ++-- interface/resources/qml/dialogs/QueryDialog.qml | 4 ++-- interface/resources/qml/dialogs/TabletAssetDialog.qml | 2 +- .../resources/qml/dialogs/TabletCustomQueryDialog.qml | 4 ++-- interface/resources/qml/dialogs/TabletFileDialog.qml | 4 ++-- interface/resources/qml/dialogs/TabletLoginDialog.qml | 4 ++-- interface/resources/qml/dialogs/TabletMessageBox.qml | 4 ++-- interface/resources/qml/dialogs/TabletQueryDialog.qml | 4 ++-- .../qml/dialogs/assetDialog/AssetDialogContent.qml | 4 ++-- .../qml/dialogs/fileDialog/FileTypeSelection.qml | 2 +- .../qml/dialogs/messageDialog/MessageDialogButton.qml | 2 +- .../qml/dialogs/preferences/AvatarPreference.qml | 2 +- .../qml/dialogs/preferences/BrowsablePreference.qml | 2 +- .../qml/dialogs/preferences/ButtonPreference.qml | 2 +- .../qml/dialogs/preferences/CheckBoxPreference.qml | 2 +- .../qml/dialogs/preferences/ComboBoxPreference.qml | 4 ++-- .../qml/dialogs/preferences/EditablePreference.qml | 2 +- .../qml/dialogs/preferences/PrimaryHandPreference.qml | 2 +- .../dialogs/preferences/RadioButtonsPreference.qml | 4 ++-- .../resources/qml/dialogs/preferences/Section.qml | 4 ++-- .../qml/dialogs/preferences/SliderPreference.qml | 2 +- .../qml/dialogs/preferences/SpinBoxPreference.qml | 2 +- .../dialogs/preferences/SpinnerSliderPreference.qml | 2 +- interface/resources/qml/hifi/+android/ActionBar.qml | 4 ++-- interface/resources/qml/hifi/+android/AudioBar.qml | 4 ++-- .../resources/qml/hifi/+android/AvatarOption.qml | 2 +- interface/resources/qml/hifi/+android/StatsBar.qml | 4 ++-- .../resources/qml/hifi/+android/WindowHeader.qml | 4 ++-- .../resources/qml/hifi/+android/bottomHudOptions.qml | 4 ++-- interface/resources/qml/hifi/+android/modesbar.qml | 4 ++-- interface/resources/qml/hifi/AssetServer.qml | 4 ++-- interface/resources/qml/hifi/Card.qml | 2 +- interface/resources/qml/hifi/ComboDialog.qml | 4 ++-- interface/resources/qml/hifi/Desktop.qml | 2 +- .../resources/qml/hifi/DesktopLetterboxMessage.qml | 2 +- interface/resources/qml/hifi/Feed.qml | 2 +- interface/resources/qml/hifi/LetterboxMessage.qml | 2 +- interface/resources/qml/hifi/NameCard.qml | 4 ++-- interface/resources/qml/hifi/Pal.qml | 4 ++-- interface/resources/qml/hifi/SkyboxChanger.qml | 4 ++-- interface/resources/qml/hifi/SpectatorCamera.qml | 4 ++-- interface/resources/qml/hifi/TabletTextButton.qml | 2 +- interface/resources/qml/hifi/TextButton.qml | 2 +- interface/resources/qml/hifi/WebBrowser.qml | 4 ++-- interface/resources/qml/hifi/audio/Audio.qml | 4 ++-- interface/resources/qml/hifi/audio/AudioTabButton.qml | 4 ++-- interface/resources/qml/hifi/audio/CheckBox.qml | 2 +- .../resources/qml/hifi/audio/PlaySampleSound.qml | 4 ++-- .../resources/qml/hifi/commerce/checkout/Checkout.qml | 4 ++-- .../qml/hifi/commerce/common/CommerceLightbox.qml | 4 ++-- .../commerce/common/EmulatedMarketplaceHeader.qml | 4 ++-- .../qml/hifi/commerce/common/FirstUseTutorial.qml | 4 ++-- .../hifi/commerce/common/sendAsset/ConnectionItem.qml | 4 ++-- .../commerce/common/sendAsset/RecipientDisplay.qml | 4 ++-- .../qml/hifi/commerce/common/sendAsset/SendAsset.qml | 4 ++-- .../inspectionCertificate/InspectionCertificate.qml | 4 ++-- .../qml/hifi/commerce/purchases/PurchasedItem.qml | 4 ++-- .../qml/hifi/commerce/purchases/Purchases.qml | 4 ++-- interface/resources/qml/hifi/commerce/wallet/Help.qml | 4 ++-- .../resources/qml/hifi/commerce/wallet/NeedsLogIn.qml | 4 ++-- .../qml/hifi/commerce/wallet/PassphraseChange.qml | 4 ++-- .../qml/hifi/commerce/wallet/PassphraseModal.qml | 4 ++-- .../qml/hifi/commerce/wallet/PassphraseSelection.qml | 4 ++-- .../resources/qml/hifi/commerce/wallet/Wallet.qml | 4 ++-- .../qml/hifi/commerce/wallet/WalletChoice.qml | 4 ++-- .../resources/qml/hifi/commerce/wallet/WalletHome.qml | 4 ++-- .../qml/hifi/commerce/wallet/WalletSetup.qml | 4 ++-- interface/resources/qml/hifi/dialogs/AboutDialog.qml | 2 +- .../resources/qml/hifi/dialogs/RunningScripts.qml | 4 ++-- .../resources/qml/hifi/dialogs/TabletAboutDialog.qml | 2 +- .../resources/qml/hifi/dialogs/TabletAssetServer.qml | 4 ++-- .../resources/qml/hifi/dialogs/TabletDCDialog.qml | 4 ++-- .../resources/qml/hifi/dialogs/TabletDebugWindow.qml | 4 ++-- .../qml/hifi/dialogs/TabletEntityStatistics.qml | 4 ++-- .../qml/hifi/dialogs/TabletEntityStatisticsItem.qml | 4 ++-- .../resources/qml/hifi/dialogs/TabletLODTools.qml | 4 ++-- .../qml/hifi/dialogs/TabletRunningScripts.qml | 4 ++-- .../qml/hifi/dialogs/content/ModelBrowserContent.qml | 2 +- .../qml/hifi/dialogs/security/SecurityImageChange.qml | 4 ++-- .../hifi/dialogs/security/SecurityImageSelection.qml | 4 ++-- .../resources/qml/hifi/tablet/CalibratingScreen.qml | 4 ++-- .../resources/qml/hifi/tablet/ControllerSettings.qml | 4 ++-- interface/resources/qml/hifi/tablet/EditTabButton.qml | 4 ++-- interface/resources/qml/hifi/tablet/EditTabView.qml | 4 ++-- interface/resources/qml/hifi/tablet/InputRecorder.qml | 4 ++-- .../resources/qml/hifi/tablet/NewMaterialDialog.qml | 4 ++-- .../resources/qml/hifi/tablet/NewModelDialog.qml | 4 ++-- .../resources/qml/hifi/tablet/OpenVrConfiguration.qml | 4 ++-- .../resources/qml/hifi/tablet/TabletAddressDialog.qml | 4 ++-- interface/resources/qml/hifi/tablet/TabletHome.qml | 2 +- interface/resources/qml/hifi/tablet/TabletMenu.qml | 2 +- .../resources/qml/hifi/tablet/TabletMenuItem.qml | 4 ++-- .../resources/qml/hifi/tablet/TabletMenuView.qml | 2 +- .../qml/hifi/tablet/TabletModelBrowserDialog.qml | 4 ++-- .../hifi/tablet/tabletWindows/TabletFileDialog.qml | 4 ++-- .../tablet/tabletWindows/TabletPreferencesDialog.qml | 4 ++-- .../hifi/tablet/tabletWindows/preferences/Section.qml | 4 ++-- .../preferences/TabletBrowsablePreference.qml | 2 +- .../+android/HifiConstants.qml | 0 .../{styles-uit => stylesUit}/AnonymousProRegular.qml | 0 .../qml/{styles-uit => stylesUit}/ButtonLabel.qml | 0 .../qml/{styles-uit => stylesUit}/FiraSansRegular.qml | 0 .../{styles-uit => stylesUit}/FiraSansSemiBold.qml | 0 .../qml/{styles-uit => stylesUit}/HiFiGlyphs.qml | 0 .../qml/{styles-uit => stylesUit}/HifiConstants.qml | 0 .../qml/{styles-uit => stylesUit}/IconButton.qml | 0 .../qml/{styles-uit => stylesUit}/InfoItem.qml | 0 .../qml/{styles-uit => stylesUit}/InputLabel.qml | 0 .../qml/{styles-uit => stylesUit}/ListItem.qml | 0 .../resources/qml/{styles-uit => stylesUit}/Logs.qml | 0 .../qml/{styles-uit => stylesUit}/OverlayTitle.qml | 0 .../qml/{styles-uit => stylesUit}/RalewayBold.qml | 0 .../qml/{styles-uit => stylesUit}/RalewayLight.qml | 0 .../qml/{styles-uit => stylesUit}/RalewayRegular.qml | 0 .../qml/{styles-uit => stylesUit}/RalewaySemiBold.qml | 0 .../qml/{styles-uit => stylesUit}/SectionName.qml | 0 .../qml/{styles-uit => stylesUit}/Separator.qml | 2 +- .../qml/{styles-uit => stylesUit}/ShortcutText.qml | 0 .../qml/{styles-uit => stylesUit}/TabName.qml | 0 .../qml/{styles-uit => stylesUit}/TextFieldInput.qml | 0 .../resources/qml/{styles-uit => stylesUit}/qmldir | 0 interface/resources/qml/windows/Decoration.qml | 2 +- interface/resources/qml/windows/DefaultFrame.qml | 2 +- .../resources/qml/windows/DefaultFrameDecoration.qml | 2 +- interface/resources/qml/windows/Fadable.qml | 2 +- interface/resources/qml/windows/Frame.qml | 2 +- interface/resources/qml/windows/ModalFrame.qml | 4 ++-- interface/resources/qml/windows/ScrollingWindow.qml | 4 ++-- interface/resources/qml/windows/TabletModalFrame.qml | 4 ++-- interface/resources/qml/windows/ToolFrame.qml | 2 +- .../resources/qml/windows/ToolFrameDecoration.qml | 2 +- interface/resources/qml/windows/Window.qml | 2 +- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 1 + scripts/developer/tests/ControlsGallery.qml | 11 ++--------- 197 files changed, 272 insertions(+), 278 deletions(-) rename interface/resources/qml/{controls-uit => controlsUit}/+android/ImageButton.qml (96%) rename interface/resources/qml/{controls-uit => controlsUit}/AttachmentsTable.qml (98%) rename interface/resources/qml/{controls-uit => controlsUit}/BaseWebView.qml (100%) rename interface/resources/qml/{controls-uit => controlsUit}/Button.qml (99%) rename interface/resources/qml/{controls-uit => controlsUit}/CheckBox.qml (99%) rename interface/resources/qml/{controls-uit => controlsUit}/CheckBoxQQC2.qml (98%) rename interface/resources/qml/{controls-uit => controlsUit}/ComboBox.qml (99%) rename interface/resources/qml/{controls-uit => controlsUit}/ContentSection.qml (99%) rename interface/resources/qml/{controls-uit => controlsUit}/FilterBar.qml (99%) rename interface/resources/qml/{controls-uit => controlsUit}/GlyphButton.qml (99%) rename interface/resources/qml/{controls-uit => controlsUit}/HorizontalRule.qml (100%) rename interface/resources/qml/{controls-uit => controlsUit}/HorizontalSpacer.qml (94%) rename interface/resources/qml/{controls-uit => controlsUit}/ImageMessageBox.qml (98%) rename interface/resources/qml/{controls-uit => controlsUit}/Key.qml (100%) rename interface/resources/qml/{controls-uit => controlsUit}/Keyboard.qml (100%) rename interface/resources/qml/{controls-uit => controlsUit}/Label.qml (97%) rename interface/resources/qml/{controls-uit => controlsUit}/QueuedButton.qml (98%) rename interface/resources/qml/{controls-uit => controlsUit}/RadioButton.qml (97%) rename interface/resources/qml/{controls-uit => controlsUit}/ScrollBar.qml (98%) rename interface/resources/qml/{controls-uit => controlsUit}/Separator.qml (98%) rename interface/resources/qml/{controls-uit => controlsUit}/Slider.qml (98%) rename interface/resources/qml/{controls-uit => controlsUit}/SpinBox.qml (98%) rename interface/resources/qml/{controls-uit => controlsUit}/Switch.qml (99%) rename interface/resources/qml/{controls-uit => controlsUit}/Table.qml (99%) rename interface/resources/qml/{controls-uit => controlsUit}/TabletContentSection.qml (99%) rename interface/resources/qml/{controls-uit => controlsUit}/TabletHeader.qml (96%) rename interface/resources/qml/{controls-uit => controlsUit}/TextAction.qml (96%) rename interface/resources/qml/{controls-uit => controlsUit}/TextEdit.qml (95%) rename interface/resources/qml/{controls-uit => controlsUit}/TextField.qml (99%) rename interface/resources/qml/{controls-uit => controlsUit}/ToolTip.qml (100%) rename interface/resources/qml/{controls-uit => controlsUit}/Tree.qml (99%) rename interface/resources/qml/{controls-uit => controlsUit}/VerticalSpacer.qml (94%) rename interface/resources/qml/{controls-uit => controlsUit}/WebGlyphButton.qml (98%) rename interface/resources/qml/{controls-uit => controlsUit}/WebSpinner.qml (100%) rename interface/resources/qml/{controls-uit => controlsUit}/WebView.qml (100%) rename interface/resources/qml/{controls-uit => controlsUit}/qmldir (100%) rename interface/resources/qml/{styles-uit => stylesUit}/+android/HifiConstants.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/AnonymousProRegular.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/ButtonLabel.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/FiraSansRegular.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/FiraSansSemiBold.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/HiFiGlyphs.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/HifiConstants.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/IconButton.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/InfoItem.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/InputLabel.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/ListItem.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/Logs.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/OverlayTitle.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/RalewayBold.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/RalewayLight.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/RalewayRegular.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/RalewaySemiBold.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/SectionName.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/Separator.qml (97%) rename interface/resources/qml/{styles-uit => stylesUit}/ShortcutText.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/TabName.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/TextFieldInput.qml (100%) rename interface/resources/qml/{styles-uit => stylesUit}/qmldir (100%) diff --git a/interface/resources/qml/AudioScopeUI.qml b/interface/resources/qml/AudioScopeUI.qml index aa181dbf8d..91908807e2 100644 --- a/interface/resources/qml/AudioScopeUI.qml +++ b/interface/resources/qml/AudioScopeUI.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 -import "styles-uit" -import "controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit Item { id: root diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 4474cfb2cd..01de7a36f9 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -2,9 +2,9 @@ import QtQuick 2.5 import QtWebChannel 1.0 import QtWebEngine 1.5 -import "controls-uit" +import controlsUit 1.0 import "styles" as HifiStyles -import "styles-uit" +import stylesUit 1.0 import "windows" ScrollingWindow { diff --git a/interface/resources/qml/CurrentAPI.qml b/interface/resources/qml/CurrentAPI.qml index 96bfb5c36b..4ea45041c3 100644 --- a/interface/resources/qml/CurrentAPI.qml +++ b/interface/resources/qml/CurrentAPI.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 -import "styles-uit" -import "controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { id: root diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml index f18969fb2f..8c5900b4c3 100644 --- a/interface/resources/qml/InfoView.qml +++ b/interface/resources/qml/InfoView.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import Hifi 1.0 as Hifi -import "controls-uit" +import controlsUit 1.0 import "windows" as Windows Windows.ScrollingWindow { diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index 336858502d..12117aaba4 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -11,8 +11,8 @@ import Hifi 1.0 import QtQuick 2.4 -import "controls-uit" -import "styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "windows" import "LoginDialog" diff --git a/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml index 96b638c911..a40110b1e9 100644 --- a/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml @@ -13,8 +13,8 @@ import QtQuick 2.4 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: linkAccountBody diff --git a/interface/resources/qml/LoginDialog/+android/SignUpBody.qml b/interface/resources/qml/LoginDialog/+android/SignUpBody.qml index 3a44a8d741..10909e4c85 100644 --- a/interface/resources/qml/LoginDialog/+android/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/+android/SignUpBody.qml @@ -13,8 +13,8 @@ import QtQuick 2.4 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: signupBody diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml index fe4c511f1d..3a57061de4 100644 --- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -12,8 +12,8 @@ import Hifi 1.0 import QtQuick 2.4 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: completeProfileBody diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index dbe4235cdd..103761236d 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -13,8 +13,9 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 + Item { id: linkAccountBody clip: true diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml index 9cb1add704..7fe29e13f6 100644 --- a/interface/resources/qml/LoginDialog/SignInBody.qml +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -12,8 +12,8 @@ import Hifi 1.0 import QtQuick 2.7 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: signInBody diff --git a/interface/resources/qml/LoginDialog/SignUpBody.qml b/interface/resources/qml/LoginDialog/SignUpBody.qml index bb30696e4c..d3c898d76f 100644 --- a/interface/resources/qml/LoginDialog/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/SignUpBody.qml @@ -12,8 +12,8 @@ import Hifi 1.0 import QtQuick 2.7 import QtQuick.Controls 1.4 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: signupBody diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index bf05a36ce1..2a41353534 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -12,8 +12,8 @@ import Hifi 1.0 import QtQuick 2.4 import QtQuick.Controls 1.4 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: usernameCollisionBody diff --git a/interface/resources/qml/LoginDialog/WelcomeBody.qml b/interface/resources/qml/LoginDialog/WelcomeBody.qml index 551ec263b7..020e6db002 100644 --- a/interface/resources/qml/LoginDialog/WelcomeBody.qml +++ b/interface/resources/qml/LoginDialog/WelcomeBody.qml @@ -11,8 +11,8 @@ import Hifi 1.0 import QtQuick 2.4 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: welcomeBody diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index 8c4d6145ec..322535641d 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -13,8 +13,8 @@ import QtWebEngine 1.1 import QtWebChannel 1.0 import "windows" as Windows -import "controls-uit" as Controls -import "styles-uit" +import controlsUit 1.0 as Controls +import stylesUit 1.0 Windows.ScrollingWindow { id: root diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index bef6423e25..53e6bcc37d 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -2,9 +2,9 @@ import QtQuick 2.3 import "windows" as Windows import "controls" -import "controls-uit" as Controls +import controlsUit 1.0 as Controls import "styles" -import "styles-uit" +import stylesUit 1.0 Windows.Window { id: root diff --git a/interface/resources/qml/TabletBrowser.qml b/interface/resources/qml/TabletBrowser.qml index 141c1f25a7..720a904231 100644 --- a/interface/resources/qml/TabletBrowser.qml +++ b/interface/resources/qml/TabletBrowser.qml @@ -3,9 +3,9 @@ import QtWebChannel 1.0 import QtWebEngine 1.5 import "controls" -import "controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls import "styles" as HifiStyles -import "styles-uit" +import stylesUit 1.0 import "windows" Item { diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml index 5e05601ce4..9c22d0b65b 100644 --- a/interface/resources/qml/UpdateDialog.qml +++ b/interface/resources/qml/UpdateDialog.qml @@ -4,9 +4,9 @@ import QtQuick.Controls 1.3 import QtQuick.Controls.Styles 1.3 import QtGraphicalEffects 1.0 -import "controls-uit" +import controlsUit 1.0 import "styles" as HifiStyles -import "styles-uit" +import stylesUit 1.0 import "windows" ScrollingWindow { diff --git a/interface/resources/qml/controls/Button.qml b/interface/resources/qml/controls/Button.qml index 6cbdec5644..b677822c0e 100644 --- a/interface/resources/qml/controls/Button.qml +++ b/interface/resources/qml/controls/Button.qml @@ -3,7 +3,6 @@ import QtQuick.Controls 2.2 as Original import "." import "../styles" -import "../controls-uit" Original.Button { id: control diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml index 943f15e1de..cce32c137a 100644 --- a/interface/resources/qml/controls/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -4,7 +4,7 @@ import QtWebChannel 1.0 import QtQuick.Controls 2.2 -import "../styles-uit" as StylesUIt +import stylesUit 1.0 as StylesUIt Item { id: flick diff --git a/interface/resources/qml/controls/TabletWebButton.qml b/interface/resources/qml/controls/TabletWebButton.qml index d016f71f2d..140461d817 100644 --- a/interface/resources/qml/controls/TabletWebButton.qml +++ b/interface/resources/qml/controls/TabletWebButton.qml @@ -10,7 +10,7 @@ import Hifi 1.0 import QtQuick 2.4 -import "../styles-uit" +import stylesUit 1.0 Rectangle { property alias text: label.text diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml index bb037ad478..be11f16498 100644 --- a/interface/resources/qml/controls/TabletWebScreen.qml +++ b/interface/resources/qml/controls/TabletWebScreen.qml @@ -1,5 +1,5 @@ import QtQuick 2.7 -import "../controls-uit" as HiFiControls +import controlsUit 1.0 as HiFiControls Item { id: root diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index db695dbfb2..0c5ca37e00 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -1,8 +1,8 @@ import QtQuick 2.7 import QtWebEngine 1.5 -import "../controls-uit" as HiFiControls +import controlsUit 1.0 as HiFiControls import "../styles" as HifiStyles -import "../styles-uit" +import stylesUit 1.0 Item { id: root diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 71bf69fdc8..375bcd50e0 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -1,5 +1,5 @@ import QtQuick 2.7 -import "../controls-uit" as HiFiControls +import controlsUit 1.0 as HiFiControls Item { width: parent !== null ? parent.width : undefined diff --git a/interface/resources/qml/controls-uit/+android/ImageButton.qml b/interface/resources/qml/controlsUit/+android/ImageButton.qml similarity index 96% rename from interface/resources/qml/controls-uit/+android/ImageButton.qml rename to interface/resources/qml/controlsUit/+android/ImageButton.qml index 5ebf7cd3e9..88eaf95d76 100644 --- a/interface/resources/qml/controls-uit/+android/ImageButton.qml +++ b/interface/resources/qml/controlsUit/+android/ImageButton.qml @@ -1,6 +1,6 @@ // // ImageButton.qml -// interface/resources/qml/controls-uit +// interface/resources/qml/controlsUit // // Created by Gabriel Calero & Cristian Duarte on 12 Oct 2017 // Copyright 2017 High Fidelity, Inc. @@ -11,7 +11,7 @@ import QtQuick 2.5 import QtQuick.Layouts 1.3 -import "../styles-uit" as HifiStyles +import "../stylesUit" as HifiStyles Item { id: button @@ -79,4 +79,4 @@ Item { } } ] -} \ No newline at end of file +} diff --git a/interface/resources/qml/controls-uit/AttachmentsTable.qml b/interface/resources/qml/controlsUit/AttachmentsTable.qml similarity index 98% rename from interface/resources/qml/controls-uit/AttachmentsTable.qml rename to interface/resources/qml/controlsUit/AttachmentsTable.qml index 8ee9909ab8..a2677962da 100644 --- a/interface/resources/qml/controls-uit/AttachmentsTable.qml +++ b/interface/resources/qml/controlsUit/AttachmentsTable.qml @@ -13,8 +13,8 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.XmlListModel 2.0 -import "../styles-uit" -import "../controls-uit" as HifiControls +import "../stylesUit" +import "." as HifiControls import "../windows" import "../hifi/models" diff --git a/interface/resources/qml/controls-uit/BaseWebView.qml b/interface/resources/qml/controlsUit/BaseWebView.qml similarity index 100% rename from interface/resources/qml/controls-uit/BaseWebView.qml rename to interface/resources/qml/controlsUit/BaseWebView.qml diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controlsUit/Button.qml similarity index 99% rename from interface/resources/qml/controls-uit/Button.qml rename to interface/resources/qml/controlsUit/Button.qml index f1a6e4bb4a..6ea7ce4b4c 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controlsUit/Button.qml @@ -12,7 +12,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 as Original import TabletScriptingInterface 1.0 -import "../styles-uit" +import "../stylesUit" Original.Button { id: control; diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controlsUit/CheckBox.qml similarity index 99% rename from interface/resources/qml/controls-uit/CheckBox.qml rename to interface/resources/qml/controlsUit/CheckBox.qml index 6e4a3df010..abf08908fb 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controlsUit/CheckBox.qml @@ -11,7 +11,7 @@ import QtQuick 2.2 import QtQuick.Controls 2.2 as Original -import "../styles-uit" +import "../stylesUit" import TabletScriptingInterface 1.0 diff --git a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml b/interface/resources/qml/controlsUit/CheckBoxQQC2.qml similarity index 98% rename from interface/resources/qml/controls-uit/CheckBoxQQC2.qml rename to interface/resources/qml/controlsUit/CheckBoxQQC2.qml index 8a9686ff5e..91d35ecd58 100644 --- a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml +++ b/interface/resources/qml/controlsUit/CheckBoxQQC2.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import "../styles-uit" -import "../controls-uit" as HiFiControls +import "../stylesUit" +import "." as HiFiControls import TabletScriptingInterface 1.0 CheckBox { diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controlsUit/ComboBox.qml similarity index 99% rename from interface/resources/qml/controls-uit/ComboBox.qml rename to interface/resources/qml/controlsUit/ComboBox.qml index 245b565a62..8d1d7a5262 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controlsUit/ComboBox.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import "../styles-uit" -import "../controls-uit" as HifiControls +import "../stylesUit" +import "." as HifiControls FocusScope { id: root diff --git a/interface/resources/qml/controls-uit/ContentSection.qml b/interface/resources/qml/controlsUit/ContentSection.qml similarity index 99% rename from interface/resources/qml/controls-uit/ContentSection.qml rename to interface/resources/qml/controlsUit/ContentSection.qml index 47a13e9262..262c29220f 100644 --- a/interface/resources/qml/controls-uit/ContentSection.qml +++ b/interface/resources/qml/controlsUit/ContentSection.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../styles-uit" +import "../stylesUit" Column { property string name: "Content Section" diff --git a/interface/resources/qml/controls-uit/FilterBar.qml b/interface/resources/qml/controlsUit/FilterBar.qml similarity index 99% rename from interface/resources/qml/controls-uit/FilterBar.qml rename to interface/resources/qml/controlsUit/FilterBar.qml index 3d4e18ed48..0892018913 100644 --- a/interface/resources/qml/controls-uit/FilterBar.qml +++ b/interface/resources/qml/controlsUit/FilterBar.qml @@ -12,8 +12,8 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 -import "../styles-uit" -import "../controls-uit" as HifiControls +import "../stylesUit" +import "." as HifiControls Item { id: root; diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controlsUit/GlyphButton.qml similarity index 99% rename from interface/resources/qml/controls-uit/GlyphButton.qml rename to interface/resources/qml/controlsUit/GlyphButton.qml index 9129486720..17f7fba2d6 100644 --- a/interface/resources/qml/controls-uit/GlyphButton.qml +++ b/interface/resources/qml/controlsUit/GlyphButton.qml @@ -12,7 +12,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 as Original import TabletScriptingInterface 1.0 -import "../styles-uit" +import "../stylesUit" Original.Button { id: control diff --git a/interface/resources/qml/controls-uit/HorizontalRule.qml b/interface/resources/qml/controlsUit/HorizontalRule.qml similarity index 100% rename from interface/resources/qml/controls-uit/HorizontalRule.qml rename to interface/resources/qml/controlsUit/HorizontalRule.qml diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controlsUit/HorizontalSpacer.qml similarity index 94% rename from interface/resources/qml/controls-uit/HorizontalSpacer.qml rename to interface/resources/qml/controlsUit/HorizontalSpacer.qml index 545154ab44..efcabf2699 100644 --- a/interface/resources/qml/controls-uit/HorizontalSpacer.qml +++ b/interface/resources/qml/controlsUit/HorizontalSpacer.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../styles-uit" +import "../stylesUit" Item { id: root diff --git a/interface/resources/qml/controls-uit/ImageMessageBox.qml b/interface/resources/qml/controlsUit/ImageMessageBox.qml similarity index 98% rename from interface/resources/qml/controls-uit/ImageMessageBox.qml rename to interface/resources/qml/controlsUit/ImageMessageBox.qml index 74313f7ffe..46d93383a4 100644 --- a/interface/resources/qml/controls-uit/ImageMessageBox.qml +++ b/interface/resources/qml/controlsUit/ImageMessageBox.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import "../styles-uit" +import "../stylesUit" Item { id: imageBox diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controlsUit/Key.qml similarity index 100% rename from interface/resources/qml/controls-uit/Key.qml rename to interface/resources/qml/controlsUit/Key.qml diff --git a/interface/resources/qml/controls-uit/Keyboard.qml b/interface/resources/qml/controlsUit/Keyboard.qml similarity index 100% rename from interface/resources/qml/controls-uit/Keyboard.qml rename to interface/resources/qml/controlsUit/Keyboard.qml diff --git a/interface/resources/qml/controls-uit/Label.qml b/interface/resources/qml/controlsUit/Label.qml similarity index 97% rename from interface/resources/qml/controls-uit/Label.qml rename to interface/resources/qml/controlsUit/Label.qml index 4c7051b495..7f208cde88 100644 --- a/interface/resources/qml/controls-uit/Label.qml +++ b/interface/resources/qml/controlsUit/Label.qml @@ -10,7 +10,7 @@ import QtQuick 2.7 -import "../styles-uit" +import "../stylesUit" RalewaySemiBold { HifiConstants { id: hifi } diff --git a/interface/resources/qml/controls-uit/QueuedButton.qml b/interface/resources/qml/controlsUit/QueuedButton.qml similarity index 98% rename from interface/resources/qml/controls-uit/QueuedButton.qml rename to interface/resources/qml/controlsUit/QueuedButton.qml index 6612d582df..70ad9eb112 100644 --- a/interface/resources/qml/controls-uit/QueuedButton.qml +++ b/interface/resources/qml/controlsUit/QueuedButton.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../styles-uit" +import "../stylesUit" import "." as HifiControls HifiControls.Button { diff --git a/interface/resources/qml/controls-uit/RadioButton.qml b/interface/resources/qml/controlsUit/RadioButton.qml similarity index 97% rename from interface/resources/qml/controls-uit/RadioButton.qml rename to interface/resources/qml/controlsUit/RadioButton.qml index 56324c55d7..ad62a77aa7 100644 --- a/interface/resources/qml/controls-uit/RadioButton.qml +++ b/interface/resources/qml/controlsUit/RadioButton.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import QtQuick.Controls 2.2 as Original -import "../styles-uit" -import "../controls-uit" as HifiControls +import "../stylesUit" +import "." as HifiControls import TabletScriptingInterface 1.0 diff --git a/interface/resources/qml/controls-uit/ScrollBar.qml b/interface/resources/qml/controlsUit/ScrollBar.qml similarity index 98% rename from interface/resources/qml/controls-uit/ScrollBar.qml rename to interface/resources/qml/controlsUit/ScrollBar.qml index 125e84e585..bcb1f62429 100644 --- a/interface/resources/qml/controls-uit/ScrollBar.qml +++ b/interface/resources/qml/controlsUit/ScrollBar.qml @@ -11,7 +11,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import "../styles-uit" +import "../stylesUit" ScrollBar { visible: size < 1.0 diff --git a/interface/resources/qml/controls-uit/Separator.qml b/interface/resources/qml/controlsUit/Separator.qml similarity index 98% rename from interface/resources/qml/controls-uit/Separator.qml rename to interface/resources/qml/controlsUit/Separator.qml index 3350764ae9..da6b9adf57 100644 --- a/interface/resources/qml/controls-uit/Separator.qml +++ b/interface/resources/qml/controlsUit/Separator.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import "../styles-uit" +import "../stylesUit" Item { property int colorScheme: 0; diff --git a/interface/resources/qml/controls-uit/Slider.qml b/interface/resources/qml/controlsUit/Slider.qml similarity index 98% rename from interface/resources/qml/controls-uit/Slider.qml rename to interface/resources/qml/controlsUit/Slider.qml index 2a5d4c137d..8cb08b69e2 100644 --- a/interface/resources/qml/controls-uit/Slider.qml +++ b/interface/resources/qml/controlsUit/Slider.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import "../styles-uit" -import "../controls-uit" as HifiControls +import "../stylesUit" +import "." as HifiControls Slider { id: slider diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controlsUit/SpinBox.qml similarity index 98% rename from interface/resources/qml/controls-uit/SpinBox.qml rename to interface/resources/qml/controlsUit/SpinBox.qml index 3d3ea7a75e..d24c7c5e8c 100644 --- a/interface/resources/qml/controls-uit/SpinBox.qml +++ b/interface/resources/qml/controlsUit/SpinBox.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import "../styles-uit" -import "../controls-uit" as HifiControls +import "../stylesUit" +import "." as HifiControls SpinBox { id: spinBox diff --git a/interface/resources/qml/controls-uit/Switch.qml b/interface/resources/qml/controlsUit/Switch.qml similarity index 99% rename from interface/resources/qml/controls-uit/Switch.qml rename to interface/resources/qml/controlsUit/Switch.qml index bfe86b1420..0961ef2500 100644 --- a/interface/resources/qml/controls-uit/Switch.qml +++ b/interface/resources/qml/controlsUit/Switch.qml @@ -11,7 +11,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 as Original -import "../styles-uit" +import "../stylesUit" Item { id: rootSwitch; diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controlsUit/Table.qml similarity index 99% rename from interface/resources/qml/controls-uit/Table.qml rename to interface/resources/qml/controlsUit/Table.qml index ce4e1c376a..ab74361046 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controlsUit/Table.qml @@ -13,7 +13,7 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Controls 2.3 as QQC2 -import "../styles-uit" +import "../stylesUit" TableView { id: tableView diff --git a/interface/resources/qml/controls-uit/TabletContentSection.qml b/interface/resources/qml/controlsUit/TabletContentSection.qml similarity index 99% rename from interface/resources/qml/controls-uit/TabletContentSection.qml rename to interface/resources/qml/controlsUit/TabletContentSection.qml index c34f4afdd6..dccaf31bbe 100644 --- a/interface/resources/qml/controls-uit/TabletContentSection.qml +++ b/interface/resources/qml/controlsUit/TabletContentSection.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../styles-uit" +import "../stylesUit" Column { property string name: "Content Section" diff --git a/interface/resources/qml/controls-uit/TabletHeader.qml b/interface/resources/qml/controlsUit/TabletHeader.qml similarity index 96% rename from interface/resources/qml/controls-uit/TabletHeader.qml rename to interface/resources/qml/controlsUit/TabletHeader.qml index 56203de286..f626700742 100644 --- a/interface/resources/qml/controls-uit/TabletHeader.qml +++ b/interface/resources/qml/controlsUit/TabletHeader.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../styles-uit" +import "../stylesUit" Rectangle { diff --git a/interface/resources/qml/controls-uit/TextAction.qml b/interface/resources/qml/controlsUit/TextAction.qml similarity index 96% rename from interface/resources/qml/controls-uit/TextAction.qml rename to interface/resources/qml/controlsUit/TextAction.qml index 1745a6c273..a0a1bb7d07 100644 --- a/interface/resources/qml/controls-uit/TextAction.qml +++ b/interface/resources/qml/controlsUit/TextAction.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../styles-uit" -import "../controls-uit" as HifiControls +import "../stylesUit" +import "." as HifiControls Item { property string icon: "" diff --git a/interface/resources/qml/controls-uit/TextEdit.qml b/interface/resources/qml/controlsUit/TextEdit.qml similarity index 95% rename from interface/resources/qml/controls-uit/TextEdit.qml rename to interface/resources/qml/controlsUit/TextEdit.qml index a72a3b13d8..7446c5040f 100644 --- a/interface/resources/qml/controls-uit/TextEdit.qml +++ b/interface/resources/qml/controlsUit/TextEdit.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import "../styles-uit" +import "../stylesUit" TextEdit { diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controlsUit/TextField.qml similarity index 99% rename from interface/resources/qml/controls-uit/TextField.qml rename to interface/resources/qml/controlsUit/TextField.qml index 917068ac01..d78f3a1340 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controlsUit/TextField.qml @@ -12,8 +12,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 -import "../styles-uit" -import "../controls-uit" as HifiControls +import "../stylesUit" +import "." as HifiControls TextField { id: textField diff --git a/interface/resources/qml/controls-uit/ToolTip.qml b/interface/resources/qml/controlsUit/ToolTip.qml similarity index 100% rename from interface/resources/qml/controls-uit/ToolTip.qml rename to interface/resources/qml/controlsUit/ToolTip.qml diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controlsUit/Tree.qml similarity index 99% rename from interface/resources/qml/controls-uit/Tree.qml rename to interface/resources/qml/controlsUit/Tree.qml index 5199a10a27..f2c49095b1 100644 --- a/interface/resources/qml/controls-uit/Tree.qml +++ b/interface/resources/qml/controlsUit/Tree.qml @@ -15,7 +15,7 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Controls 2.2 as QQC2 -import "../styles-uit" +import "../stylesUit" TreeView { id: treeView diff --git a/interface/resources/qml/controls-uit/VerticalSpacer.qml b/interface/resources/qml/controlsUit/VerticalSpacer.qml similarity index 94% rename from interface/resources/qml/controls-uit/VerticalSpacer.qml rename to interface/resources/qml/controlsUit/VerticalSpacer.qml index 2df65f1002..4c93aa1801 100644 --- a/interface/resources/qml/controls-uit/VerticalSpacer.qml +++ b/interface/resources/qml/controlsUit/VerticalSpacer.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../styles-uit" +import "../stylesUit" Item { id: root diff --git a/interface/resources/qml/controls-uit/WebGlyphButton.qml b/interface/resources/qml/controlsUit/WebGlyphButton.qml similarity index 98% rename from interface/resources/qml/controls-uit/WebGlyphButton.qml rename to interface/resources/qml/controlsUit/WebGlyphButton.qml index fd7cd001b2..7739ecd5e7 100644 --- a/interface/resources/qml/controls-uit/WebGlyphButton.qml +++ b/interface/resources/qml/controlsUit/WebGlyphButton.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import QtQuick.Controls 2.2 as Original -import "../styles-uit" +import "../stylesUit" Original.Button { id: control diff --git a/interface/resources/qml/controls-uit/WebSpinner.qml b/interface/resources/qml/controlsUit/WebSpinner.qml similarity index 100% rename from interface/resources/qml/controls-uit/WebSpinner.qml rename to interface/resources/qml/controlsUit/WebSpinner.qml diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controlsUit/WebView.qml similarity index 100% rename from interface/resources/qml/controls-uit/WebView.qml rename to interface/resources/qml/controlsUit/WebView.qml diff --git a/interface/resources/qml/controls-uit/qmldir b/interface/resources/qml/controlsUit/qmldir similarity index 100% rename from interface/resources/qml/controls-uit/qmldir rename to interface/resources/qml/controlsUit/qmldir diff --git a/interface/resources/qml/dialogs/AssetDialog.qml b/interface/resources/qml/dialogs/AssetDialog.qml index e8d28e9b37..b8eaab0b8d 100644 --- a/interface/resources/qml/dialogs/AssetDialog.qml +++ b/interface/resources/qml/dialogs/AssetDialog.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import Qt.labs.settings 1.0 -import "../styles-uit" +import stylesUit 1.0 import "../windows" import "assetDialog" diff --git a/interface/resources/qml/dialogs/CustomQueryDialog.qml b/interface/resources/qml/dialogs/CustomQueryDialog.qml index 0c86b93c4b..026068eee1 100644 --- a/interface/resources/qml/dialogs/CustomQueryDialog.qml +++ b/interface/resources/qml/dialogs/CustomQueryDialog.qml @@ -12,8 +12,8 @@ import QtQuick 2.7; import QtQuick.Dialogs 1.2 as OriginalDialogs; import QtQuick.Controls 2.3 -import "../controls-uit"; -import "../styles-uit"; +import controlsUit 1.0 +import stylesUit 1.0 import "../windows"; ModalWindow { diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 6651af0db3..b7340575dd 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -16,8 +16,8 @@ import QtQuick.Controls 1.4 as QQC1 import QtQuick.Controls 2.3 import ".." -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" import "fileDialog" diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index b5ac6cab72..9428e3ab6e 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import QtQuick.Dialogs 1.2 as OriginalDialogs -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" import "messageDialog" diff --git a/interface/resources/qml/dialogs/PreferencesDialog.qml b/interface/resources/qml/dialogs/PreferencesDialog.qml index fffd0e2ed9..9df1d0b963 100644 --- a/interface/resources/qml/dialogs/PreferencesDialog.qml +++ b/interface/resources/qml/dialogs/PreferencesDialog.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../controls-uit" as HifiControls -import "../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 import "../windows" import "preferences" diff --git a/interface/resources/qml/dialogs/QueryDialog.qml b/interface/resources/qml/dialogs/QueryDialog.qml index 41ee30e6d5..9cfb3011bd 100644 --- a/interface/resources/qml/dialogs/QueryDialog.qml +++ b/interface/resources/qml/dialogs/QueryDialog.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" ModalWindow { diff --git a/interface/resources/qml/dialogs/TabletAssetDialog.qml b/interface/resources/qml/dialogs/TabletAssetDialog.qml index 897378e40c..b3bd45f972 100644 --- a/interface/resources/qml/dialogs/TabletAssetDialog.qml +++ b/interface/resources/qml/dialogs/TabletAssetDialog.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../styles-uit" +import stylesUit 1.0 import "../windows" import "assetDialog" diff --git a/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml b/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml index 81a2c5c1e0..c7772984ab 100644 --- a/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletCustomQueryDialog.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Dialogs 1.2 as OriginalDialogs import QtQuick.Controls 2.3 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" TabletModalWindow { diff --git a/interface/resources/qml/dialogs/TabletFileDialog.qml b/interface/resources/qml/dialogs/TabletFileDialog.qml index 6848c230e3..3be6e30dd0 100644 --- a/interface/resources/qml/dialogs/TabletFileDialog.qml +++ b/interface/resources/qml/dialogs/TabletFileDialog.qml @@ -16,8 +16,8 @@ import QtQuick.Controls 1.4 as QQC1 import QtQuick.Controls 2.3 import ".." -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" import "fileDialog" diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index c85b2b2ba0..6314921286 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -11,8 +11,8 @@ import Hifi 1.0 import QtQuick 2.5 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" import "../LoginDialog" diff --git a/interface/resources/qml/dialogs/TabletMessageBox.qml b/interface/resources/qml/dialogs/TabletMessageBox.qml index fabe0dd247..1e6f0734ad 100644 --- a/interface/resources/qml/dialogs/TabletMessageBox.qml +++ b/interface/resources/qml/dialogs/TabletMessageBox.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import QtQuick.Dialogs 1.2 as OriginalDialogs -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" import "messageDialog" diff --git a/interface/resources/qml/dialogs/TabletQueryDialog.qml b/interface/resources/qml/dialogs/TabletQueryDialog.qml index 5746a3d67c..8f63730b8e 100644 --- a/interface/resources/qml/dialogs/TabletQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletQueryDialog.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Dialogs 1.2 as OriginalDialogs import QtQuick.Controls 2.3 -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../windows" TabletModalWindow { diff --git a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml index c3e842bc2f..da976ef3e1 100644 --- a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml +++ b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 import QtQuick.Controls 1.5 as QQC1 -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../fileDialog" diff --git a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml index 50a10974b5..6c042b5598 100644 --- a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml +++ b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../../controls-uit" +import controlsUit 1.0 ComboBox { id: root diff --git a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml index 8411980db7..f5715fa2c2 100644 --- a/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml +++ b/interface/resources/qml/dialogs/messageDialog/MessageDialogButton.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import QtQuick.Dialogs 1.2 -import "../../controls-uit" +import controlsUit 1.0 Button { property var dialog; diff --git a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml index 0efc3776b3..9505e70530 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../../controls-uit" +import controlsUit 1.0 import "../../hifi/tablet/tabletWindows/preferences" Preference { diff --git a/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml b/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml index 2cf50891c9..6059f8ff1c 100644 --- a/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml +++ b/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "../../dialogs" -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/ButtonPreference.qml b/interface/resources/qml/dialogs/preferences/ButtonPreference.qml index 454a9124ae..09c5b4329d 100644 --- a/interface/resources/qml/dialogs/preferences/ButtonPreference.qml +++ b/interface/resources/qml/dialogs/preferences/ButtonPreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import TabletScriptingInterface 1.0 -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml b/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml index e2172d8eda..f6f840bbe8 100644 --- a/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml +++ b/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import TabletScriptingInterface 1.0 -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml b/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml index 3b3efaf520..98cb397976 100644 --- a/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml +++ b/interface/resources/qml/dialogs/preferences/ComboBoxPreference.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../../controls-uit" as HiFiControls -import "../../styles-uit" +import controlsUit 1.0 as HiFiControls +import stylesUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/EditablePreference.qml b/interface/resources/qml/dialogs/preferences/EditablePreference.qml index 8acf8e1f76..e0c79ebba0 100644 --- a/interface/resources/qml/dialogs/preferences/EditablePreference.qml +++ b/interface/resources/qml/dialogs/preferences/EditablePreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "../../dialogs" -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml index cfc2e94ed9..f963003c59 100644 --- a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml +++ b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/RadioButtonsPreference.qml b/interface/resources/qml/dialogs/preferences/RadioButtonsPreference.qml index 103904a666..0a09d8d609 100644 --- a/interface/resources/qml/dialogs/preferences/RadioButtonsPreference.qml +++ b/interface/resources/qml/dialogs/preferences/RadioButtonsPreference.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/Section.qml b/interface/resources/qml/dialogs/preferences/Section.qml index c2c6583b7e..a9b755ad83 100644 --- a/interface/resources/qml/dialogs/preferences/Section.qml +++ b/interface/resources/qml/dialogs/preferences/Section.qml @@ -12,8 +12,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import Hifi 1.0 -import "../../controls-uit" as HiFiControls -import "../../styles-uit" +import controlsUit 1.0 as HiFiControls +import stylesUit 1.0 import "." Preference { diff --git a/interface/resources/qml/dialogs/preferences/SliderPreference.qml b/interface/resources/qml/dialogs/preferences/SliderPreference.qml index 2bdda09fc3..c8a2aae158 100644 --- a/interface/resources/qml/dialogs/preferences/SliderPreference.qml +++ b/interface/resources/qml/dialogs/preferences/SliderPreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "../../dialogs" -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/SpinBoxPreference.qml b/interface/resources/qml/dialogs/preferences/SpinBoxPreference.qml index b2c334b674..1b080c2759 100644 --- a/interface/resources/qml/dialogs/preferences/SpinBoxPreference.qml +++ b/interface/resources/qml/dialogs/preferences/SpinBoxPreference.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/SpinnerSliderPreference.qml b/interface/resources/qml/dialogs/preferences/SpinnerSliderPreference.qml index 126e62fc30..cbc804d9d7 100644 --- a/interface/resources/qml/dialogs/preferences/SpinnerSliderPreference.qml +++ b/interface/resources/qml/dialogs/preferences/SpinnerSliderPreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "../../dialogs" -import "../../controls-uit" +import controlsUit 1.0 Preference { id: root diff --git a/interface/resources/qml/hifi/+android/ActionBar.qml b/interface/resources/qml/hifi/+android/ActionBar.qml index d487901d6f..3c58156f30 100644 --- a/interface/resources/qml/hifi/+android/ActionBar.qml +++ b/interface/resources/qml/hifi/+android/ActionBar.qml @@ -3,8 +3,8 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls import ".." diff --git a/interface/resources/qml/hifi/+android/AudioBar.qml b/interface/resources/qml/hifi/+android/AudioBar.qml index 6cc17fccf7..912572fdf8 100644 --- a/interface/resources/qml/hifi/+android/AudioBar.qml +++ b/interface/resources/qml/hifi/+android/AudioBar.qml @@ -3,8 +3,8 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls import ".." diff --git a/interface/resources/qml/hifi/+android/AvatarOption.qml b/interface/resources/qml/hifi/+android/AvatarOption.qml index 85d7e52eb2..7eba3c2a67 100644 --- a/interface/resources/qml/hifi/+android/AvatarOption.qml +++ b/interface/resources/qml/hifi/+android/AvatarOption.qml @@ -11,7 +11,7 @@ import QtQuick.Layouts 1.3 import QtQuick 2.5 -import "../controls-uit" as HifiControlsUit +import controlsUit 1.0 as HifiControlsUit ColumnLayout { id: itemRoot diff --git a/interface/resources/qml/hifi/+android/StatsBar.qml b/interface/resources/qml/hifi/+android/StatsBar.qml index aee438b44f..64e93b4a08 100644 --- a/interface/resources/qml/hifi/+android/StatsBar.qml +++ b/interface/resources/qml/hifi/+android/StatsBar.qml @@ -3,8 +3,8 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls import ".." diff --git a/interface/resources/qml/hifi/+android/WindowHeader.qml b/interface/resources/qml/hifi/+android/WindowHeader.qml index 4ec0a0c6e6..5316fc4786 100644 --- a/interface/resources/qml/hifi/+android/WindowHeader.qml +++ b/interface/resources/qml/hifi/+android/WindowHeader.qml @@ -16,8 +16,8 @@ import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 import "." import "../styles" as HifiStyles -import "../styles-uit" -import "../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../controls" as HifiControls import ".." diff --git a/interface/resources/qml/hifi/+android/bottomHudOptions.qml b/interface/resources/qml/hifi/+android/bottomHudOptions.qml index 22beccf531..6b830d94c2 100644 --- a/interface/resources/qml/hifi/+android/bottomHudOptions.qml +++ b/interface/resources/qml/hifi/+android/bottomHudOptions.qml @@ -16,8 +16,8 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 import "../../styles" as HifiStyles -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls import ".." import "." diff --git a/interface/resources/qml/hifi/+android/modesbar.qml b/interface/resources/qml/hifi/+android/modesbar.qml index 994bf1efe4..1bf04fb8d9 100644 --- a/interface/resources/qml/hifi/+android/modesbar.qml +++ b/interface/resources/qml/hifi/+android/modesbar.qml @@ -3,8 +3,8 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls import ".." diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index 1a7f5bac40..ad337a6361 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -14,8 +14,8 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 -import "../styles-uit" -import "../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../windows" as Windows import "../dialogs" diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 83bf1e2c54..7f29324416 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -17,7 +17,7 @@ import QtGraphicalEffects 1.0 import TabletScriptingInterface 1.0 import "toolbars" -import "../styles-uit" +import stylesUit 1.0 Item { id: root; diff --git a/interface/resources/qml/hifi/ComboDialog.qml b/interface/resources/qml/hifi/ComboDialog.qml index e5dc8a9c1a..74d9c1019b 100644 --- a/interface/resources/qml/hifi/ComboDialog.qml +++ b/interface/resources/qml/hifi/ComboDialog.qml @@ -10,8 +10,8 @@ // import QtQuick 2.5 -import "../styles-uit" -import "../controls-uit" +import stylesUit 1.0 +import controlsUit 1.0 Item { property var dialogTitleText : ""; diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 4d342fe775..511d9377e5 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -8,7 +8,7 @@ import "../desktop" as OriginalDesktop import ".." import "." import "./toolbars" -import "../controls-uit" +import controlsUit 1.0 OriginalDesktop.Desktop { id: desktop diff --git a/interface/resources/qml/hifi/DesktopLetterboxMessage.qml b/interface/resources/qml/hifi/DesktopLetterboxMessage.qml index 9e9dcc75b2..048add24e5 100644 --- a/interface/resources/qml/hifi/DesktopLetterboxMessage.qml +++ b/interface/resources/qml/hifi/DesktopLetterboxMessage.qml @@ -10,7 +10,7 @@ // import QtQuick 2.5 -import "../styles-uit" +import stylesUit 1.0 Item { property alias text: popupText.text diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index 346481fe1f..4cfd4804b3 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -15,7 +15,7 @@ import Hifi 1.0 import QtQuick 2.5 import QtGraphicalEffects 1.0 import "toolbars" -import "../styles-uit" +import stylesUit 1.0 import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. Column { diff --git a/interface/resources/qml/hifi/LetterboxMessage.qml b/interface/resources/qml/hifi/LetterboxMessage.qml index 8a18d88842..68bebdd041 100644 --- a/interface/resources/qml/hifi/LetterboxMessage.qml +++ b/interface/resources/qml/hifi/LetterboxMessage.qml @@ -10,7 +10,7 @@ // import QtQuick 2.5 -import "../styles-uit" +import stylesUit 1.0 Item { property alias text: popupText.text diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index dfa6555150..242ca5ab57 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -13,8 +13,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtGraphicalEffects 1.0 -import "../styles-uit" -import "../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "toolbars" // references Users, UserActivityLogger, MyAvatar, Vec3, Quat, AddressManager, Account from root context diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 1384cb8711..368beaab47 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -15,8 +15,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtGraphicalEffects 1.0 import Qt.labs.settings 1.0 -import "../styles-uit" -import "../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../controls" as HifiControls import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. diff --git a/interface/resources/qml/hifi/SkyboxChanger.qml b/interface/resources/qml/hifi/SkyboxChanger.qml index f0c97a11a3..a66fc38415 100644 --- a/interface/resources/qml/hifi/SkyboxChanger.qml +++ b/interface/resources/qml/hifi/SkyboxChanger.qml @@ -10,8 +10,8 @@ // import QtQuick 2.5 -import "../styles-uit" -import "../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import QtQuick.Controls 2.2 Item { diff --git a/interface/resources/qml/hifi/SpectatorCamera.qml b/interface/resources/qml/hifi/SpectatorCamera.qml index 4bf80e410b..09b722b906 100644 --- a/interface/resources/qml/hifi/SpectatorCamera.qml +++ b/interface/resources/qml/hifi/SpectatorCamera.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.7 -import "../styles-uit" -import "../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../controls" as HifiControls // references HMD, XXX from root context diff --git a/interface/resources/qml/hifi/TabletTextButton.qml b/interface/resources/qml/hifi/TabletTextButton.qml index e5ff1d381d..6c9e0331df 100644 --- a/interface/resources/qml/hifi/TabletTextButton.qml +++ b/interface/resources/qml/hifi/TabletTextButton.qml @@ -10,7 +10,7 @@ import Hifi 1.0 import QtQuick 2.4 -import "../styles-uit" +import stylesUit 1.0 Rectangle { property alias text: label.text diff --git a/interface/resources/qml/hifi/TextButton.qml b/interface/resources/qml/hifi/TextButton.qml index 02e49d86e4..61588a9603 100644 --- a/interface/resources/qml/hifi/TextButton.qml +++ b/interface/resources/qml/hifi/TextButton.qml @@ -9,7 +9,7 @@ // import Hifi 1.0 import QtQuick 2.4 -import "../styles-uit" +import stylesUit 1.0 Rectangle { property alias text: label.text; diff --git a/interface/resources/qml/hifi/WebBrowser.qml b/interface/resources/qml/hifi/WebBrowser.qml index ab93752d92..c05de26471 100644 --- a/interface/resources/qml/hifi/WebBrowser.qml +++ b/interface/resources/qml/hifi/WebBrowser.qml @@ -18,8 +18,8 @@ import QtGraphicalEffects 1.0 import QtWebEngine 1.5 import QtWebChannel 1.0 -import "../styles-uit" -import "../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../windows" import "../controls" diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index f4a708567a..c8dd83cd62 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -15,8 +15,8 @@ import QtQuick 2.5 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" import "./" as AudioControls diff --git a/interface/resources/qml/hifi/audio/AudioTabButton.qml b/interface/resources/qml/hifi/audio/AudioTabButton.qml index 3a3ed90f5e..32331ccb6e 100644 --- a/interface/resources/qml/hifi/audio/AudioTabButton.qml +++ b/interface/resources/qml/hifi/audio/AudioTabButton.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 TabButton { id: control diff --git a/interface/resources/qml/hifi/audio/CheckBox.qml b/interface/resources/qml/hifi/audio/CheckBox.qml index 3a954d4004..5ab62a5091 100644 --- a/interface/resources/qml/hifi/audio/CheckBox.qml +++ b/interface/resources/qml/hifi/audio/CheckBox.qml @@ -11,7 +11,7 @@ import QtQuick 2.7 -import "../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls HifiControls.CheckBoxQQC2 { color: "white" diff --git a/interface/resources/qml/hifi/audio/PlaySampleSound.qml b/interface/resources/qml/hifi/audio/PlaySampleSound.qml index 2b9599a3cc..cfe55af9c4 100644 --- a/interface/resources/qml/hifi/audio/PlaySampleSound.qml +++ b/interface/resources/qml/hifi/audio/PlaySampleSound.qml @@ -13,8 +13,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls RowLayout { property var sound: null; diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 271aab87d1..2cf176c1e5 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtQuick.Controls 1.4 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../wallet" as HifiWallet import "../common" as HifiCommerceCommon diff --git a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml index 5901adc484..b7215500d2 100644 --- a/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml +++ b/interface/resources/qml/hifi/commerce/common/CommerceLightbox.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "qrc:////qml//styles-uit" -import "qrc:////qml//controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "qrc:////qml//controls" as HifiControls // references XXX from root context diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml index 99c2d89da8..0d0af875d1 100644 --- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml +++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.7 import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context diff --git a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml index 0b982893f1..c2d85b68b4 100644 --- a/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml +++ b/interface/resources/qml/hifi/commerce/common/FirstUseTutorial.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/ConnectionItem.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/ConnectionItem.qml index 41eacd68d5..1eb8af31e6 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/ConnectionItem.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/ConnectionItem.qml @@ -16,8 +16,8 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 -import "../../../../styles-uit" -import "../../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../../controls" as HifiControls import "../../wallet" as HifiWallet diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/RecipientDisplay.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/RecipientDisplay.qml index 9293dc83ab..9e1a967d50 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/RecipientDisplay.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/RecipientDisplay.qml @@ -15,8 +15,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.6 import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 -import "../../../../styles-uit" -import "../../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../../controls" as HifiControls import "../" as HifiCommerceCommon diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index bb4bb624bc..ec7146f33e 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -15,8 +15,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.6 import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 -import "../../../../styles-uit" -import "../../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../../controls" as HifiControls import "../" as HifiCommerceCommon import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index 885838a26e..8d0b93d11a 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../wallet" as HifiWallet diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 0828d86eff..c8ec7238d6 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -15,8 +15,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../wallet" as HifiWallet import TabletScriptingInterface 1.0 diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index e7b541b350..932a3ab6c4 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. import "../wallet" as HifiWallet diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 575edfc34d..1598aec41c 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.7 import QtQuick.Controls 2.2 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context diff --git a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml index 03af964830..ab05f55deb 100644 --- a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml +++ b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml index 8451c90836..6ddfe0da1c 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index c4abd40d2a..86d50e87ec 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../common" as HifiCommerceCommon diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml index e052b78876..179ffcf707 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls // references XXX from root context diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index f44e715703..ed4ba66b2b 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../common" as HifiCommerceCommon import "../common/sendAsset" diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletChoice.qml b/interface/resources/qml/hifi/commerce/wallet/WalletChoice.qml index 19065ee542..e7163a3641 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletChoice.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletChoice.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import "../common" as HifiCommerceCommon -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit Item { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index d32017189e..f18d4c7b44 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -15,8 +15,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 import QtQuick.Controls 2.2 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index ecd7234400..1cecebc41b 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../../controls" as HifiControls import "../common" as HifiCommerceCommon diff --git a/interface/resources/qml/hifi/dialogs/AboutDialog.qml b/interface/resources/qml/hifi/dialogs/AboutDialog.qml index b8e6e89aec..3d5d1a94a3 100644 --- a/interface/resources/qml/hifi/dialogs/AboutDialog.qml +++ b/interface/resources/qml/hifi/dialogs/AboutDialog.qml @@ -10,7 +10,7 @@ import QtQuick 2.8 -import "../../styles-uit" +import stylesUit 1.0 import "../../windows" ScrollingWindow { diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index 9a180a66f6..be17e65ab3 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -13,8 +13,8 @@ import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" import "../" diff --git a/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml b/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml index 579aa1cb1e..d26bf81e57 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import "../../styles-uit" +import stylesUit 1.0 Rectangle { width: 480 diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 0eeb252049..f665032b01 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -14,8 +14,8 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" import ".." diff --git a/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml b/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml index afe06897df..763f56b92b 100644 --- a/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml +++ b/interface/resources/qml/hifi/dialogs/TabletDCDialog.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" Rectangle { diff --git a/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml b/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml index 50df4dedbc..213dca8b48 100644 --- a/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml +++ b/interface/resources/qml/hifi/dialogs/TabletDebugWindow.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import Hifi 1.0 as Hifi -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Rectangle { id: root diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml index 24798af21a..4cfc99e0eb 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" Rectangle { diff --git a/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml b/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml index d5c5a5ee02..e86dfd7554 100644 --- a/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml +++ b/interface/resources/qml/hifi/dialogs/TabletEntityStatisticsItem.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Column { id: root diff --git a/interface/resources/qml/hifi/dialogs/TabletLODTools.qml b/interface/resources/qml/hifi/dialogs/TabletLODTools.qml index ab53f03477..bb3d668850 100644 --- a/interface/resources/qml/hifi/dialogs/TabletLODTools.qml +++ b/interface/resources/qml/hifi/dialogs/TabletLODTools.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" Rectangle { diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml index 018c8f5737..6cd220307d 100644 --- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml @@ -13,8 +13,8 @@ import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" import "../" diff --git a/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml b/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml index ce1abc6154..b1aa8e5c45 100644 --- a/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml +++ b/interface/resources/qml/hifi/dialogs/content/ModelBrowserContent.qml @@ -1,7 +1,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 -import "../../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls Column { width: pane.contentWidth diff --git a/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml b/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml index 96d75e340b..82d094144d 100644 --- a/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml +++ b/interface/resources/qml/hifi/dialogs/security/SecurityImageChange.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "qrc:////qml//styles-uit" as HifiStylesUit -import "qrc:////qml//controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "qrc:////qml//controls" as HifiControls // references XXX from root context diff --git a/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml b/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml index f058aad40a..366372622c 100644 --- a/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml +++ b/interface/resources/qml/hifi/dialogs/security/SecurityImageSelection.qml @@ -13,8 +13,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "qrc:////qml//styles-uit" as HifiStylesUit -import "qrc:////qml//controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "qrc:////qml//controls" as HifiControls // references XXX from root context diff --git a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml index e3115a5738..6b2aa331e8 100644 --- a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml +++ b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml @@ -10,9 +10,9 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../styles-uit" +import stylesUit 1.0 import "../../controls" -import "../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls Rectangle { diff --git a/interface/resources/qml/hifi/tablet/ControllerSettings.qml b/interface/resources/qml/hifi/tablet/ControllerSettings.qml index 6706830537..b8bbd71f33 100644 --- a/interface/resources/qml/hifi/tablet/ControllerSettings.qml +++ b/interface/resources/qml/hifi/tablet/ControllerSettings.qml @@ -11,9 +11,9 @@ import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import Qt.labs.settings 1.0 -import "../../styles-uit" +import stylesUit 1.0 import "../../controls" -import "../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls import "../../dialogs" import "../../dialogs/preferences" import "tabletWindows" diff --git a/interface/resources/qml/hifi/tablet/EditTabButton.qml b/interface/resources/qml/hifi/tablet/EditTabButton.qml index 13894f4d15..5fc4341eb8 100644 --- a/interface/resources/qml/hifi/tablet/EditTabButton.qml +++ b/interface/resources/qml/hifi/tablet/EditTabButton.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 TabButton { id: control diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index bf7dd3e66b..d9549feeb0 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -4,8 +4,8 @@ import QtWebChannel 1.0 import "../../controls" import "../toolbars" import QtGraphicalEffects 1.0 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 TabBar { id: editTabView diff --git a/interface/resources/qml/hifi/tablet/InputRecorder.qml b/interface/resources/qml/hifi/tablet/InputRecorder.qml index 527a6cacb4..9b63a612a8 100644 --- a/interface/resources/qml/hifi/tablet/InputRecorder.qml +++ b/interface/resources/qml/hifi/tablet/InputRecorder.qml @@ -9,8 +9,8 @@ import QtQuick 2.5 import Hifi 1.0 -import "../../styles-uit" -import "../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../../windows" import "../../dialogs" diff --git a/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml b/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml index 526a42f8e2..dde372648b 100644 --- a/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewMaterialDialog.qml @@ -13,8 +13,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Dialogs 1.2 as OriginalDialogs -import "../../styles-uit" -import "../../controls-uit" +import stylesUit 1.0 +import controlsUit 1.0 import "../dialogs" Rectangle { diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml index 553a4fd59f..9540979479 100644 --- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml @@ -12,8 +12,8 @@ import QtQuick 2.5 import QtQuick.Dialogs 1.2 as OriginalDialogs -import "../../styles-uit" -import "../../controls-uit" +import stylesUit 1.0 +import controlsUit 1.0 import "../dialogs" Rectangle { diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index f91642105f..2fc5cc4196 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -9,9 +9,9 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../../styles-uit" +import stylesUit 1.0 import "../../controls" -import "../../controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls import "." diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 3d518289fb..0f26ba20aa 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -18,8 +18,8 @@ import "../../styles" import "../../windows" import "../" import "../toolbars" -import "../../styles-uit" as HifiStyles -import "../../controls-uit" as HifiControls +import stylesUit 1.0 as HifiStyles +import controlsUit 1.0 as HifiControls import QtQuick.Controls 2.2 as QQC2 import QtQuick.Templates 2.2 as T diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 1922b02f93..934ed91995 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -6,7 +6,7 @@ import QtQuick.Layouts 1.3 import TabletScriptingInterface 1.0 import "." -import "../../styles-uit" +import stylesUit 1.0 import "../audio" as HifiAudio Item { diff --git a/interface/resources/qml/hifi/tablet/TabletMenu.qml b/interface/resources/qml/hifi/tablet/TabletMenu.qml index 6540d53fca..267fb9f0cf 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenu.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenu.qml @@ -7,7 +7,7 @@ import QtWebEngine 1.1 import "." -import "../../styles-uit" +import stylesUit 1.0 import "../../controls" FocusScope { diff --git a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml index 74f175e049..25db90c771 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuItem.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuItem.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: root diff --git a/interface/resources/qml/hifi/tablet/TabletMenuView.qml b/interface/resources/qml/hifi/tablet/TabletMenuView.qml index b632a17e57..73b0405984 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuView.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuView.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import TabletScriptingInterface 1.0 -import "../../styles-uit" +import stylesUit 1.0 import "." FocusScope { diff --git a/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml b/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml index d69d760b95..ce4e641476 100644 --- a/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletModelBrowserDialog.qml @@ -10,8 +10,8 @@ import QtQuick 2.5 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 import "../dialogs/content" Item { diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml index 871d1c92a9..8e91655dda 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml @@ -16,8 +16,8 @@ import QtQuick.Controls 1.4 as QQC1 import QtQuick.Controls 2.3 import ".." -import "../../../controls-uit" -import "../../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../../../windows" import "../../../dialogs/fileDialog" diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml index 3708f75114..57ca705352 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml @@ -12,8 +12,8 @@ import QtQuick 2.5 import "." import "./preferences" -import "../../../styles-uit" -import "../../../controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { id: dialog diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml index 6ac3f706e4..57fdeb482b 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml @@ -12,8 +12,8 @@ import QtQuick 2.5 import Hifi 1.0 import "../../../../dialogs/preferences" -import "../../../../controls-uit" as HiFiControls -import "../../../../styles-uit" +import controlsUit 1.0 as HiFiControls +import stylesUit 1.0 import "." Preference { diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml index 8c0e934971..36b927f5f9 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletBrowsablePreference.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "../../../../dialogs" -import "../../../../controls-uit" +import controlsUit 1.0 import "../" Preference { diff --git a/interface/resources/qml/styles-uit/+android/HifiConstants.qml b/interface/resources/qml/stylesUit/+android/HifiConstants.qml similarity index 100% rename from interface/resources/qml/styles-uit/+android/HifiConstants.qml rename to interface/resources/qml/stylesUit/+android/HifiConstants.qml diff --git a/interface/resources/qml/styles-uit/AnonymousProRegular.qml b/interface/resources/qml/stylesUit/AnonymousProRegular.qml similarity index 100% rename from interface/resources/qml/styles-uit/AnonymousProRegular.qml rename to interface/resources/qml/stylesUit/AnonymousProRegular.qml diff --git a/interface/resources/qml/styles-uit/ButtonLabel.qml b/interface/resources/qml/stylesUit/ButtonLabel.qml similarity index 100% rename from interface/resources/qml/styles-uit/ButtonLabel.qml rename to interface/resources/qml/stylesUit/ButtonLabel.qml diff --git a/interface/resources/qml/styles-uit/FiraSansRegular.qml b/interface/resources/qml/stylesUit/FiraSansRegular.qml similarity index 100% rename from interface/resources/qml/styles-uit/FiraSansRegular.qml rename to interface/resources/qml/stylesUit/FiraSansRegular.qml diff --git a/interface/resources/qml/styles-uit/FiraSansSemiBold.qml b/interface/resources/qml/stylesUit/FiraSansSemiBold.qml similarity index 100% rename from interface/resources/qml/styles-uit/FiraSansSemiBold.qml rename to interface/resources/qml/stylesUit/FiraSansSemiBold.qml diff --git a/interface/resources/qml/styles-uit/HiFiGlyphs.qml b/interface/resources/qml/stylesUit/HiFiGlyphs.qml similarity index 100% rename from interface/resources/qml/styles-uit/HiFiGlyphs.qml rename to interface/resources/qml/stylesUit/HiFiGlyphs.qml diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/stylesUit/HifiConstants.qml similarity index 100% rename from interface/resources/qml/styles-uit/HifiConstants.qml rename to interface/resources/qml/stylesUit/HifiConstants.qml diff --git a/interface/resources/qml/styles-uit/IconButton.qml b/interface/resources/qml/stylesUit/IconButton.qml similarity index 100% rename from interface/resources/qml/styles-uit/IconButton.qml rename to interface/resources/qml/stylesUit/IconButton.qml diff --git a/interface/resources/qml/styles-uit/InfoItem.qml b/interface/resources/qml/stylesUit/InfoItem.qml similarity index 100% rename from interface/resources/qml/styles-uit/InfoItem.qml rename to interface/resources/qml/stylesUit/InfoItem.qml diff --git a/interface/resources/qml/styles-uit/InputLabel.qml b/interface/resources/qml/stylesUit/InputLabel.qml similarity index 100% rename from interface/resources/qml/styles-uit/InputLabel.qml rename to interface/resources/qml/stylesUit/InputLabel.qml diff --git a/interface/resources/qml/styles-uit/ListItem.qml b/interface/resources/qml/stylesUit/ListItem.qml similarity index 100% rename from interface/resources/qml/styles-uit/ListItem.qml rename to interface/resources/qml/stylesUit/ListItem.qml diff --git a/interface/resources/qml/styles-uit/Logs.qml b/interface/resources/qml/stylesUit/Logs.qml similarity index 100% rename from interface/resources/qml/styles-uit/Logs.qml rename to interface/resources/qml/stylesUit/Logs.qml diff --git a/interface/resources/qml/styles-uit/OverlayTitle.qml b/interface/resources/qml/stylesUit/OverlayTitle.qml similarity index 100% rename from interface/resources/qml/styles-uit/OverlayTitle.qml rename to interface/resources/qml/stylesUit/OverlayTitle.qml diff --git a/interface/resources/qml/styles-uit/RalewayBold.qml b/interface/resources/qml/stylesUit/RalewayBold.qml similarity index 100% rename from interface/resources/qml/styles-uit/RalewayBold.qml rename to interface/resources/qml/stylesUit/RalewayBold.qml diff --git a/interface/resources/qml/styles-uit/RalewayLight.qml b/interface/resources/qml/stylesUit/RalewayLight.qml similarity index 100% rename from interface/resources/qml/styles-uit/RalewayLight.qml rename to interface/resources/qml/stylesUit/RalewayLight.qml diff --git a/interface/resources/qml/styles-uit/RalewayRegular.qml b/interface/resources/qml/stylesUit/RalewayRegular.qml similarity index 100% rename from interface/resources/qml/styles-uit/RalewayRegular.qml rename to interface/resources/qml/stylesUit/RalewayRegular.qml diff --git a/interface/resources/qml/styles-uit/RalewaySemiBold.qml b/interface/resources/qml/stylesUit/RalewaySemiBold.qml similarity index 100% rename from interface/resources/qml/styles-uit/RalewaySemiBold.qml rename to interface/resources/qml/stylesUit/RalewaySemiBold.qml diff --git a/interface/resources/qml/styles-uit/SectionName.qml b/interface/resources/qml/stylesUit/SectionName.qml similarity index 100% rename from interface/resources/qml/styles-uit/SectionName.qml rename to interface/resources/qml/stylesUit/SectionName.qml diff --git a/interface/resources/qml/styles-uit/Separator.qml b/interface/resources/qml/stylesUit/Separator.qml similarity index 97% rename from interface/resources/qml/styles-uit/Separator.qml rename to interface/resources/qml/stylesUit/Separator.qml index 4134b928a7..d9f11e192c 100644 --- a/interface/resources/qml/styles-uit/Separator.qml +++ b/interface/resources/qml/stylesUit/Separator.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import "../styles-uit" +import "." Item { // Size diff --git a/interface/resources/qml/styles-uit/ShortcutText.qml b/interface/resources/qml/stylesUit/ShortcutText.qml similarity index 100% rename from interface/resources/qml/styles-uit/ShortcutText.qml rename to interface/resources/qml/stylesUit/ShortcutText.qml diff --git a/interface/resources/qml/styles-uit/TabName.qml b/interface/resources/qml/stylesUit/TabName.qml similarity index 100% rename from interface/resources/qml/styles-uit/TabName.qml rename to interface/resources/qml/stylesUit/TabName.qml diff --git a/interface/resources/qml/styles-uit/TextFieldInput.qml b/interface/resources/qml/stylesUit/TextFieldInput.qml similarity index 100% rename from interface/resources/qml/styles-uit/TextFieldInput.qml rename to interface/resources/qml/stylesUit/TextFieldInput.qml diff --git a/interface/resources/qml/styles-uit/qmldir b/interface/resources/qml/stylesUit/qmldir similarity index 100% rename from interface/resources/qml/styles-uit/qmldir rename to interface/resources/qml/stylesUit/qmldir diff --git a/interface/resources/qml/windows/Decoration.qml b/interface/resources/qml/windows/Decoration.qml index f8fd9f4e6c..efaea6be8a 100644 --- a/interface/resources/qml/windows/Decoration.qml +++ b/interface/resources/qml/windows/Decoration.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" +import stylesUit 1.0 Rectangle { HifiConstants { id: hifi } diff --git a/interface/resources/qml/windows/DefaultFrame.qml b/interface/resources/qml/windows/DefaultFrame.qml index 60e744bec3..5a366e367b 100644 --- a/interface/resources/qml/windows/DefaultFrame.qml +++ b/interface/resources/qml/windows/DefaultFrame.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import "." -import "../styles-uit" +import stylesUit 1.0 Frame { HifiConstants { id: hifi } diff --git a/interface/resources/qml/windows/DefaultFrameDecoration.qml b/interface/resources/qml/windows/DefaultFrameDecoration.qml index 1ddd83976e..fb0dd55985 100644 --- a/interface/resources/qml/windows/DefaultFrameDecoration.qml +++ b/interface/resources/qml/windows/DefaultFrameDecoration.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" +import stylesUit 1.0 Decoration { HifiConstants { id: hifi } diff --git a/interface/resources/qml/windows/Fadable.qml b/interface/resources/qml/windows/Fadable.qml index 406c6be556..6d88fb067a 100644 --- a/interface/resources/qml/windows/Fadable.qml +++ b/interface/resources/qml/windows/Fadable.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 -import "../styles-uit" +import stylesUit 1.0 // Enable window visibility transitions FocusScope { diff --git a/interface/resources/qml/windows/Frame.qml b/interface/resources/qml/windows/Frame.qml index 271d4f2e07..7b0fbf8d8c 100644 --- a/interface/resources/qml/windows/Frame.qml +++ b/interface/resources/qml/windows/Frame.qml @@ -11,7 +11,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "../styles-uit" +import stylesUit 1.0 import "../js/Utils.js" as Utils Item { diff --git a/interface/resources/qml/windows/ModalFrame.qml b/interface/resources/qml/windows/ModalFrame.qml index cb23ccd5ad..ae149224e3 100644 --- a/interface/resources/qml/windows/ModalFrame.qml +++ b/interface/resources/qml/windows/ModalFrame.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import "." -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Frame { HifiConstants { id: hifi } diff --git a/interface/resources/qml/windows/ScrollingWindow.qml b/interface/resources/qml/windows/ScrollingWindow.qml index c156b80388..4cab96701e 100644 --- a/interface/resources/qml/windows/ScrollingWindow.qml +++ b/interface/resources/qml/windows/ScrollingWindow.qml @@ -14,8 +14,8 @@ import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" -import "../controls-uit" as HiFiControls +import stylesUit 1.0 +import controlsUit 1.0 as HiFiControls // FIXME how do I set the initial position of a window without // overriding places where the a individual client of the window diff --git a/interface/resources/qml/windows/TabletModalFrame.qml b/interface/resources/qml/windows/TabletModalFrame.qml index 550eec8357..1e9310eb5a 100644 --- a/interface/resources/qml/windows/TabletModalFrame.qml +++ b/interface/resources/qml/windows/TabletModalFrame.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import "." -import "../controls-uit" -import "../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Rectangle { diff --git a/interface/resources/qml/windows/ToolFrame.qml b/interface/resources/qml/windows/ToolFrame.qml index 20c86afb5e..bb2bada498 100644 --- a/interface/resources/qml/windows/ToolFrame.qml +++ b/interface/resources/qml/windows/ToolFrame.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" +import stylesUit 1.0 Frame { HifiConstants { id: hifi } diff --git a/interface/resources/qml/windows/ToolFrameDecoration.qml b/interface/resources/qml/windows/ToolFrameDecoration.qml index ba36a2a38c..4f149037b3 100644 --- a/interface/resources/qml/windows/ToolFrameDecoration.qml +++ b/interface/resources/qml/windows/ToolFrameDecoration.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" +import stylesUit 1.0 Decoration { id: root diff --git a/interface/resources/qml/windows/Window.qml b/interface/resources/qml/windows/Window.qml index 835967c628..9f180af55d 100644 --- a/interface/resources/qml/windows/Window.qml +++ b/interface/resources/qml/windows/Window.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 import "." -import "../styles-uit" +import stylesUit 1.0 // FIXME how do I set the initial position of a window without // overriding places where the a individual client of the window diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 74098f69c7..f67a356078 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -250,6 +250,7 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) { engine->setNetworkAccessManagerFactory(new QmlNetworkAccessManagerFactory); auto importList = engine->importPathList(); + importList.insert(importList.begin(), PathUtils::resourcesPath() + "qml/"); importList.insert(importList.begin(), PathUtils::resourcesPath()); engine->setImportPathList(importList); for (const auto& path : importList) { diff --git a/scripts/developer/tests/ControlsGallery.qml b/scripts/developer/tests/ControlsGallery.qml index ceb8a26dc9..9685fa6fe8 100644 --- a/scripts/developer/tests/ControlsGallery.qml +++ b/scripts/developer/tests/ControlsGallery.qml @@ -2,16 +2,9 @@ import QtQuick 2.10 import QtQuick.Window 2.10 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import "qrc:////qml//styles-uit" as HifiStylesUit -import "qrc:////qml//controls-uit" as HifiControlsUit -//uncomment to use from qmlscratch tool -//import '../../../interface/resources/qml/controls-uit' as HifiControlsUit -//import '../../../interface/resources/qml/styles-uit' - -//uncomment to use with HIFI_USE_SOURCE_TREE_RESOURCES=1 -//import '../../../resources/qml/controls-uit' as HifiControlsUit -//import '../../../resources/qml/styles-uit' +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit Item { visible: true From d0ac5128b02316817a52d8da76276757f99532e3 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sun, 17 Jun 2018 14:53:52 +0300 Subject: [PATCH 105/125] repurpose qmlscratch into 'controls gallery' launcher --- tests-manual/ui/qml/ControlsGalleryWindow.qml | 14 + tests-manual/ui/qml/Palettes.qml | 150 ---- tests-manual/ui/qml/ScrollingGraph.qml | 111 --- tests-manual/ui/qml/StubMenu.qml | 730 ------------------ tests-manual/ui/qml/Stubs.qml | 346 --------- tests-manual/ui/qml/TestControllers.qml | 160 ---- tests-manual/ui/qml/TestDialog.qml | 94 --- tests-manual/ui/qml/TestMenu.qml | 10 - tests-manual/ui/qml/TestRoot.qml | 43 -- .../ui/qml/controlDemo/ButtonPage.qml | 128 --- tests-manual/ui/qml/controlDemo/InputPage.qml | 114 --- .../ui/qml/controlDemo/ProgressPage.qml | 90 --- tests-manual/ui/qml/controlDemo/main.qml | 161 ---- tests-manual/ui/qml/main.qml | 461 ----------- tests-manual/ui/qmlscratch.pro | 28 +- tests-manual/ui/src/main.cpp | 125 +-- 16 files changed, 40 insertions(+), 2725 deletions(-) create mode 100644 tests-manual/ui/qml/ControlsGalleryWindow.qml delete mode 100644 tests-manual/ui/qml/Palettes.qml delete mode 100644 tests-manual/ui/qml/ScrollingGraph.qml delete mode 100644 tests-manual/ui/qml/StubMenu.qml delete mode 100644 tests-manual/ui/qml/Stubs.qml delete mode 100644 tests-manual/ui/qml/TestControllers.qml delete mode 100644 tests-manual/ui/qml/TestDialog.qml delete mode 100644 tests-manual/ui/qml/TestMenu.qml delete mode 100644 tests-manual/ui/qml/TestRoot.qml delete mode 100644 tests-manual/ui/qml/controlDemo/ButtonPage.qml delete mode 100644 tests-manual/ui/qml/controlDemo/InputPage.qml delete mode 100644 tests-manual/ui/qml/controlDemo/ProgressPage.qml delete mode 100644 tests-manual/ui/qml/controlDemo/main.qml delete mode 100644 tests-manual/ui/qml/main.qml diff --git a/tests-manual/ui/qml/ControlsGalleryWindow.qml b/tests-manual/ui/qml/ControlsGalleryWindow.qml new file mode 100644 index 0000000000..32fd62da36 --- /dev/null +++ b/tests-manual/ui/qml/ControlsGalleryWindow.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import QtQuick.Window 2.3 +import QtQuick.Controls 1.4 +import '../../../scripts/developer/tests' as Tests + +ApplicationWindow { + width: 640 + height: 480 + visible: true + + Tests.ControlsGallery { + anchors.fill: parent + } +} diff --git a/tests-manual/ui/qml/Palettes.qml b/tests-manual/ui/qml/Palettes.qml deleted file mode 100644 index 2bdf6eba8b..0000000000 --- a/tests-manual/ui/qml/Palettes.qml +++ /dev/null @@ -1,150 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 - -Rectangle { - color: "teal" - height: 512 - width: 192 - SystemPalette { id: sp; colorGroup: SystemPalette.Active } - SystemPalette { id: spi; colorGroup: SystemPalette.Inactive } - SystemPalette { id: spd; colorGroup: SystemPalette.Disabled } - - Column { - anchors.margins: 8 - anchors.fill: parent - spacing: 8 - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "base" } - Rectangle { height: parent.height; width: 16; color: sp.base } - Rectangle { height: parent.height; width: 16; color: spi.base } - Rectangle { height: parent.height; width: 16; color: spd.base } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "alternateBase" } - Rectangle { height: parent.height; width: 16; color: sp.alternateBase } - Rectangle { height: parent.height; width: 16; color: spi.alternateBase } - Rectangle { height: parent.height; width: 16; color: spd.alternateBase } - } - Item { - height: 16 - width:parent.width - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "dark" } - Rectangle { height: parent.height; width: 16; color: sp.dark } - Rectangle { height: parent.height; width: 16; color: spi.dark } - Rectangle { height: parent.height; width: 16; color: spd.dark } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "mid" } - Rectangle { height: parent.height; width: 16; color: sp.mid } - Rectangle { height: parent.height; width: 16; color: spi.mid } - Rectangle { height: parent.height; width: 16; color: spd.mid } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "mid light" } - Rectangle { height: parent.height; width: 16; color: sp.midlight } - Rectangle { height: parent.height; width: 16; color: spi.midlight } - Rectangle { height: parent.height; width: 16; color: spd.midlight } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "light" } - Rectangle { height: parent.height; width: 16; color: sp.light} - Rectangle { height: parent.height; width: 16; color: spi.light} - Rectangle { height: parent.height; width: 16; color: spd.light} - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "shadow" } - Rectangle { height: parent.height; width: 16; color: sp.shadow} - Rectangle { height: parent.height; width: 16; color: spi.shadow} - Rectangle { height: parent.height; width: 16; color: spd.shadow} - } - Item { - height: 16 - width:parent.width - } - - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "text" } - Rectangle { height: parent.height; width: 16; color: sp.text } - Rectangle { height: parent.height; width: 16; color: spi.text } - Rectangle { height: parent.height; width: 16; color: spd.text } - } - Item { - height: 16 - width:parent.width - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "window" } - Rectangle { height: parent.height; width: 16; color: sp.window } - Rectangle { height: parent.height; width: 16; color: spi.window } - Rectangle { height: parent.height; width: 16; color: spd.window } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "window text" } - Rectangle { height: parent.height; width: 16; color: sp.windowText } - Rectangle { height: parent.height; width: 16; color: spi.windowText } - Rectangle { height: parent.height; width: 16; color: spd.windowText } - } - Item { - height: 16 - width:parent.width - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "button" } - Rectangle { height: parent.height; width: 16; color: sp.button } - Rectangle { height: parent.height; width: 16; color: spi.button } - Rectangle { height: parent.height; width: 16; color: spd.button } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "buttonText" } - Rectangle { height: parent.height; width: 16; color: sp.buttonText } - Rectangle { height: parent.height; width: 16; color: spi.buttonText } - Rectangle { height: parent.height; width: 16; color: spd.buttonText } - } - Item { - height: 16 - width:parent.width - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "highlight" } - Rectangle { height: parent.height; width: 16; color: sp.highlight } - Rectangle { height: parent.height; width: 16; color: spi.highlight } - Rectangle { height: parent.height; width: 16; color: spd.highlight } - } - Row { - width: parent.width - height: 16 - Text { height: parent.height; width: 128; text: "highlighted text" } - Rectangle { height: parent.height; width: 16; color: sp.highlightedText} - Rectangle { height: parent.height; width: 16; color: spi.highlightedText} - Rectangle { height: parent.height; width: 16; color: spd.highlightedText} - } - } -} diff --git a/tests-manual/ui/qml/ScrollingGraph.qml b/tests-manual/ui/qml/ScrollingGraph.qml deleted file mode 100644 index 55523a23f4..0000000000 --- a/tests-manual/ui/qml/ScrollingGraph.qml +++ /dev/null @@ -1,111 +0,0 @@ -import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 - -Rectangle { - id: root - property int size: 64 - width: size - height: size - color: 'black' - property int controlId: 0 - property real value: 0.5 - property int scrollWidth: 1 - property real min: 0.0 - property real max: 1.0 - property bool log: false - property real range: max - min - property color lineColor: 'yellow' - property bool bar: false - property real lastHeight: -1 - property string label: "" - - function update() { - value = Controller.getValue(controlId); - if (log) { - var log = Math.log(10) / Math.log(Math.abs(value)); - var sign = Math.sign(value); - value = log * sign; - } - canvas.requestPaint(); - } - - function drawHeight() { - if (value < min) { - return 0; - } - if (value > max) { - return height; - } - return ((value - min) / range) * height; - } - - Timer { - interval: 50; running: true; repeat: true - onTriggered: root.update() - } - - Canvas { - id: canvas - anchors.fill: parent - antialiasing: false - - Text { - anchors.top: parent.top - text: root.label - color: 'white' - } - - Text { - anchors.right: parent.right - anchors.top: parent.top - text: root.max - color: 'white' - } - - Text { - anchors.right: parent.right - anchors.bottom: parent.bottom - text: root.min - color: 'white' - } - - function scroll() { - var ctx = canvas.getContext('2d'); - var image = ctx.getImageData(0, 0, canvas.width, canvas.height); - ctx.beginPath(); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(image, -root.scrollWidth, 0, canvas.width, canvas.height) - ctx.restore() - } - - onPaint: { - scroll(); - var ctx = canvas.getContext('2d'); - ctx.save(); - var currentHeight = root.drawHeight(); - if (root.lastHeight == -1) { - root.lastHeight = currentHeight - } - -// var x = canvas.width - root.drawWidth; -// var y = canvas.height - drawHeight; -// ctx.fillStyle = root.color -// ctx.fillRect(x, y, root.drawWidth, root.bar ? drawHeight : 1) -// ctx.fill(); -// ctx.restore() - - - ctx.beginPath(); - ctx.lineWidth = 1 - ctx.strokeStyle = root.lineColor - ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight) - ctx.stroke() - ctx.restore() - root.lastHeight = currentHeight - } - } -} - - diff --git a/tests-manual/ui/qml/StubMenu.qml b/tests-manual/ui/qml/StubMenu.qml deleted file mode 100644 index fd0298988a..0000000000 --- a/tests-manual/ui/qml/StubMenu.qml +++ /dev/null @@ -1,730 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 - -import "../../../interface/resources/qml/hifi" - -Menu { - property var menuOption: MenuOption {} - Item { - Action { - id: login; - text: menuOption.login - } - Action { - id: update; - text: "Update"; - enabled: false - } - Action { - id: crashReporter; - text: "Crash Reporter..."; - enabled: false - } - Action { - id: help; - text: menuOption.help - onTriggered: Application.showHelp() - } - Action { - id: aboutApp; - text: menuOption.aboutApp - } - Action { - id: quit; - text: menuOption.quit - } - - ExclusiveGroup { id: renderResolutionGroup } - Action { - id: renderResolutionOne; - exclusiveGroup: renderResolutionGroup; - text: menuOption.renderResolutionOne; - checkable: true; - checked: true - } - Action { - id: renderResolutionTwoThird; - exclusiveGroup: renderResolutionGroup; - text: menuOption.renderResolutionTwoThird; - checkable: true - } - Action { - id: renderResolutionHalf; - exclusiveGroup: renderResolutionGroup; - text: menuOption.renderResolutionHalf; - checkable: true - } - Action { - id: renderResolutionThird; - exclusiveGroup: renderResolutionGroup; - text: menuOption.renderResolutionThird; - checkable: true - } - Action { - id: renderResolutionQuarter; - exclusiveGroup: renderResolutionGroup; - text: menuOption.renderResolutionQuarter; - checkable: true - } - - ExclusiveGroup { id: ambientLightGroup } - Action { - id: renderAmbientLightGlobal; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLightGlobal; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight0; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight0; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight1; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight1; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight2; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight2; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight3; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight3; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight4; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight4; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight5; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight5; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight6; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight6; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight7; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight7; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight8; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight8; - checkable: true; - checked: true - } - Action { - id: renderAmbientLight9; - exclusiveGroup: ambientLightGroup; - text: menuOption.renderAmbientLight9; - checkable: true; - checked: true - } - Action { - id: preferences - shortcut: StandardKey.Preferences - text: menuOption.preferences - onTriggered: dialogsManager.editPreferences() - } - - } - - Menu { - title: "File" - MenuItem { - action: login - } - MenuItem { - action: update - } - MenuItem { - action: help - } - MenuItem { - action: crashReporter - } - MenuItem { - action: aboutApp - } - MenuItem { - action: quit - } - } - - Menu { - title: "Edit" - MenuItem { - text: "Undo" } - MenuItem { - text: "Redo" } - MenuItem { - text: menuOption.runningScripts - } - MenuItem { - text: menuOption.loadScript - } - MenuItem { - text: menuOption.loadScriptURL - } - MenuItem { - text: menuOption.stopAllScripts - } - MenuItem { - text: menuOption.reloadAllScripts - } - MenuItem { - text: menuOption.scriptEditor - } - MenuItem { - text: menuOption.console_ - } - MenuItem { - text: menuOption.reloadContent - } - MenuItem { - text: menuOption.packageModel - } - } - - Menu { - title: "Audio" - MenuItem { - text: menuOption.muteAudio; - checkable: true - } - MenuItem { - text: menuOption.audioTools; - checkable: true - } - } - Menu { - title: "Avatar" - // Avatar > Attachments... - MenuItem { - text: menuOption.attachments - } - Menu { - title: "Size" - // Avatar > Size > Increase - MenuItem { - text: menuOption.increaseAvatarSize - } - // Avatar > Size > Decrease - MenuItem { - text: menuOption.decreaseAvatarSize - } - // Avatar > Size > Reset - MenuItem { - text: menuOption.resetAvatarSize - } - } - // Avatar > Reset Sensors - MenuItem { - text: menuOption.resetSensors - } - } - Menu { - title: "Display" - } - Menu { - title: "View" - ExclusiveGroup { - id: cameraModeGroup - } - - MenuItem { - text: menuOption.firstPerson; - checkable: true; - exclusiveGroup: cameraModeGroup - } - MenuItem { - text: menuOption.thirdPerson; - checkable: true; - exclusiveGroup: cameraModeGroup - } - MenuItem { - text: menuOption.fullscreenMirror; - checkable: true; - exclusiveGroup: cameraModeGroup - } - MenuItem { - text: menuOption.independentMode; - checkable: true; - exclusiveGroup: cameraModeGroup - } - MenuItem { - text: menuOption.cameraEntityMode; - checkable: true; - exclusiveGroup: cameraModeGroup - } - MenuSeparator{} - MenuItem { - text: menuOption.miniMirror; - checkable: true - } - } - Menu { - title: "Navigate" - MenuItem { - text: "Home" } - MenuItem { - text: menuOption.addressBar - } - MenuItem { - text: "Directory" } - MenuItem { - text: menuOption.copyAddress - } - MenuItem { - text: menuOption.copyPath - } - } - Menu { - title: "Settings" - MenuItem { - text: "Advanced Menus" } - MenuItem { - text: "Developer Menus" } - MenuItem { - text: menuOption.preferences - } - MenuItem { - text: "Avatar..." } - MenuItem { - text: "Audio..." } - MenuItem { - text: "LOD..." } - MenuItem { - text: menuOption.inputMenu - } - } - Menu { - title: "Developer" - Menu { - title: "Render" - MenuItem { - text: menuOption.atmosphere; - checkable: true - } - MenuItem { - text: menuOption.worldAxes; - checkable: true - } - MenuItem { - text: menuOption.debugAmbientOcclusion; - checkable: true - } - MenuItem { - text: menuOption.antialiasing; - checkable: true - } - MenuItem { - text: menuOption.stars; - checkable: true - } - Menu { - title: menuOption.renderAmbientLight - MenuItem { - action: renderAmbientLightGlobal; } - MenuItem { - action: renderAmbientLight0; } - MenuItem { - action: renderAmbientLight1; } - MenuItem { - action: renderAmbientLight2; } - MenuItem { - action: renderAmbientLight3; } - MenuItem { - action: renderAmbientLight4; } - MenuItem { - action: renderAmbientLight5; } - MenuItem { - action: renderAmbientLight6; } - MenuItem { - action: renderAmbientLight7; } - MenuItem { - action: renderAmbientLight8; } - MenuItem { - action: renderAmbientLight9; } - } - MenuItem { - text: menuOption.throttleFPSIfNotFocus; - checkable: true - } - Menu { - title: menuOption.renderResolution - MenuItem { - action: renderResolutionOne - } - MenuItem { - action: renderResolutionTwoThird - } - MenuItem { - action: renderResolutionHalf - } - MenuItem { - action: renderResolutionThird - } - MenuItem { - action: renderResolutionQuarter - } - } - MenuItem { - text: menuOption.lodTools - } - } - Menu { - title: "Assets" - MenuItem { - text: menuOption.uploadAsset - } - MenuItem { - text: menuOption.assetMigration - } - } - Menu { - title: "Avatar" - Menu { - title: "Face Tracking" - MenuItem { - text: menuOption.noFaceTracking; - checkable: true - } - MenuItem { - text: menuOption.faceshift; - checkable: true - } - MenuItem { - text: menuOption.useCamera; - checkable: true - } - MenuSeparator{} - MenuItem { - text: menuOption.binaryEyelidControl; - checkable: true - } - MenuItem { - text: menuOption.coupleEyelids; - checkable: true - } - MenuItem { - text: menuOption.useAudioForMouth; - checkable: true - } - MenuItem { - text: menuOption.velocityFilter; - checkable: true - } - MenuItem { - text: menuOption.calibrateCamera - } - MenuSeparator{} - MenuItem { - text: menuOption.muteFaceTracking; - checkable: true - } - MenuItem { - text: menuOption.autoMuteAudio; - checkable: true - } - } - Menu { - title: "Eye Tracking" - MenuItem { - text: menuOption.sMIEyeTracking; - checkable: true - } - Menu { - title: "Calibrate" - MenuItem { - text: menuOption.onePointCalibration - } - MenuItem { - text: menuOption.threePointCalibration - } - MenuItem { - text: menuOption.fivePointCalibration - } - } - MenuItem { - text: menuOption.simulateEyeTracking; - checkable: true - } - } - MenuItem { - text: menuOption.avatarReceiveStats; - checkable: true - } - MenuItem { - text: menuOption.renderBoundingCollisionShapes; - checkable: true - } - MenuItem { - text: menuOption.renderLookAtVectors; - checkable: true - } - MenuItem { - text: menuOption.renderLookAtTargets; - checkable: true - } - MenuItem { - text: menuOption.renderFocusIndicator; - checkable: true - } - MenuItem { - text: menuOption.showWhosLookingAtMe; - checkable: true - } - MenuItem { - text: menuOption.fixGaze; - checkable: true - } - MenuItem { - text: menuOption.animDebugDrawDefaultPose; - checkable: true - } - MenuItem { - text: menuOption.animDebugDrawAnimPose; - checkable: true - } - MenuItem { - text: menuOption.animDebugDrawPosition; - checkable: true - } - MenuItem { - text: menuOption.meshVisible; - checkable: true - } - MenuItem { - text: menuOption.disableEyelidAdjustment; - checkable: true - } - MenuItem { - text: menuOption.turnWithHead; - checkable: true - } - MenuItem { - text: menuOption.keyboardMotorControl; - checkable: true - } - MenuItem { - text: menuOption.scriptedMotorControl; - checkable: true - } - MenuItem { - text: menuOption.enableCharacterController; - checkable: true - } - } - Menu { - title: "Hands" - MenuItem { - text: menuOption.displayHandTargets; - checkable: true - } - MenuItem { - text: menuOption.lowVelocityFilter; - checkable: true - } - Menu { - title: "Leap Motion" - MenuItem { - text: menuOption.leapMotionOnHMD; - checkable: true - } - } - } - Menu { - title: "Entities" - MenuItem { - text: menuOption.octreeStats - } - MenuItem { - text: menuOption.showRealtimeEntityStats; - checkable: true - } - } - Menu { - title: "Network" - MenuItem { - text: menuOption.reloadContent - } - MenuItem { - text: menuOption.disableNackPackets; - checkable: true - } - MenuItem { - text: menuOption.disableActivityLogger; - checkable: true - } - MenuItem { - text: menuOption.cachesSize - } - MenuItem { - text: menuOption.diskCacheEditor - } - MenuItem { - text: menuOption.showDSConnectTable - } - MenuItem { - text: menuOption.bandwidthDetails - } - } - Menu { - title: "Timing" - Menu { - title: "Performance Timer" - MenuItem { - text: menuOption.displayDebugTimingDetails; - checkable: true - } - MenuItem { - text: menuOption.onlyDisplayTopTen; - checkable: true - } - MenuItem { - text: menuOption.expandUpdateTiming; - checkable: true - } - MenuItem { - text: menuOption.expandMyAvatarTiming; - checkable: true - } - MenuItem { - text: menuOption.expandMyAvatarSimulateTiming; - checkable: true - } - MenuItem { - text: menuOption.expandOtherAvatarTiming; - checkable: true - } - MenuItem { - text: menuOption.expandPaintGLTiming; - checkable: true - } - } - MenuItem { - text: menuOption.frameTimer; - checkable: true - } - MenuItem { - text: menuOption.runTimingTests - } - MenuItem { - text: menuOption.pipelineWarnings; - checkable: true - } - MenuItem { - text: menuOption.logExtraTimings; - checkable: true - } - MenuItem { - text: menuOption.suppressShortTimings; - checkable: true - } - } - Menu { - title: "Audio" - MenuItem { - text: menuOption.audioNoiseReduction; - checkable: true - } - MenuItem { - text: menuOption.echoServerAudio; - checkable: true - } - MenuItem { - text: menuOption.echoLocalAudio; - checkable: true - } - MenuItem { - text: menuOption.muteEnvironment - } - Menu { - title: "Audio" - MenuItem { - text: menuOption.audioScope; - checkable: true - } - MenuItem { - text: menuOption.audioScopePause; - checkable: true - } - Menu { - title: "Display Frames" - ExclusiveGroup { - id: audioScopeFramesGroup - } - MenuItem { - exclusiveGroup: audioScopeFramesGroup; - text: menuOption.audioScopeFiveFrames; - checkable: true - } - MenuItem { - exclusiveGroup: audioScopeFramesGroup; - text: menuOption.audioScopeTwentyFrames; - checkable: true - } - MenuItem { - exclusiveGroup: audioScopeFramesGroup; - text: menuOption.audioScopeFiftyFrames; - checkable: true - } - } - MenuItem { - text: menuOption.audioNetworkStats - } - } - } - Menu { - title: "Physics" - MenuItem { - text: menuOption.physicsShowOwned; - checkable: true - } - MenuItem { - text: menuOption.physicsShowHulls; - checkable: true - } - } - MenuItem { - text: menuOption.displayCrashOptions; - checkable: true - } - MenuItem { - text: menuOption.crashInterface - } - MenuItem { - text: menuOption.log - } - MenuItem { - text: menuOption.stats; - checkable: true - } - } -} diff --git a/tests-manual/ui/qml/Stubs.qml b/tests-manual/ui/qml/Stubs.qml deleted file mode 100644 index 8c1465d54c..0000000000 --- a/tests-manual/ui/qml/Stubs.qml +++ /dev/null @@ -1,346 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 - -// Stubs for the global service objects set by Interface.cpp when creating the UI -// This is useful for testing inside Qt creator where these services don't actually exist. -Item { - - Item { - objectName: "offscreenFlags" - property bool navigationFocused: false - } - - Item { - objectName: "urlHandler" - function fixupUrl(url) { return url; } - function canHandleUrl(url) { return false; } - function handleUrl(url) { return true; } - } - - Item { - objectName: "Account" - function isLoggedIn() { return true; } - function getUsername() { return "Jherico"; } - } - - Item { - objectName: "GL" - property string vendor: "" - } - - Item { - objectName: "ApplicationCompositor" - property bool reticleOverDesktop: true - } - - Item { - objectName: "Controller" - function getRecommendedOverlayRect() { - return Qt.rect(0, 0, 1920, 1080); - } - } - - Item { - objectName: "Preferences" - // List of categories obtained by logging categories as they are added in Interface in Preferences::addPreference(). - property var categories: [ - "Avatar Basics", "Snapshots", "Scripts", "Privacy", "Level of Detail Tuning", "Avatar Tuning", "Avatar Camera", - "Audio", "Octree", "HMD", "Sixense Controllers", "Graphics" - ] - } - - Item { - objectName: "ScriptDiscoveryService" - //property var scriptsModelFilter: scriptsModel - signal scriptCountChanged() - property var _runningScripts:[ - { name: "wireFrameTest.js", url: "foo/wireframetest.js", path: "foo/wireframetest.js", local: true }, - { name: "edit.js", url: "foo/edit.js", path: "foo/edit.js", local: false }, - { name: "listAllScripts.js", url: "foo/listAllScripts.js", path: "foo/listAllScripts.js", local: false }, - { name: "users.js", url: "foo/users.js", path: "foo/users.js", local: false }, - ] - - function getRunning() { - return _runningScripts; - } - } - - Item { - objectName: "HMD" - property bool active: false - } - - Item { - id: menuHelper - objectName: "MenuHelper" - - Component { - id: modelMaker - ListModel { } - } - - function toModel(menu, parent) { - if (!parent) { parent = menuHelper } - var result = modelMaker.createObject(parent); - if (menu.type !== MenuItemType.Menu) { - console.warn("Not a menu: " + menu); - return result; - } - - var items = menu.items; - for (var i = 0; i < items.length; ++i) { - var item = items[i]; - switch (item.type) { - case 2: - result.append({"name": item.title, "item": item}) - break; - case 1: - result.append({"name": item.text, "item": item}) - break; - case 0: - result.append({"name": "", "item": item}) - break; - } - } - return result; - } - - } - - Item { - objectName: "Desktop" - - property string _OFFSCREEN_ROOT_OBJECT_NAME: "desktopRoot"; - property string _OFFSCREEN_DIALOG_OBJECT_NAME: "topLevelWindow"; - - - function findChild(item, name) { - for (var i = 0; i < item.children.length; ++i) { - if (item.children[i].objectName === name) { - return item.children[i]; - } - } - return null; - } - - function findParent(item, name) { - while (item) { - if (item.objectName === name) { - return item; - } - item = item.parent; - } - return null; - } - - function findDialog(item) { - item = findParent(item, _OFFSCREEN_DIALOG_OBJECT_NAME); - return item; - } - - function closeDialog(item) { - item = findDialog(item); - if (item) { - item.visible = false - } else { - console.warn("Could not find top level dialog") - } - } - - function getDesktop(item) { - while (item) { - if (item.desktopRoot) { - break; - } - item = item.parent; - } - return item - } - - function raise(item) { - var desktop = getDesktop(item); - if (desktop) { - desktop.raise(item); - } - } - } - - Menu { - id: root - objectName: "rootMenu" - - Menu { - title: "Audio" - } - - Menu { - title: "Avatar" - } - - Menu { - title: "Display" - ExclusiveGroup { id: displayMode } - Menu { - title: "More Stuff" - - Menu { title: "Empty" } - - MenuItem { - text: "Do Nothing" - onTriggered: console.log("Nothing") - } - } - MenuItem { - text: "Oculus" - exclusiveGroup: displayMode - checkable: true - } - MenuItem { - text: "OpenVR" - exclusiveGroup: displayMode - checkable: true - } - MenuItem { - text: "OSVR" - exclusiveGroup: displayMode - checkable: true - } - MenuItem { - text: "2D Screen" - exclusiveGroup: displayMode - checkable: true - checked: true - } - MenuItem { - text: "3D Screen (Active)" - exclusiveGroup: displayMode - checkable: true - } - MenuItem { - text: "3D Screen (Passive)" - exclusiveGroup: displayMode - checkable: true - } - } - - Menu { - title: "View" - Menu { - title: "Camera Mode" - ExclusiveGroup { id: cameraMode } - MenuItem { - exclusiveGroup: cameraMode - text: "First Person"; - onTriggered: console.log(text + " checked " + checked) - checkable: true - checked: true - } - MenuItem { - exclusiveGroup: cameraMode - text: "Third Person"; - onTriggered: console.log(text) - checkable: true - } - MenuItem { - exclusiveGroup: cameraMode - text: "Independent Mode"; - onTriggered: console.log(text) - checkable: true - } - MenuItem { - exclusiveGroup: cameraMode - text: "Entity Mode"; - onTriggered: console.log(text) - enabled: false - checkable: true - } - MenuItem { - exclusiveGroup: cameraMode - text: "Fullscreen Mirror"; - onTriggered: console.log(text) - checkable: true - } - } - } - - Menu { - title: "Edit" - - MenuItem { - text: "Undo" - shortcut: "Ctrl+Z" - enabled: false - onTriggered: console.log(text) - } - - MenuItem { - text: "Redo" - shortcut: "Ctrl+Shift+Z" - enabled: false - onTriggered: console.log(text) - } - - MenuSeparator { } - - MenuItem { - text: "Cut" - shortcut: "Ctrl+X" - onTriggered: console.log(text) - } - - MenuItem { - text: "Copy" - shortcut: "Ctrl+C" - onTriggered: console.log(text) - } - - MenuItem { - text: "Paste" - shortcut: "Ctrl+V" - visible: false - onTriggered: console.log("Paste") - } - } - - Menu { - title: "Navigate" - } - - Menu { - title: "Market" - } - - Menu { - title: "Settings" - } - - Menu { - title: "Developer" - } - - Menu { - title: "Quit" - } - - Menu { - title: "File" - - Action { - id: login - text: "Login" - } - - Action { - id: quit - text: "Quit" - shortcut: "Ctrl+Q" - onTriggered: Qt.quit(); - } - - MenuItem { action: quit } - MenuItem { action: login } - } - } - -} - diff --git a/tests-manual/ui/qml/TestControllers.qml b/tests-manual/ui/qml/TestControllers.qml deleted file mode 100644 index e9a7fb49e5..0000000000 --- a/tests-manual/ui/qml/TestControllers.qml +++ /dev/null @@ -1,160 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 - -import "controller" -import "controls" as HifiControls -import "styles" - -HifiControls.VrDialog { - id: root - HifiConstants { id: hifi } - title: "Controller Test" - resizable: true - contentImplicitWidth: clientArea.implicitWidth - contentImplicitHeight: clientArea.implicitHeight - backgroundColor: "beige" - - property var actions: Controller.Actions - property var standard: Controller.Standard - property var hydra: null - property var testMapping: null - property bool testMappingEnabled: false - property var xbox: null - - function buildMapping() { - testMapping = Controller.newMapping(); - testMapping.fromQml(standard.RY).invert().toQml(actions.Pitch); - testMapping.fromQml(function(){ - return Math.sin(Date.now() / 250); - }).toQml(actions.Yaw); - //testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); - // Step yaw takes a number of degrees - testMapping.fromQml(standard.LB).pulse(0.10).invert().scale(40.0).toQml(actions.StepYaw); - testMapping.fromQml(standard.RB).pulse(0.10).scale(15.0).toQml(actions.StepYaw); - testMapping.fromQml(standard.RX).scale(15.0).toQml(actions.StepYaw); - } - - function toggleMapping() { - testMapping.enable(!testMappingEnabled); - testMappingEnabled = !testMappingEnabled; - } - - Component.onCompleted: { - var xboxRegex = /^GamePad/; - var hydraRegex = /^Hydra/; - for (var prop in Controller.Hardware) { - if(xboxRegex.test(prop)) { - root.xbox = Controller.Hardware[prop] - print("found xbox") - continue - } - if (hydraRegex.test(prop)) { - root.hydra = Controller.Hardware[prop] - print("found hydra") - continue - } - } - } - - Column { - id: clientArea - spacing: 12 - x: root.clientX - y: root.clientY - - Row { - spacing: 8 - - Button { - text: !root.testMapping ? "Build Mapping" : (root.testMappingEnabled ? "Disable Mapping" : "Enable Mapping") - onClicked: { - - if (!root.testMapping) { - root.buildMapping() - } else { - root.toggleMapping(); - } - } - } - } - - Row { - Standard { device: root.standard; label: "Standard"; width: 180 } - } - - Row { - spacing: 8 - Xbox { device: root.xbox; label: "XBox"; width: 180 } - Hydra { device: root.hydra; width: 180 } - } - - Row { - spacing: 4 - ScrollingGraph { - controlId: Controller.Actions.Yaw - label: "Yaw" - min: -2.0 - max: 2.0 - size: 64 - } - - ScrollingGraph { - controlId: Controller.Actions.YawLeft - label: "Yaw Left" - min: -2.0 - max: 2.0 - size: 64 - } - - ScrollingGraph { - controlId: Controller.Actions.YawRight - label: "Yaw Right" - min: -2.0 - max: 2.0 - size: 64 - } - - ScrollingGraph { - controlId: Controller.Actions.StepYaw - label: "StepYaw" - min: -20.0 - max: 20.0 - size: 64 - } - } - - Row { - ScrollingGraph { - controlId: Controller.Actions.TranslateZ - label: "TranslateZ" - min: -2.0 - max: 2.0 - size: 64 - } - - ScrollingGraph { - controlId: Controller.Actions.Forward - label: "Forward" - min: -2.0 - max: 2.0 - size: 64 - } - - ScrollingGraph { - controlId: Controller.Actions.Backward - label: "Backward" - min: -2.0 - max: 2.0 - size: 64 - } - - } - } -} // dialog - - - - - diff --git a/tests-manual/ui/qml/TestDialog.qml b/tests-manual/ui/qml/TestDialog.qml deleted file mode 100644 index e6675b7282..0000000000 --- a/tests-manual/ui/qml/TestDialog.qml +++ /dev/null @@ -1,94 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.3 -import "controls" - -VrDialog { - title: "Test Dialog" - id: testDialog - objectName: "TestDialog" - width: 512 - height: 512 - animationDuration: 200 - - onEnabledChanged: { - if (enabled) { - edit.forceActiveFocus(); - } - } - - Item { - id: clientArea - // The client area - anchors.fill: parent - anchors.margins: parent.margins - anchors.topMargin: parent.topMargin - - Rectangle { - property int d: 100 - id: square - objectName: "testRect" - width: d - height: d - anchors.centerIn: parent - color: "red" - NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; } - } - - - TextEdit { - id: edit - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.right: parent.right - anchors.rightMargin: 12 - clip: true - text: "test edit" - anchors.top: parent.top - anchors.topMargin: 12 - } - - Button { - x: 128 - y: 192 - text: "Test" - anchors.bottom: parent.bottom - anchors.bottomMargin: 12 - anchors.right: parent.right - anchors.rightMargin: 12 - onClicked: { - console.log("Click"); - - if (square.visible) { - square.visible = false - } else { - square.visible = true - } - } - } - - Button { - id: customButton2 - y: 192 - text: "Move" - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.bottom: parent.bottom - anchors.bottomMargin: 12 - onClicked: { - onClicked: testDialog.x == 0 ? testDialog.x = 200 : testDialog.x = 0 - } - } - - Keys.onPressed: { - console.log("Key " + event.key); - switch (event.key) { - case Qt.Key_Q: - if (Qt.ControlModifier == event.modifiers) { - event.accepted = true; - break; - } - } - } - } -} diff --git a/tests-manual/ui/qml/TestMenu.qml b/tests-manual/ui/qml/TestMenu.qml deleted file mode 100644 index fe8a26e234..0000000000 --- a/tests-manual/ui/qml/TestMenu.qml +++ /dev/null @@ -1,10 +0,0 @@ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import Hifi 1.0 - -// Currently for testing a pure QML replacement menu -Item { - Menu { - objectName: "rootMenu"; - } -} diff --git a/tests-manual/ui/qml/TestRoot.qml b/tests-manual/ui/qml/TestRoot.qml deleted file mode 100644 index bd38c696bf..0000000000 --- a/tests-manual/ui/qml/TestRoot.qml +++ /dev/null @@ -1,43 +0,0 @@ -import Hifi 1.0 -import QtQuick 2.3 -import QtQuick.Controls 1.3 -// Import local folder last so that our own control customizations override -// the built in ones -import "controls" - -Root { - id: root - anchors.fill: parent - onParentChanged: { - forceActiveFocus(); - } - Button { - id: messageBox - anchors.right: createDialog.left - anchors.rightMargin: 24 - anchors.bottom: parent.bottom - anchors.bottomMargin: 24 - text: "Message" - onClicked: { - console.log("Foo") - root.information("a") - console.log("Bar") - } - } - Button { - id: createDialog - anchors.right: parent.right - anchors.rightMargin: 24 - anchors.bottom: parent.bottom - anchors.bottomMargin: 24 - text: "Create" - onClicked: { - root.loadChild("MenuTest.qml"); - } - } - - Keys.onPressed: { - console.log("Key press root") - } -} - diff --git a/tests-manual/ui/qml/controlDemo/ButtonPage.qml b/tests-manual/ui/qml/controlDemo/ButtonPage.qml deleted file mode 100644 index 0ed7e2d6ad..0000000000 --- a/tests-manual/ui/qml/controlDemo/ButtonPage.qml +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.2 - -ScrollView { - id: page - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - - Item { - id: content - - width: Math.max(page.viewport.width, grid.implicitWidth + 2 * grid.rowSpacing) - height: Math.max(page.viewport.height, grid.implicitHeight + 2 * grid.columnSpacing) - - GridLayout { - id: grid - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: grid.rowSpacing - anchors.rightMargin: grid.rowSpacing - anchors.topMargin: grid.columnSpacing - - columns: page.width < page.height ? 1 : 2 - - GroupBox { - title: "Button" - Layout.fillWidth: true - Layout.columnSpan: grid.columns - RowLayout { - anchors.fill: parent - Button { text: "OK"; isDefault: true } - Button { text: "Cancel" } - Item { Layout.fillWidth: true } - Button { - text: "Attach" - menu: Menu { - MenuItem { text: "Image" } - MenuItem { text: "Document" } - } - } - } - } - - GroupBox { - title: "CheckBox" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - CheckBox { text: "E-mail"; checked: true } - CheckBox { text: "Calendar"; checked: true } - CheckBox { text: "Contacts" } - } - } - - GroupBox { - title: "RadioButton" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - ExclusiveGroup { id: radioGroup } - RadioButton { text: "Portrait"; exclusiveGroup: radioGroup } - RadioButton { text: "Landscape"; exclusiveGroup: radioGroup } - RadioButton { text: "Automatic"; exclusiveGroup: radioGroup; checked: true } - } - } - - GroupBox { - title: "Switch" - Layout.fillWidth: true - Layout.columnSpan: grid.columns - ColumnLayout { - anchors.fill: parent - RowLayout { - Label { text: "Wi-Fi"; Layout.fillWidth: true } - Switch { checked: true } - } - RowLayout { - Label { text: "Bluetooth"; Layout.fillWidth: true } - Switch { checked: false } - } - } - } - } - } -} diff --git a/tests-manual/ui/qml/controlDemo/InputPage.qml b/tests-manual/ui/qml/controlDemo/InputPage.qml deleted file mode 100644 index cb1878d023..0000000000 --- a/tests-manual/ui/qml/controlDemo/InputPage.qml +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.2 - -ScrollView { - id: page - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - - Item { - id: content - - width: Math.max(page.viewport.width, column.implicitWidth + 2 * column.spacing) - height: Math.max(page.viewport.height, column.implicitHeight + 2 * column.spacing) - - ColumnLayout { - id: column - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: column.spacing - - GroupBox { - title: "TextField" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - TextField { placeholderText: "..."; Layout.fillWidth: true; z: 1 } - TextField { placeholderText: "Password"; echoMode: TextInput.Password; Layout.fillWidth: true } - } - } - - GroupBox { - title: "ComboBox" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - ComboBox { - model: Qt.fontFamilies() - Layout.fillWidth: true - } - ComboBox { - editable: true - model: ListModel { - id: listModel - ListElement { text: "Apple" } - ListElement { text: "Banana" } - ListElement { text: "Coconut" } - ListElement { text: "Orange" } - } - onAccepted: { - if (find(currentText) === -1) { - listModel.append({text: editText}) - currentIndex = find(editText) - } - } - Layout.fillWidth: true - } - } - } - - GroupBox { - title: "SpinBox" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - SpinBox { value: 99; Layout.fillWidth: true; z: 1 } - SpinBox { decimals: 2; Layout.fillWidth: true } - } - } - } - } -} diff --git a/tests-manual/ui/qml/controlDemo/ProgressPage.qml b/tests-manual/ui/qml/controlDemo/ProgressPage.qml deleted file mode 100644 index a1fa596f79..0000000000 --- a/tests-manual/ui/qml/controlDemo/ProgressPage.qml +++ /dev/null @@ -1,90 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.2 - -ScrollView { - id: page - - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - - Item { - id: content - - width: Math.max(page.viewport.width, column.implicitWidth + 2 * column.spacing) - height: Math.max(page.viewport.height, column.implicitHeight + 2 * column.spacing) - - ColumnLayout { - id: column - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: column.spacing - - GroupBox { - title: "ProgressBar" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - ProgressBar { indeterminate: true; Layout.fillWidth: true } - ProgressBar { value: slider.value; Layout.fillWidth: true } - } - } - - GroupBox { - title: "Slider" - Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - Slider { id: slider; value: 0.5; Layout.fillWidth: true } - } - } - - GroupBox { - title: "BusyIndicator" - Layout.fillWidth: true - BusyIndicator { running: true } - } - } - } -} diff --git a/tests-manual/ui/qml/controlDemo/main.qml b/tests-manual/ui/qml/controlDemo/main.qml deleted file mode 100644 index 168b9fb291..0000000000 --- a/tests-manual/ui/qml/controlDemo/main.qml +++ /dev/null @@ -1,161 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.1 -import QtQuick.Controls 1.2 -import "qml/UI.js" as UI -import "qml" -//import "/Users/bdavis/Git/hifi/interface/resources/qml" - -Item { - anchors.fill: parent - visible: true - //title: "Qt Quick Controls Gallery" - - MessageDialog { - id: aboutDialog - icon: StandardIcon.Information - title: "About" - text: "Qt Quick Controls Gallery" - informativeText: "This example demonstrates most of the available Qt Quick Controls." - } - - Action { - id: copyAction - text: "&Copy" - shortcut: StandardKey.Copy - iconName: "edit-copy" - enabled: (!!activeFocusItem && !!activeFocusItem["copy"]) - onTriggered: activeFocusItem.copy() - } - - Action { - id: cutAction - text: "Cu&t" - shortcut: StandardKey.Cut - iconName: "edit-cut" - enabled: (!!activeFocusItem && !!activeFocusItem["cut"]) - onTriggered: activeFocusItem.cut() - } - - Action { - id: pasteAction - text: "&Paste" - shortcut: StandardKey.Paste - iconName: "edit-paste" - enabled: (!!activeFocusItem && !!activeFocusItem["paste"]) - onTriggered: activeFocusItem.paste() - } - -// toolBar: ToolBar { -// RowLayout { -// anchors.fill: parent -// anchors.margins: spacing -// Label { -// text: UI.label -// } -// Item { Layout.fillWidth: true } -// CheckBox { -// id: enabler -// text: "Enabled" -// checked: true -// } -// } -// } - -// menuBar: MenuBar { -// Menu { -// title: "&File" -// MenuItem { -// text: "E&xit" -// shortcut: StandardKey.Quit -// onTriggered: Qt.quit() -// } -// } -// Menu { -// title: "&Edit" -// visible: tabView.currentIndex == 2 -// MenuItem { action: cutAction } -// MenuItem { action: copyAction } -// MenuItem { action: pasteAction } -// } -// Menu { -// title: "&Help" -// MenuItem { -// text: "About..." -// onTriggered: aboutDialog.open() -// } -// } -// } - - TabView { - id: tabView - - anchors.fill: parent - anchors.margins: UI.margin - tabPosition: UI.tabPosition - - Layout.minimumWidth: 360 - Layout.minimumHeight: 360 - Layout.preferredWidth: 480 - Layout.preferredHeight: 640 - - Tab { - title: "Buttons" - ButtonPage { - enabled: enabler.checked - } - } - Tab { - title: "Progress" - ProgressPage { - enabled: enabler.checked - } - } - Tab { - title: "Input" - InputPage { - enabled: enabler.checked - } - } - } -} diff --git a/tests-manual/ui/qml/main.qml b/tests-manual/ui/qml/main.qml deleted file mode 100644 index 607bd624a1..0000000000 --- a/tests-manual/ui/qml/main.qml +++ /dev/null @@ -1,461 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Dialogs 1.2 as OriginalDialogs -import Qt.labs.settings 1.0 - -import "../../../interface/resources/qml" -//import "../../../interface/resources/qml/windows" -import "../../../interface/resources/qml/windows" -import "../../../interface/resources/qml/dialogs" -import "../../../interface/resources/qml/hifi" -import "../../../interface/resources/qml/hifi/dialogs" -import "../../../interface/resources/qml/styles-uit" - -ApplicationWindow { - id: appWindow - objectName: "MainWindow" - visible: true - width: 1280 - height: 800 - title: qsTr("Scratch App") - toolBar: Row { - id: testButtons - anchors { margins: 8; left: parent.left; top: parent.top } - spacing: 8 - property int count: 0 - - property var tabs: []; - property var urls: []; - property var toolbar; - property var lastButton; - - Button { - text: HMD.active ? "Disable HMD" : "Enable HMD" - onClicked: HMD.active = !HMD.active - } - - Button { - text: desktop.hmdHandMouseActive ? "Disable HMD HandMouse" : "Enable HMD HandMouse" - onClicked: desktop.hmdHandMouseActive = !desktop.hmdHandMouseActive - } - - // Window visibility - Button { - text: "toggle desktop" - onClicked: desktop.togglePinned() - } - - Button { - text: "Create Toolbar" - onClicked: testButtons.toolbar = desktop.getToolbar("com.highfidelity.interface.toolbar.system"); - } - - Button { - text: "Toggle Toolbar Direction" - onClicked: testButtons.toolbar.horizontal = !testButtons.toolbar.horizontal - } - - Button { - readonly property var icons: [ - "edit-01.svg", - "model-01.svg", - "cube-01.svg", - "sphere-01.svg", - "light-01.svg", - "text-01.svg", - "web-01.svg", - "zone-01.svg", - "particle-01.svg", - ] - property int iconIndex: 0 - readonly property string toolIconUrl: "../../../../../scripts/system/assets/images/tools/" - text: "Create Button" - onClicked: { - var name = icons[iconIndex]; - var url = toolIconUrl + name; - iconIndex = (iconIndex + 1) % icons.length; - var button = testButtons.lastButton = testButtons.toolbar.addButton({ - imageURL: url, - objectName: name, - subImage: { - y: 50, - }, - alpha: 0.9 - }); - - button.clicked.connect(function(){ - console.log("Clicked on button " + button.imageURL + " alpha " + button.alpha) - }); - } - } - - Button { - text: "Toggle Button Visible" - onClicked: testButtons.lastButton.visible = !testButtons.lastButton.visible - } - - // Error alerts - /* - Button { - // Message without title. - text: "Show Error" - onClicked: { - var messageBox = desktop.messageBox({ - text: "Diagnostic cycle will be complete in 30 seconds", - icon: hifi.icons.critical, - }); - messageBox.selected.connect(function(button) { - console.log("You clicked " + button) - }) - } - } - Button { - // detailedText is not currently used anywhere in Interface but it is easier to leave in and style good enough. - text: "Show Long Error" - onClicked: { - desktop.messageBox({ - informativeText: "Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds Diagnostic cycle will be complete in 30 seconds ", - text: "Baloney", - icon: hifi.icons.warning, - detailedText: "sakjd;laskj dksa;dl jka;lsd j;lkjas ;dlkaj s;dlakjd ;alkjda; slkjda; lkjda;lksjd ;alksjd; alksjd ;alksjd; alksjd; alksdjas;ldkjas;lkdja ;kj ;lkasjd; lkj as;dlka jsd;lka jsd;laksjd a" - }); - } - } - */ - - // query - /* - // There is no such desktop.queryBox() function; may need to update test to cover QueryDialog.qml? - Button { - text: "Show Query" - onClicked: { - var queryBox = desktop.queryBox({ - text: "Have you stopped beating your wife?", - placeholderText: "Are you sure?", - // icon: hifi.icons.critical, - }); - queryBox.selected.connect(function(result) { - console.log("User responded with " + result); - }); - - queryBox.canceled.connect(function() { - console.log("User cancelled query box "); - }) - } - } - */ - - // Browser - /* - Button { - text: "Open Browser" - onClicked: builder.createObject(desktop); - property var builder: Component { - Browser {} - } - } - */ - - - // file dialog - /* - - Button { - text: "Open Directory" - property var builder: Component { - FileDialog { selectDirectory: true } - } - - onClicked: { - var fileDialog = builder.createObject(desktop); - fileDialog.canceled.connect(function(){ - console.log("Cancelled") - }) - fileDialog.selectedFile.connect(function(file){ - console.log("Selected " + file) - }) - } - } - Button { - text: "Open File" - property var builder: Component { - FileDialog { - title: "Open File" - filter: "All Files (*.*)" - //filter: "HTML files (*.html);;Other(*.png)" - } - } - - onClicked: { - var fileDialog = builder.createObject(desktop); - fileDialog.canceled.connect(function(){ - console.log("Cancelled") - }) - fileDialog.selectedFile.connect(function(file){ - console.log("Selected " + file) - }) - } - } - */ - - // tabs - /* - Button { - text: "Add Tab" - onClicked: { - console.log(desktop.toolWindow); - desktop.toolWindow.addWebTab({ source: "Foo" }); - desktop.toolWindow.showTabForUrl("Foo", true); - } - } - - Button { - text: "Add Tab 2" - onClicked: { - console.log(desktop.toolWindow); - desktop.toolWindow.addWebTab({ source: "Foo 2" }); - desktop.toolWindow.showTabForUrl("Foo 2", true); - } - } - - Button { - text: "Add Tab 3" - onClicked: { - console.log(desktop.toolWindow); - desktop.toolWindow.addWebTab({ source: "Foo 3" }); - desktop.toolWindow.showTabForUrl("Foo 3", true); - } - } - - Button { - text: "Destroy Tab" - onClicked: { - console.log(desktop.toolWindow); - desktop.toolWindow.removeTabForUrl("Foo"); - } - } - */ - - // Hifi specific stuff - /* - Button { - // Shows the dialog with preferences sections but not each section's preference items - // because Preferences.preferencesByCategory() method is not stubbed out. - text: "Settings > General..." - property var builder: Component { - GeneralPreferencesDialog { } - } - onClicked: { - var runningScripts = builder.createObject(desktop); - } - } - - Button { - text: "Running Scripts" - property var builder: Component { - RunningScripts { } - } - onClicked: { - var runningScripts = builder.createObject(desktop); - } - } - - Button { - text: "Attachments" - property var builder: Component { - AttachmentsDialog { } - } - onClicked: { - var attachmentsDialog = builder.createObject(desktop); - } - } - Button { - // Replicates message box that pops up after selecting new avatar. Includes title. - text: "Confirm Avatar" - onClicked: { - var messageBox = desktop.messageBox({ - title: "Set Avatar", - text: "Would you like to use 'Albert' for your avatar?", - icon: hifi.icons.question, // Test question icon - //icon: hifi.icons.information, // Test informaton icon - //icon: hifi.icons.warning, // Test warning icon - //icon: hifi.icons.critical, // Test critical icon - //icon: hifi.icons.none, // Test no icon - buttons: OriginalDialogs.StandardButton.Ok + OriginalDialogs.StandardButton.Cancel, - defaultButton: OriginalDialogs.StandardButton.Ok - }); - messageBox.selected.connect(function(button) { - console.log("You clicked " + button) - }) - } - } - */ - // bookmarks - /* - Button { - text: "Bookmark Location" - onClicked: { - desktop.inputDialog({ - title: "Bookmark Location", - icon: hifi.icons.placemark, - label: "Name" - }); - } - } - Button { - text: "Delete Bookmark" - onClicked: { - desktop.inputDialog({ - title: "Delete Bookmark", - icon: hifi.icons.placemark, - label: "Select the bookmark to delete", - items: ["Bookmark A", "Bookmark B", "Bookmark C"] - }); - } - } - Button { - text: "Duplicate Bookmark" - onClicked: { - desktop.messageBox({ - title: "Duplicate Bookmark", - icon: hifi.icons.warning, - text: "The bookmark name you entered alread exists in yoru list.", - informativeText: "Would you like to overwrite it?", - buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No, - defaultButton: OriginalDialogs.StandardButton.Yes - }); - } - } - */ - - } - - - HifiConstants { id: hifi } - - Desktop { - id: desktop - anchors.fill: parent - - //rootMenu: StubMenu { id: rootMenu } - //Component.onCompleted: offscreenWindow = appWindow - - /* - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - onClicked: desktop.popupMenu(Qt.vector2d(mouseX, mouseY)); - } - */ - - Browser { - url: "http://s3.amazonaws.com/DreamingContent/testUiDelegates.html" - } - - - Window { - id: blue - closable: true - visible: true - resizable: true - destroyOnHidden: false - title: "Blue" - - width: 100; height: 100 - x: 1280 / 2; y: 720 / 2 - Settings { - category: "TestWindow.Blue" - property alias x: blue.x - property alias y: blue.y - property alias width: blue.width - property alias height: blue.height - } - - Rectangle { - anchors.fill: parent - visible: true - color: "blue" - Component.onDestruction: console.log("Blue destroyed") - } - } - - Window { - id: green - closable: true - visible: true - resizable: true - title: "Green" - destroyOnHidden: false - - width: 100; height: 100 - x: 1280 / 2; y: 720 / 2 - Settings { - category: "TestWindow.Green" - property alias x: green.x - property alias y: green.y - property alias width: green.width - property alias height: green.height - } - - Rectangle { - anchors.fill: parent - visible: true - color: "green" - Component.onDestruction: console.log("Green destroyed") - } - } - -/* - Rectangle { width: 100; height: 100; x: 100; y: 100; color: "#00f" } - - Window { - id: green - alwaysOnTop: true - frame: HiddenFrame{} - hideBackground: true - closable: true - visible: true - resizable: false - x: 1280 / 2; y: 720 / 2 - width: 100; height: 100 - Rectangle { - color: "#0f0" - width: green.width; - height: green.height; - } - } - */ - -/* - Window { - id: yellow - closable: true - visible: true - resizable: true - x: 100; y: 100 - width: 100; height: 100 - Rectangle { - anchors.fill: parent - visible: true - color: "yellow" - } - } -*/ - } - - Action { - id: openBrowserAction - text: "Open Browser" - shortcut: "Ctrl+Shift+X" - onTriggered: { - builder.createObject(desktop); - } - property var builder: Component { - ModelBrowserDialog{} - } - } -} - - - - diff --git a/tests-manual/ui/qmlscratch.pro b/tests-manual/ui/qmlscratch.pro index 5c9b91ee52..3287180d26 100644 --- a/tests-manual/ui/qmlscratch.pro +++ b/tests-manual/ui/qmlscratch.pro @@ -4,34 +4,10 @@ QT += gui qml quick xml webengine widgets CONFIG += c++11 -SOURCES += src/main.cpp \ - ../../libraries/ui/src/FileDialogHelper.cpp - -HEADERS += \ - ../../libraries/ui/src/FileDialogHelper.h +SOURCES += src/main.cpp # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = ../../interface/resources/qml - DISTFILES += \ - qml/*.qml \ - ../../interface/resources/QtWebEngine/UIDelegates/original/*.qml \ - ../../interface/resources/QtWebEngine/UIDelegates/*.qml \ - ../../interface/resources/qml/*.qml \ - ../../interface/resources/qml/controls/*.qml \ - ../../interface/resources/qml/controls-uit/*.qml \ - ../../interface/resources/qml/dialogs/*.qml \ - ../../interface/resources/qml/dialogs/fileDialog/*.qml \ - ../../interface/resources/qml/dialogs/preferences/*.qml \ - ../../interface/resources/qml/dialogs/messageDialog/*.qml \ - ../../interface/resources/qml/desktop/*.qml \ - ../../interface/resources/qml/menus/*.qml \ - ../../interface/resources/qml/styles/*.qml \ - ../../interface/resources/qml/styles-uit/*.qml \ - ../../interface/resources/qml/windows/*.qml \ - ../../interface/resources/qml/hifi/*.qml \ - ../../interface/resources/qml/hifi/toolbars/*.qml \ - ../../interface/resources/qml/hifi/dialogs/*.qml \ - ../../interface/resources/qml/hifi/dialogs/preferences/*.qml \ - ../../interface/resources/qml/hifi/overlays/*.qml + qml/*.qml diff --git a/tests-manual/ui/src/main.cpp b/tests-manual/ui/src/main.cpp index 312b5f3823..a5061f4d01 100644 --- a/tests-manual/ui/src/main.cpp +++ b/tests-manual/ui/src/main.cpp @@ -3,88 +3,31 @@ #include #include -#include "../../../libraries/ui/src/FileDialogHelper.h" - - -class Preference : public QObject { - Q_OBJECT - Q_PROPERTY(QString category READ getCategory() CONSTANT) - Q_PROPERTY(QString name READ getName() CONSTANT) - Q_PROPERTY(Type type READ getType() CONSTANT) - Q_ENUMS(Type) -public: - enum Type { - Editable, - Browsable, - Spinner, - Checkbox, - }; - - Preference(QObject* parent = nullptr) : QObject(parent) {} - - Preference(const QString& category, const QString& name, QObject* parent = nullptr) - : QObject(parent), _category(category), _name(name) { } - const QString& getCategory() const { return _category; } - const QString& getName() const { return _name; } - virtual Type getType() { return Editable; } - -protected: - const QString _category; - const QString _name; -}; - -class Reticle : public QObject { - Q_OBJECT - Q_PROPERTY(QPoint position READ getPosition CONSTANT) -public: - - Reticle(QObject* parent) : QObject(parent) { - } - - QPoint getPosition() { - if (!_window) { - return QPoint(0, 0); - } - return _window->mapFromGlobal(QCursor::pos()); - } - - void setWindow(QWindow* window) { - _window = window; - } - -private: - QWindow* _window{nullptr}; -}; - QString getRelativeDir(const QString& relativePath = ".") { - QDir path(__FILE__); path.cdUp(); + QDir path(__FILE__); + path.cdUp(); + path.cdUp(); auto result = path.absoluteFilePath(relativePath); result = path.cleanPath(result) + "/"; return result; } -QString getTestQmlDir() { - return getRelativeDir("../qml"); +QString getResourcesDir() { + return getRelativeDir("../../interface/resources"); } -QString getInterfaceQmlDir() { - return getRelativeDir("/"); +QString getQmlDir() { + return getRelativeDir("../../interface/resources/qml"); } - -void setChild(QQmlApplicationEngine& engine, const char* name) { - for (auto obj : engine.rootObjects()) { - auto child = obj->findChild(QString(name)); - if (child) { - engine.rootContext()->setContextProperty(name, child); - return; - } - } - qWarning() << "Could not find object named " << name; +QString getScriptsDir() { + return getRelativeDir("../../scripts"); } void addImportPath(QQmlApplicationEngine& engine, const QString& relativePath, bool insert = false) { QString resolvedPath = getRelativeDir(relativePath); + + qDebug() << "adding import path: " << QDir::toNativeSeparators(resolvedPath); engine.addImportPath(resolvedPath); } @@ -93,44 +36,24 @@ int main(int argc, char *argv[]) { app.setOrganizationName("Some Company"); app.setOrganizationDomain("somecompany.com"); app.setApplicationName("Amazing Application"); - QDir::setCurrent(getRelativeDir("..")); - QtWebEngine::initialize(); - qmlRegisterType("Hifi", 1, 0, "Preference"); + auto scriptsDir = getScriptsDir(); + auto resourcesDir = getResourcesDir(); QQmlApplicationEngine engine; + addImportPath(engine, "."); addImportPath(engine, "qml"); - addImportPath(engine, "../../interface/resources/qml"); - addImportPath(engine, "../../interface/resources"); - engine.load(QUrl(QStringLiteral("qml/Stubs.qml"))); + addImportPath(engine, resourcesDir); + addImportPath(engine, resourcesDir + "/qml"); + addImportPath(engine, scriptsDir); + addImportPath(engine, scriptsDir + "/developer/tests"); - setChild(engine, "offscreenFlags"); - setChild(engine, "Account"); - setChild(engine, "ApplicationCompositor"); - setChild(engine, "Controller"); - setChild(engine, "Desktop"); - setChild(engine, "ScriptDiscoveryService"); - setChild(engine, "HMD"); - setChild(engine, "GL"); - setChild(engine, "MenuHelper"); - setChild(engine, "Preferences"); - setChild(engine, "urlHandler"); - engine.rootContext()->setContextProperty("DebugQML", true); - engine.rootContext()->setContextProperty("fileDialogHelper", new FileDialogHelper()); + QFontDatabase::addApplicationFont(resourcesDir + "/fonts/FiraSans-Regular.ttf"); + QFontDatabase::addApplicationFont(resourcesDir + "/fonts/FiraSans-SemiBold.ttf"); + QFontDatabase::addApplicationFont(resourcesDir + "/fonts/hifi-glyphs.ttf"); - //engine.load(QUrl(QStringLiteral("qrc:/qml/gallery/main.qml"))); - engine.load(QUrl(QStringLiteral("qml/main.qml"))); - for (QObject* rootObject : engine.rootObjects()) { - if (rootObject->objectName() == "MainWindow") { - Reticle* reticle = new Reticle(rootObject); - reticle->setWindow((QWindow*)rootObject); - engine.rootContext()->setContextProperty("Reticle", reticle); - engine.rootContext()->setContextProperty("Window", rootObject); - break; - } - } + auto url = getRelativeDir(".") + "qml/ControlsGalleryWindow.qml"; + + engine.load(url); return app.exec(); } - -#include "main.moc" - From 2296848fcb9f4255d144ffcc773703f1bd196ba8 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 28 Jun 2018 16:58:45 -0700 Subject: [PATCH 106/125] Adding potential fix for showing audio stats - depends on PR#13401 --- scripts/developer/utilities/audio/Stats.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/developer/utilities/audio/Stats.qml b/scripts/developer/utilities/audio/Stats.qml index f359e9b04c..e2291e485d 100644 --- a/scripts/developer/utilities/audio/Stats.qml +++ b/scripts/developer/utilities/audio/Stats.qml @@ -12,7 +12,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:////qml//controls-uit" as HifiControls +import controlsUit 1.0 as HifiControls Column { id: stats From c6764c62445497e08617848bf9ad01a74fe3c281 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Mon, 16 Jul 2018 23:59:06 +0300 Subject: [PATCH 107/125] cherry-picking --- interface/resources/qml/InteractiveWindow.qml | 4 ++-- interface/resources/qml/hifi/AvatarApp.qml | 4 ++-- interface/resources/qml/hifi/avatarapp/AdjustWearables.qml | 5 ++--- interface/resources/qml/hifi/avatarapp/AvatarAppHeader.qml | 2 +- interface/resources/qml/hifi/avatarapp/AvatarAppStyle.qml | 2 +- .../qml/hifi/avatarapp/AvatarWearablesIndicator.qml | 4 ++-- interface/resources/qml/hifi/avatarapp/BlueButton.qml | 4 ++-- .../resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml | 4 ++-- interface/resources/qml/hifi/avatarapp/InputField.qml | 4 ++-- interface/resources/qml/hifi/avatarapp/InputTextStyle4.qml | 4 ++-- interface/resources/qml/hifi/avatarapp/MessageBox.qml | 4 ++-- interface/resources/qml/hifi/avatarapp/Settings.qml | 4 ++-- interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml | 2 +- interface/resources/qml/hifi/avatarapp/ShadowImage.qml | 2 +- interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml | 2 +- interface/resources/qml/hifi/avatarapp/SquareLabel.qml | 4 ++-- interface/resources/qml/hifi/avatarapp/Vector3.qml | 4 ++-- interface/resources/qml/hifi/avatarapp/WhiteButton.qml | 4 ++-- .../commerce/marketplaceItemTester/MarketplaceItemTester.qml | 5 +++-- interface/resources/qml/hifi/tablet/EditEntityList.qml | 4 ++-- interface/resources/qml/hifi/tablet/EditToolsTabView.qml | 4 ++-- 21 files changed, 38 insertions(+), 38 deletions(-) diff --git a/interface/resources/qml/InteractiveWindow.qml b/interface/resources/qml/InteractiveWindow.qml index e8ddbf823d..c217238e93 100644 --- a/interface/resources/qml/InteractiveWindow.qml +++ b/interface/resources/qml/InteractiveWindow.qml @@ -12,9 +12,9 @@ import QtQuick 2.3 import "windows" as Windows import "controls" -import "controls-uit" as Controls +import controlsUit 1.0 as Controls import "styles" -import "styles-uit" +import stylesUit 1.0 Windows.Window { id: root; diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index aea5931627..39590748cf 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -3,8 +3,8 @@ import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtQml.Models 2.1 import QtGraphicalEffects 1.0 -import "../controls-uit" as HifiControls -import "../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 import "avatarapp" Rectangle { diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 5fff14e4a1..0740914440 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -1,9 +1,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtQuick.Layouts 1.3 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit -import "../../controls" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit Rectangle { id: root; diff --git a/interface/resources/qml/hifi/avatarapp/AvatarAppHeader.qml b/interface/resources/qml/hifi/avatarapp/AvatarAppHeader.qml index 9d9db010fb..d3c9cd1d5f 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarAppHeader.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarAppHeader.qml @@ -1,6 +1,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../styles-uit" +import stylesUit 1.0 ShadowRectangle { id: header diff --git a/interface/resources/qml/hifi/avatarapp/AvatarAppStyle.qml b/interface/resources/qml/hifi/avatarapp/AvatarAppStyle.qml index f66c7121cb..36cb4b1080 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarAppStyle.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarAppStyle.qml @@ -10,7 +10,7 @@ import QtQuick 2.5 import QtQuick.Window 2.2 -import "../../styles-uit" +import stylesUit 1.0 QtObject { readonly property QtObject colors: QtObject { diff --git a/interface/resources/qml/hifi/avatarapp/AvatarWearablesIndicator.qml b/interface/resources/qml/hifi/avatarapp/AvatarWearablesIndicator.qml index cb73e9fe71..8b28d4c66b 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarWearablesIndicator.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarWearablesIndicator.qml @@ -1,6 +1,6 @@ import QtQuick 2.9 -import "../../controls-uit" -import "../../styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 ShadowRectangle { property int wearablesCount: 0 diff --git a/interface/resources/qml/hifi/avatarapp/BlueButton.qml b/interface/resources/qml/hifi/avatarapp/BlueButton.qml index e668951517..0cc84d5ba0 100644 --- a/interface/resources/qml/hifi/avatarapp/BlueButton.qml +++ b/interface/resources/qml/hifi/avatarapp/BlueButton.qml @@ -1,6 +1,6 @@ import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit HifiControlsUit.Button { HifiConstants { diff --git a/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml b/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml index 1387c0791a..780981a5a3 100644 --- a/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml +++ b/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml @@ -1,7 +1,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls Rectangle { diff --git a/interface/resources/qml/hifi/avatarapp/InputField.qml b/interface/resources/qml/hifi/avatarapp/InputField.qml index 905518ef0f..2020d56c96 100644 --- a/interface/resources/qml/hifi/avatarapp/InputField.qml +++ b/interface/resources/qml/hifi/avatarapp/InputField.qml @@ -1,7 +1,7 @@ import QtQuick 2.5 import QtQuick.Controls 2.2 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit TextField { id: textField diff --git a/interface/resources/qml/hifi/avatarapp/InputTextStyle4.qml b/interface/resources/qml/hifi/avatarapp/InputTextStyle4.qml index 4b868b47ce..6c2101498c 100644 --- a/interface/resources/qml/hifi/avatarapp/InputTextStyle4.qml +++ b/interface/resources/qml/hifi/avatarapp/InputTextStyle4.qml @@ -1,5 +1,5 @@ -import "../../controls-uit" as HifiControlsUit -import "../../styles-uit" +import controlsUit 1.0 as HifiControlsUit +import stylesUit 1.0 import QtQuick 2.0 import QtQuick.Controls 2.2 diff --git a/interface/resources/qml/hifi/avatarapp/MessageBox.qml b/interface/resources/qml/hifi/avatarapp/MessageBox.qml index f111303214..eb28745b1a 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBox.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBox.qml @@ -1,7 +1,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls Rectangle { diff --git a/interface/resources/qml/hifi/avatarapp/Settings.qml b/interface/resources/qml/hifi/avatarapp/Settings.qml index 3446191163..bad1394133 100644 --- a/interface/resources/qml/hifi/avatarapp/Settings.qml +++ b/interface/resources/qml/hifi/avatarapp/Settings.qml @@ -2,8 +2,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls Rectangle { diff --git a/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml b/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml index c2d84bb371..a2c84fad47 100644 --- a/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml +++ b/interface/resources/qml/hifi/avatarapp/ShadowGlyph.qml @@ -1,4 +1,4 @@ -import "../../styles-uit" +import stylesUit 1.0 import QtQuick 2.9 import QtGraphicalEffects 1.0 diff --git a/interface/resources/qml/hifi/avatarapp/ShadowImage.qml b/interface/resources/qml/hifi/avatarapp/ShadowImage.qml index 3995446e49..51e1043702 100644 --- a/interface/resources/qml/hifi/avatarapp/ShadowImage.qml +++ b/interface/resources/qml/hifi/avatarapp/ShadowImage.qml @@ -1,4 +1,4 @@ -import "../../styles-uit" +import stylesUit 1.0 import QtQuick 2.9 import QtGraphicalEffects 1.0 diff --git a/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml b/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml index 741fce3d8d..3968fcb1ff 100644 --- a/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml +++ b/interface/resources/qml/hifi/avatarapp/ShadowRectangle.qml @@ -1,4 +1,4 @@ -import "../../styles-uit" +import stylesUit 1.0 import QtQuick 2.9 import QtGraphicalEffects 1.0 diff --git a/interface/resources/qml/hifi/avatarapp/SquareLabel.qml b/interface/resources/qml/hifi/avatarapp/SquareLabel.qml index e2c456ec04..69aff47373 100644 --- a/interface/resources/qml/hifi/avatarapp/SquareLabel.qml +++ b/interface/resources/qml/hifi/avatarapp/SquareLabel.qml @@ -1,5 +1,5 @@ -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import QtQuick 2.9 import QtGraphicalEffects 1.0 diff --git a/interface/resources/qml/hifi/avatarapp/Vector3.qml b/interface/resources/qml/hifi/avatarapp/Vector3.qml index d77665f992..698123104f 100644 --- a/interface/resources/qml/hifi/avatarapp/Vector3.qml +++ b/interface/resources/qml/hifi/avatarapp/Vector3.qml @@ -1,7 +1,7 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit import "../../controls" as HifiControls Row { diff --git a/interface/resources/qml/hifi/avatarapp/WhiteButton.qml b/interface/resources/qml/hifi/avatarapp/WhiteButton.qml index dc729ae097..d0a4a152db 100644 --- a/interface/resources/qml/hifi/avatarapp/WhiteButton.qml +++ b/interface/resources/qml/hifi/avatarapp/WhiteButton.qml @@ -1,6 +1,6 @@ import QtQuick 2.5 -import "../../styles-uit" -import "../../controls-uit" as HifiControlsUit +import stylesUit 1.0 +import controlsUit 1.0 as HifiControlsUit HifiControlsUit.Button { HifiConstants { diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 2a4f2d0e22..a37a0ac756 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -15,8 +15,9 @@ import QtQuick 2.10 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.3 import Hifi 1.0 as Hifi -import "qrc:////qml//styles-uit" as HifiStylesUit -import "qrc:////qml//controls-uit" as HifiControlsUit +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit + diff --git a/interface/resources/qml/hifi/tablet/EditEntityList.qml b/interface/resources/qml/hifi/tablet/EditEntityList.qml index d484885103..d2fb99ea0a 100644 --- a/interface/resources/qml/hifi/tablet/EditEntityList.qml +++ b/interface/resources/qml/hifi/tablet/EditEntityList.qml @@ -4,8 +4,8 @@ import QtWebChannel 1.0 import "../../controls" import "../toolbars" import QtGraphicalEffects 1.0 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 WebView { diff --git a/interface/resources/qml/hifi/tablet/EditToolsTabView.qml b/interface/resources/qml/hifi/tablet/EditToolsTabView.qml index 13b1caf8fb..dc7ad683e3 100644 --- a/interface/resources/qml/hifi/tablet/EditToolsTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditToolsTabView.qml @@ -4,8 +4,8 @@ import QtWebChannel 1.0 import "../../controls" import "../toolbars" import QtGraphicalEffects 1.0 -import "../../controls-uit" as HifiControls -import "../../styles-uit" +import controlsUit 1.0 as HifiControls +import stylesUit 1.0 TabBar { id: editTabView From 82313f9464d5e3aa410845f68e13c1e02458f274 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Thu, 18 Oct 2018 22:03:50 +0300 Subject: [PATCH 108/125] update imports for utilities / all other qml files found --- interface/resources/QtWebEngine/UIDelegates/Menu.qml | 4 ++-- interface/resources/QtWebEngine/UIDelegates/MenuItem.qml | 4 ++-- interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml | 4 ++-- scripts/developer/utilities/audio/TabletStats.qml | 3 +-- scripts/developer/utilities/lib/jet/qml/TaskList.qml | 4 ++-- scripts/developer/utilities/lib/jet/qml/TaskListView.qml | 4 ++-- scripts/developer/utilities/lib/plotperf/Color.qml | 4 ++-- scripts/developer/utilities/render/antialiasing.qml | 4 ++-- .../developer/utilities/render/configSlider/ConfigSlider.qml | 4 ++-- .../developer/utilities/render/configSlider/RichSlider.qml | 4 ++-- scripts/developer/utilities/render/deferredLighting.qml | 4 ++-- scripts/developer/utilities/render/engineInspector.qml | 4 ++-- scripts/developer/utilities/render/highlight.qml | 4 ++-- .../developer/utilities/render/highlight/HighlightStyle.qml | 4 ++-- scripts/developer/utilities/render/lod.qml | 4 ++-- scripts/developer/utilities/render/shadow.qml | 4 ++-- scripts/developer/utilities/render/transition.qml | 4 ++-- scripts/developer/utilities/workload/workloadInspector.qml | 4 ++-- .../marketplace/spectator-camera/SpectatorCamera.qml | 4 ++-- 19 files changed, 37 insertions(+), 38 deletions(-) diff --git a/interface/resources/QtWebEngine/UIDelegates/Menu.qml b/interface/resources/QtWebEngine/UIDelegates/Menu.qml index 46c00e758e..adfd29df9e 100644 --- a/interface/resources/QtWebEngine/UIDelegates/Menu.qml +++ b/interface/resources/QtWebEngine/UIDelegates/Menu.qml @@ -1,7 +1,7 @@ import QtQuick 2.5 -import "../../qml/controls-uit" -import "../../qml/styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: menu diff --git a/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml index 6014b6834b..b4d3ca4bb2 100644 --- a/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml +++ b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml @@ -1,7 +1,7 @@ import QtQuick 2.5 -import "../../qml/controls-uit" -import "../../qml/styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 Item { id: root diff --git a/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml b/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml index e4ab3037ef..089c745571 100644 --- a/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml +++ b/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml @@ -1,7 +1,7 @@ import QtQuick 2.5 -import "../../qml/controls-uit" -import "../../qml/styles-uit" +import controlsUit 1.0 +import stylesUit 1.0 import "../../qml/dialogs" QtObject { diff --git a/scripts/developer/utilities/audio/TabletStats.qml b/scripts/developer/utilities/audio/TabletStats.qml index 2f8d212a2a..b50acabec4 100644 --- a/scripts/developer/utilities/audio/TabletStats.qml +++ b/scripts/developer/utilities/audio/TabletStats.qml @@ -11,8 +11,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 - -import "qrc:////qml//styles-uit" +import stylesUit 1.0 Item { id: dialog diff --git a/scripts/developer/utilities/lib/jet/qml/TaskList.qml b/scripts/developer/utilities/lib/jet/qml/TaskList.qml index 5b1aa0afb5..166f604666 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskList.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskList.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../jet.js" as Jet diff --git a/scripts/developer/utilities/lib/jet/qml/TaskListView.qml b/scripts/developer/utilities/lib/jet/qml/TaskListView.qml index 2c75865698..0f083aa72c 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskListView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskListView.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../jet.js" as Jet diff --git a/scripts/developer/utilities/lib/plotperf/Color.qml b/scripts/developer/utilities/lib/plotperf/Color.qml index 15d7f9fcc9..1ad72fe2e6 100644 --- a/scripts/developer/utilities/lib/plotperf/Color.qml +++ b/scripts/developer/utilities/lib/plotperf/Color.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { diff --git a/scripts/developer/utilities/render/antialiasing.qml b/scripts/developer/utilities/render/antialiasing.qml index 1a8f9dac2d..5abfd30935 100644 --- a/scripts/developer/utilities/render/antialiasing.qml +++ b/scripts/developer/utilities/render/antialiasing.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "configSlider" import "../lib/plotperf" diff --git a/scripts/developer/utilities/render/configSlider/ConfigSlider.qml b/scripts/developer/utilities/render/configSlider/ConfigSlider.qml index 41de77fb09..bf9089d82c 100644 --- a/scripts/developer/utilities/render/configSlider/ConfigSlider.qml +++ b/scripts/developer/utilities/render/configSlider/ConfigSlider.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { diff --git a/scripts/developer/utilities/render/configSlider/RichSlider.qml b/scripts/developer/utilities/render/configSlider/RichSlider.qml index 01b14f3d48..ff16cb32ad 100644 --- a/scripts/developer/utilities/render/configSlider/RichSlider.qml +++ b/scripts/developer/utilities/render/configSlider/RichSlider.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 as Original import QtQuick.Controls.Styles 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index a9479b2935..64e00acdac 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "configSlider" import "../lib/jet/qml" as Jet diff --git a/scripts/developer/utilities/render/engineInspector.qml b/scripts/developer/utilities/render/engineInspector.qml index 1b9941e64e..16dd8eb985 100644 --- a/scripts/developer/utilities/render/engineInspector.qml +++ b/scripts/developer/utilities/render/engineInspector.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../lib/jet/qml" as Jet diff --git a/scripts/developer/utilities/render/highlight.qml b/scripts/developer/utilities/render/highlight.qml index 88d6a807ae..d8af2a828e 100644 --- a/scripts/developer/utilities/render/highlight.qml +++ b/scripts/developer/utilities/render/highlight.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "configSlider" import "../lib/plotperf" import "highlight" diff --git a/scripts/developer/utilities/render/highlight/HighlightStyle.qml b/scripts/developer/utilities/render/highlight/HighlightStyle.qml index 371b7e81f7..475aadfdce 100644 --- a/scripts/developer/utilities/render/highlight/HighlightStyle.qml +++ b/scripts/developer/utilities/render/highlight/HighlightStyle.qml @@ -12,8 +12,8 @@ import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 import "../configSlider" import "../../lib/plotperf" -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls Item { id: root diff --git a/scripts/developer/utilities/render/lod.qml b/scripts/developer/utilities/render/lod.qml index 889d8db836..892b43d8be 100644 --- a/scripts/developer/utilities/render/lod.qml +++ b/scripts/developer/utilities/render/lod.qml @@ -11,8 +11,8 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../lib/plotperf" import "configSlider" diff --git a/scripts/developer/utilities/render/shadow.qml b/scripts/developer/utilities/render/shadow.qml index 464fe00eb9..a1d6777a68 100644 --- a/scripts/developer/utilities/render/shadow.qml +++ b/scripts/developer/utilities/render/shadow.qml @@ -12,8 +12,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "configSlider" diff --git a/scripts/developer/utilities/render/transition.qml b/scripts/developer/utilities/render/transition.qml index f74468a273..c150c523f9 100644 --- a/scripts/developer/utilities/render/transition.qml +++ b/scripts/developer/utilities/render/transition.qml @@ -13,8 +13,8 @@ import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 import QtQuick.Dialogs 1.0 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "configSlider" import "../lib/plotperf" diff --git a/scripts/developer/utilities/workload/workloadInspector.qml b/scripts/developer/utilities/workload/workloadInspector.qml index 2eaa9d8133..746a572f29 100644 --- a/scripts/developer/utilities/workload/workloadInspector.qml +++ b/scripts/developer/utilities/workload/workloadInspector.qml @@ -11,8 +11,8 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 -import "qrc:///qml/styles-uit" -import "qrc:///qml/controls-uit" as HifiControls +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls import "../render/configSlider" import "../lib/jet/qml" as Jet import "../lib/plotperf" diff --git a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml index 033039b87d..753771987a 100644 --- a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml +++ b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml @@ -16,8 +16,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 -import "qrc:////qml//styles-uit" as HifiStylesUit -import "qrc:////qml//controls-uit" as HifiControlsUit +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit import "qrc:////qml//controls" as HifiControls import "qrc:////qml//hifi" as Hifi From f7d9bafc7d629c4ae46bd0cdebc8d9fa8556f713 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 24 Oct 2018 00:28:55 +0300 Subject: [PATCH 109/125] add FilterBar & ScrollBar to qmldir --- interface/resources/qml/controlsUit/qmldir | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/resources/qml/controlsUit/qmldir b/interface/resources/qml/controlsUit/qmldir index 989115b8d2..d0577f5575 100644 --- a/interface/resources/qml/controlsUit/qmldir +++ b/interface/resources/qml/controlsUit/qmldir @@ -6,6 +6,7 @@ CheckBox 1.0 CheckBox.qml CheckBoxQQC2 1.0 CheckBoxQQC2.qml ComboBox 1.0 ComboBox.qml ContentSection 1.0 ContentSection.qml +FilterBar 1.0 FilterBar.qml GlyphButton 1.0 GlyphButton.qml HorizontalRule 1.0 HorizontalRule.qml HorizontalSpacer 1.0 HorizontalSpacer.qml @@ -15,6 +16,7 @@ Keyboard 1.0 Keyboard.qml Label 1.0 Label.qml QueuedButton 1.0 QueuedButton.qml RadioButton 1.0 RadioButton.qml +ScrollBar 1.0 ScrollBar.qml Separator 1.0 Separator.qml Slider 1.0 Slider.qml SpinBox 1.0 SpinBox.qml From fe4e4cfbd4b2bdf87d15f091d3e679be4314856a Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 24 Oct 2018 01:40:10 +0300 Subject: [PATCH 110/125] return controls-uit & styles-uit folders to preserve backward-compatibility --- interface/resources/qml/controls-uit/AttachmentsTable.qml | 8 ++++++++ interface/resources/qml/controls-uit/BaseWebView.qml | 8 ++++++++ interface/resources/qml/controls-uit/Button.qml | 8 ++++++++ interface/resources/qml/controls-uit/CheckBox.qml | 8 ++++++++ interface/resources/qml/controls-uit/CheckBoxQQC2.qml | 8 ++++++++ interface/resources/qml/controls-uit/ComboBox.qml | 8 ++++++++ interface/resources/qml/controls-uit/ContentSection.qml | 8 ++++++++ interface/resources/qml/controls-uit/FilterBar.qml | 8 ++++++++ interface/resources/qml/controls-uit/GlyphButton.qml | 8 ++++++++ interface/resources/qml/controls-uit/HorizontalRule.qml | 8 ++++++++ interface/resources/qml/controls-uit/HorizontalSpacer.qml | 8 ++++++++ interface/resources/qml/controls-uit/ImageMessageBox.qml | 8 ++++++++ interface/resources/qml/controls-uit/Key.qml | 8 ++++++++ interface/resources/qml/controls-uit/Keyboard.qml | 8 ++++++++ interface/resources/qml/controls-uit/Label.qml | 8 ++++++++ interface/resources/qml/controls-uit/QueuedButton.qml | 8 ++++++++ interface/resources/qml/controls-uit/RadioButton.qml | 8 ++++++++ interface/resources/qml/controls-uit/ScrollBar.qml | 8 ++++++++ interface/resources/qml/controls-uit/Separator.qml | 8 ++++++++ interface/resources/qml/controls-uit/Slider.qml | 8 ++++++++ interface/resources/qml/controls-uit/SpinBox.qml | 8 ++++++++ interface/resources/qml/controls-uit/Switch.qml | 8 ++++++++ interface/resources/qml/controls-uit/Table.qml | 8 ++++++++ .../resources/qml/controls-uit/TabletContentSection.qml | 8 ++++++++ interface/resources/qml/controls-uit/TabletHeader.qml | 8 ++++++++ interface/resources/qml/controls-uit/TextAction.qml | 8 ++++++++ interface/resources/qml/controls-uit/TextEdit.qml | 8 ++++++++ interface/resources/qml/controls-uit/TextField.qml | 8 ++++++++ interface/resources/qml/controls-uit/ToolTip.qml | 8 ++++++++ interface/resources/qml/controls-uit/Tree.qml | 8 ++++++++ interface/resources/qml/controls-uit/VerticalSpacer.qml | 8 ++++++++ interface/resources/qml/controls-uit/WebGlyphButton.qml | 8 ++++++++ interface/resources/qml/controls-uit/WebSpinner.qml | 8 ++++++++ interface/resources/qml/controls-uit/WebView.qml | 8 ++++++++ interface/resources/qml/controls-uit/readme.txt | 1 + .../resources/qml/styles-uit/AnonymousProRegular.qml | 8 ++++++++ interface/resources/qml/styles-uit/ButtonLabel.qml | 8 ++++++++ interface/resources/qml/styles-uit/FiraSansRegular.qml | 8 ++++++++ interface/resources/qml/styles-uit/FiraSansSemiBold.qml | 8 ++++++++ interface/resources/qml/styles-uit/HiFiGlyphs.qml | 8 ++++++++ interface/resources/qml/styles-uit/HifiConstants.qml | 8 ++++++++ interface/resources/qml/styles-uit/IconButton.qml | 8 ++++++++ interface/resources/qml/styles-uit/InfoItem.qml | 8 ++++++++ interface/resources/qml/styles-uit/InputLabel.qml | 8 ++++++++ interface/resources/qml/styles-uit/ListItem.qml | 8 ++++++++ interface/resources/qml/styles-uit/Logs.qml | 8 ++++++++ interface/resources/qml/styles-uit/OverlayTitle.qml | 8 ++++++++ interface/resources/qml/styles-uit/RalewayBold.qml | 8 ++++++++ interface/resources/qml/styles-uit/RalewayLight.qml | 8 ++++++++ interface/resources/qml/styles-uit/RalewayRegular.qml | 8 ++++++++ interface/resources/qml/styles-uit/RalewaySemiBold.qml | 8 ++++++++ interface/resources/qml/styles-uit/SectionName.qml | 8 ++++++++ interface/resources/qml/styles-uit/Separator.qml | 8 ++++++++ interface/resources/qml/styles-uit/ShortcutText.qml | 8 ++++++++ interface/resources/qml/styles-uit/TabName.qml | 8 ++++++++ interface/resources/qml/styles-uit/TextFieldInput.qml | 8 ++++++++ interface/resources/qml/styles-uit/readme.txt | 1 + 57 files changed, 442 insertions(+) create mode 100644 interface/resources/qml/controls-uit/AttachmentsTable.qml create mode 100644 interface/resources/qml/controls-uit/BaseWebView.qml create mode 100644 interface/resources/qml/controls-uit/Button.qml create mode 100644 interface/resources/qml/controls-uit/CheckBox.qml create mode 100644 interface/resources/qml/controls-uit/CheckBoxQQC2.qml create mode 100644 interface/resources/qml/controls-uit/ComboBox.qml create mode 100644 interface/resources/qml/controls-uit/ContentSection.qml create mode 100644 interface/resources/qml/controls-uit/FilterBar.qml create mode 100644 interface/resources/qml/controls-uit/GlyphButton.qml create mode 100644 interface/resources/qml/controls-uit/HorizontalRule.qml create mode 100644 interface/resources/qml/controls-uit/HorizontalSpacer.qml create mode 100644 interface/resources/qml/controls-uit/ImageMessageBox.qml create mode 100644 interface/resources/qml/controls-uit/Key.qml create mode 100644 interface/resources/qml/controls-uit/Keyboard.qml create mode 100644 interface/resources/qml/controls-uit/Label.qml create mode 100644 interface/resources/qml/controls-uit/QueuedButton.qml create mode 100644 interface/resources/qml/controls-uit/RadioButton.qml create mode 100644 interface/resources/qml/controls-uit/ScrollBar.qml create mode 100644 interface/resources/qml/controls-uit/Separator.qml create mode 100644 interface/resources/qml/controls-uit/Slider.qml create mode 100644 interface/resources/qml/controls-uit/SpinBox.qml create mode 100644 interface/resources/qml/controls-uit/Switch.qml create mode 100644 interface/resources/qml/controls-uit/Table.qml create mode 100644 interface/resources/qml/controls-uit/TabletContentSection.qml create mode 100644 interface/resources/qml/controls-uit/TabletHeader.qml create mode 100644 interface/resources/qml/controls-uit/TextAction.qml create mode 100644 interface/resources/qml/controls-uit/TextEdit.qml create mode 100644 interface/resources/qml/controls-uit/TextField.qml create mode 100644 interface/resources/qml/controls-uit/ToolTip.qml create mode 100644 interface/resources/qml/controls-uit/Tree.qml create mode 100644 interface/resources/qml/controls-uit/VerticalSpacer.qml create mode 100644 interface/resources/qml/controls-uit/WebGlyphButton.qml create mode 100644 interface/resources/qml/controls-uit/WebSpinner.qml create mode 100644 interface/resources/qml/controls-uit/WebView.qml create mode 100644 interface/resources/qml/controls-uit/readme.txt create mode 100644 interface/resources/qml/styles-uit/AnonymousProRegular.qml create mode 100644 interface/resources/qml/styles-uit/ButtonLabel.qml create mode 100644 interface/resources/qml/styles-uit/FiraSansRegular.qml create mode 100644 interface/resources/qml/styles-uit/FiraSansSemiBold.qml create mode 100644 interface/resources/qml/styles-uit/HiFiGlyphs.qml create mode 100644 interface/resources/qml/styles-uit/HifiConstants.qml create mode 100644 interface/resources/qml/styles-uit/IconButton.qml create mode 100644 interface/resources/qml/styles-uit/InfoItem.qml create mode 100644 interface/resources/qml/styles-uit/InputLabel.qml create mode 100644 interface/resources/qml/styles-uit/ListItem.qml create mode 100644 interface/resources/qml/styles-uit/Logs.qml create mode 100644 interface/resources/qml/styles-uit/OverlayTitle.qml create mode 100644 interface/resources/qml/styles-uit/RalewayBold.qml create mode 100644 interface/resources/qml/styles-uit/RalewayLight.qml create mode 100644 interface/resources/qml/styles-uit/RalewayRegular.qml create mode 100644 interface/resources/qml/styles-uit/RalewaySemiBold.qml create mode 100644 interface/resources/qml/styles-uit/SectionName.qml create mode 100644 interface/resources/qml/styles-uit/Separator.qml create mode 100644 interface/resources/qml/styles-uit/ShortcutText.qml create mode 100644 interface/resources/qml/styles-uit/TabName.qml create mode 100644 interface/resources/qml/styles-uit/TextFieldInput.qml create mode 100644 interface/resources/qml/styles-uit/readme.txt diff --git a/interface/resources/qml/controls-uit/AttachmentsTable.qml b/interface/resources/qml/controls-uit/AttachmentsTable.qml new file mode 100644 index 0000000000..fdd62b7c84 --- /dev/null +++ b/interface/resources/qml/controls-uit/AttachmentsTable.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +AttachmentsTable { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/BaseWebView.qml b/interface/resources/qml/controls-uit/BaseWebView.qml new file mode 100644 index 0000000000..6ff58d1e47 --- /dev/null +++ b/interface/resources/qml/controls-uit/BaseWebView.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +BaseWebView { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml new file mode 100644 index 0000000000..ddadd0038a --- /dev/null +++ b/interface/resources/qml/controls-uit/Button.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +Button { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml new file mode 100644 index 0000000000..01ffd3c788 --- /dev/null +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +CheckBox { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml new file mode 100644 index 0000000000..9950e4b47f --- /dev/null +++ b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +CheckBoxQQC2 { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml new file mode 100644 index 0000000000..74a68706bf --- /dev/null +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +ComboBox { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/ContentSection.qml b/interface/resources/qml/controls-uit/ContentSection.qml new file mode 100644 index 0000000000..d6cdd92722 --- /dev/null +++ b/interface/resources/qml/controls-uit/ContentSection.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +ContentSection { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/FilterBar.qml b/interface/resources/qml/controls-uit/FilterBar.qml new file mode 100644 index 0000000000..a9b3c350b4 --- /dev/null +++ b/interface/resources/qml/controls-uit/FilterBar.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +FilterBar { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controls-uit/GlyphButton.qml new file mode 100644 index 0000000000..e6dbec69a1 --- /dev/null +++ b/interface/resources/qml/controls-uit/GlyphButton.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +GlyphButton { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/HorizontalRule.qml b/interface/resources/qml/controls-uit/HorizontalRule.qml new file mode 100644 index 0000000000..b0eaa56778 --- /dev/null +++ b/interface/resources/qml/controls-uit/HorizontalRule.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +HorizontalRule { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml new file mode 100644 index 0000000000..f9bf73f6e0 --- /dev/null +++ b/interface/resources/qml/controls-uit/HorizontalSpacer.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +HorizontalSpacer { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/ImageMessageBox.qml b/interface/resources/qml/controls-uit/ImageMessageBox.qml new file mode 100644 index 0000000000..efdf513b19 --- /dev/null +++ b/interface/resources/qml/controls-uit/ImageMessageBox.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +ImageMessageBox { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml new file mode 100644 index 0000000000..844cb0c560 --- /dev/null +++ b/interface/resources/qml/controls-uit/Key.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +Key { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/Keyboard.qml b/interface/resources/qml/controls-uit/Keyboard.qml new file mode 100644 index 0000000000..5c1622d594 --- /dev/null +++ b/interface/resources/qml/controls-uit/Keyboard.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +Keyboard { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/Label.qml b/interface/resources/qml/controls-uit/Label.qml new file mode 100644 index 0000000000..824f6436bb --- /dev/null +++ b/interface/resources/qml/controls-uit/Label.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +Label { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/QueuedButton.qml b/interface/resources/qml/controls-uit/QueuedButton.qml new file mode 100644 index 0000000000..4ec44c16d5 --- /dev/null +++ b/interface/resources/qml/controls-uit/QueuedButton.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +QueuedButton { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/RadioButton.qml b/interface/resources/qml/controls-uit/RadioButton.qml new file mode 100644 index 0000000000..dab757279c --- /dev/null +++ b/interface/resources/qml/controls-uit/RadioButton.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +RadioButton { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/ScrollBar.qml b/interface/resources/qml/controls-uit/ScrollBar.qml new file mode 100644 index 0000000000..71c4c956b0 --- /dev/null +++ b/interface/resources/qml/controls-uit/ScrollBar.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +ScrollBar { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/Separator.qml b/interface/resources/qml/controls-uit/Separator.qml new file mode 100644 index 0000000000..6be7d7cb40 --- /dev/null +++ b/interface/resources/qml/controls-uit/Separator.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +Separator { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/Slider.qml b/interface/resources/qml/controls-uit/Slider.qml new file mode 100644 index 0000000000..5cdd07a35a --- /dev/null +++ b/interface/resources/qml/controls-uit/Slider.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +Slider { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml new file mode 100644 index 0000000000..56f360c286 --- /dev/null +++ b/interface/resources/qml/controls-uit/SpinBox.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +SpinBox { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/Switch.qml b/interface/resources/qml/controls-uit/Switch.qml new file mode 100644 index 0000000000..77d1d4e081 --- /dev/null +++ b/interface/resources/qml/controls-uit/Switch.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +Switch { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml new file mode 100644 index 0000000000..6ca7675db6 --- /dev/null +++ b/interface/resources/qml/controls-uit/Table.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +Table { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/TabletContentSection.qml b/interface/resources/qml/controls-uit/TabletContentSection.qml new file mode 100644 index 0000000000..0738426f4b --- /dev/null +++ b/interface/resources/qml/controls-uit/TabletContentSection.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +TabletContentSection { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/TabletHeader.qml b/interface/resources/qml/controls-uit/TabletHeader.qml new file mode 100644 index 0000000000..689a6e1dfc --- /dev/null +++ b/interface/resources/qml/controls-uit/TabletHeader.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +TabletHeader { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/TextAction.qml b/interface/resources/qml/controls-uit/TextAction.qml new file mode 100644 index 0000000000..617418d992 --- /dev/null +++ b/interface/resources/qml/controls-uit/TextAction.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +TextAction { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/TextEdit.qml b/interface/resources/qml/controls-uit/TextEdit.qml new file mode 100644 index 0000000000..99d6b43234 --- /dev/null +++ b/interface/resources/qml/controls-uit/TextEdit.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +TextEdit { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml new file mode 100644 index 0000000000..8f44823c3b --- /dev/null +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +TextField { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/ToolTip.qml b/interface/resources/qml/controls-uit/ToolTip.qml new file mode 100644 index 0000000000..29b98bf572 --- /dev/null +++ b/interface/resources/qml/controls-uit/ToolTip.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +ToolTip { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml new file mode 100644 index 0000000000..3eca0fa1f6 --- /dev/null +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +Tree { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/VerticalSpacer.qml b/interface/resources/qml/controls-uit/VerticalSpacer.qml new file mode 100644 index 0000000000..f05b83d9c7 --- /dev/null +++ b/interface/resources/qml/controls-uit/VerticalSpacer.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +VerticalSpacer { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/WebGlyphButton.qml b/interface/resources/qml/controls-uit/WebGlyphButton.qml new file mode 100644 index 0000000000..57201edcda --- /dev/null +++ b/interface/resources/qml/controls-uit/WebGlyphButton.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +WebGlyphButton { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/WebSpinner.qml b/interface/resources/qml/controls-uit/WebSpinner.qml new file mode 100644 index 0000000000..4ab8a5475e --- /dev/null +++ b/interface/resources/qml/controls-uit/WebSpinner.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +WebSpinner { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml new file mode 100644 index 0000000000..4d2e37903b --- /dev/null +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import controlsUit 1.0 + +WebView { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/controls-uit/readme.txt b/interface/resources/qml/controls-uit/readme.txt new file mode 100644 index 0000000000..8aa3714ff9 --- /dev/null +++ b/interface/resources/qml/controls-uit/readme.txt @@ -0,0 +1 @@ +this folder exists purely for compatibility reasons and might be deleted in future! please consider using 'import controlsUit 1.0' instead of including this folder \ No newline at end of file diff --git a/interface/resources/qml/styles-uit/AnonymousProRegular.qml b/interface/resources/qml/styles-uit/AnonymousProRegular.qml new file mode 100644 index 0000000000..09e6c1d0b0 --- /dev/null +++ b/interface/resources/qml/styles-uit/AnonymousProRegular.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +AnonymousProRegular { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/ButtonLabel.qml b/interface/resources/qml/styles-uit/ButtonLabel.qml new file mode 100644 index 0000000000..81f7928240 --- /dev/null +++ b/interface/resources/qml/styles-uit/ButtonLabel.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +ButtonLabel { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/FiraSansRegular.qml b/interface/resources/qml/styles-uit/FiraSansRegular.qml new file mode 100644 index 0000000000..d67fba71f8 --- /dev/null +++ b/interface/resources/qml/styles-uit/FiraSansRegular.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +FiraSansRegular { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/FiraSansSemiBold.qml b/interface/resources/qml/styles-uit/FiraSansSemiBold.qml new file mode 100644 index 0000000000..e9f49a5715 --- /dev/null +++ b/interface/resources/qml/styles-uit/FiraSansSemiBold.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +FiraSansSemiBold { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/HiFiGlyphs.qml b/interface/resources/qml/styles-uit/HiFiGlyphs.qml new file mode 100644 index 0000000000..129afb732e --- /dev/null +++ b/interface/resources/qml/styles-uit/HiFiGlyphs.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +HiFiGlyphs { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml new file mode 100644 index 0000000000..cf753d5c8d --- /dev/null +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +HifiConstants { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/IconButton.qml b/interface/resources/qml/styles-uit/IconButton.qml new file mode 100644 index 0000000000..93f37e9d7a --- /dev/null +++ b/interface/resources/qml/styles-uit/IconButton.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +IconButton { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/InfoItem.qml b/interface/resources/qml/styles-uit/InfoItem.qml new file mode 100644 index 0000000000..57397b4c6f --- /dev/null +++ b/interface/resources/qml/styles-uit/InfoItem.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +InfoItem { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/InputLabel.qml b/interface/resources/qml/styles-uit/InputLabel.qml new file mode 100644 index 0000000000..5b03ca1686 --- /dev/null +++ b/interface/resources/qml/styles-uit/InputLabel.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +InputLabel { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/ListItem.qml b/interface/resources/qml/styles-uit/ListItem.qml new file mode 100644 index 0000000000..e98d9bab81 --- /dev/null +++ b/interface/resources/qml/styles-uit/ListItem.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +ListItem { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/Logs.qml b/interface/resources/qml/styles-uit/Logs.qml new file mode 100644 index 0000000000..66d2eaeb2e --- /dev/null +++ b/interface/resources/qml/styles-uit/Logs.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +Logs { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/OverlayTitle.qml b/interface/resources/qml/styles-uit/OverlayTitle.qml new file mode 100644 index 0000000000..ec9cf33482 --- /dev/null +++ b/interface/resources/qml/styles-uit/OverlayTitle.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +OverlayTitle { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/RalewayBold.qml b/interface/resources/qml/styles-uit/RalewayBold.qml new file mode 100644 index 0000000000..2370401ba2 --- /dev/null +++ b/interface/resources/qml/styles-uit/RalewayBold.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +RalewayBold { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/RalewayLight.qml b/interface/resources/qml/styles-uit/RalewayLight.qml new file mode 100644 index 0000000000..0641a2746a --- /dev/null +++ b/interface/resources/qml/styles-uit/RalewayLight.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +RalewayLight { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/RalewayRegular.qml b/interface/resources/qml/styles-uit/RalewayRegular.qml new file mode 100644 index 0000000000..cf601f5c91 --- /dev/null +++ b/interface/resources/qml/styles-uit/RalewayRegular.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +RalewayRegular { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/RalewaySemiBold.qml b/interface/resources/qml/styles-uit/RalewaySemiBold.qml new file mode 100644 index 0000000000..a3c7cfe864 --- /dev/null +++ b/interface/resources/qml/styles-uit/RalewaySemiBold.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +RalewaySemiBold { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/SectionName.qml b/interface/resources/qml/styles-uit/SectionName.qml new file mode 100644 index 0000000000..b3cb143988 --- /dev/null +++ b/interface/resources/qml/styles-uit/SectionName.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +SectionName { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/Separator.qml b/interface/resources/qml/styles-uit/Separator.qml new file mode 100644 index 0000000000..9687fb0520 --- /dev/null +++ b/interface/resources/qml/styles-uit/Separator.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +Separator { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/ShortcutText.qml b/interface/resources/qml/styles-uit/ShortcutText.qml new file mode 100644 index 0000000000..921000651a --- /dev/null +++ b/interface/resources/qml/styles-uit/ShortcutText.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +ShortcutText { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/TabName.qml b/interface/resources/qml/styles-uit/TabName.qml new file mode 100644 index 0000000000..001c110a41 --- /dev/null +++ b/interface/resources/qml/styles-uit/TabName.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +TabName { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/TextFieldInput.qml b/interface/resources/qml/styles-uit/TextFieldInput.qml new file mode 100644 index 0000000000..ce5bc9edbd --- /dev/null +++ b/interface/resources/qml/styles-uit/TextFieldInput.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import stylesUit 1.0 + +TextFieldInput { + Component.onCompleted: { + console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); + } +} diff --git a/interface/resources/qml/styles-uit/readme.txt b/interface/resources/qml/styles-uit/readme.txt new file mode 100644 index 0000000000..105eda3c81 --- /dev/null +++ b/interface/resources/qml/styles-uit/readme.txt @@ -0,0 +1 @@ +this folder exists purely for compatibility reasons and might be deleted in future! please consider using 'import stylesUit 1.0' instead of including this folder \ No newline at end of file From 9623753416c7bba367ef02d3bc7d43eb3d489b2c Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 24 Oct 2018 22:44:36 +0300 Subject: [PATCH 111/125] change imports (how could I miss this place?) --- .../qml/hifi/commerce/marketplaceItemTester/ItemUnderTest.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/ItemUnderTest.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/ItemUnderTest.qml index 27277a28f4..966f359e92 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/ItemUnderTest.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/ItemUnderTest.qml @@ -14,8 +14,8 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import Hifi 1.0 as Hifi -import "qrc:////qml//styles-uit" as HifiStylesUit -import "qrc:////qml//controls-uit" as HifiControlsUit +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as HifiControlsUit Rectangle { id: root; From 4c6700b4973a0437dadc7bc5d8614f60155ad5ec Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 24 Oct 2018 22:52:28 +0300 Subject: [PATCH 112/125] remove deprecations warnings --- interface/resources/qml/controls-uit/AttachmentsTable.qml | 4 ---- interface/resources/qml/controls-uit/BaseWebView.qml | 4 ---- interface/resources/qml/controls-uit/Button.qml | 4 ---- interface/resources/qml/controls-uit/CheckBox.qml | 4 ---- interface/resources/qml/controls-uit/CheckBoxQQC2.qml | 4 ---- interface/resources/qml/controls-uit/ComboBox.qml | 4 ---- interface/resources/qml/controls-uit/ContentSection.qml | 4 ---- interface/resources/qml/controls-uit/FilterBar.qml | 4 ---- interface/resources/qml/controls-uit/GlyphButton.qml | 4 ---- interface/resources/qml/controls-uit/HorizontalRule.qml | 4 ---- interface/resources/qml/controls-uit/HorizontalSpacer.qml | 4 ---- interface/resources/qml/controls-uit/ImageMessageBox.qml | 4 ---- interface/resources/qml/controls-uit/Key.qml | 4 ---- interface/resources/qml/controls-uit/Keyboard.qml | 4 ---- interface/resources/qml/controls-uit/Label.qml | 4 ---- interface/resources/qml/controls-uit/QueuedButton.qml | 4 ---- interface/resources/qml/controls-uit/RadioButton.qml | 4 ---- interface/resources/qml/controls-uit/ScrollBar.qml | 4 ---- interface/resources/qml/controls-uit/Separator.qml | 4 ---- interface/resources/qml/controls-uit/Slider.qml | 4 ---- interface/resources/qml/controls-uit/SpinBox.qml | 4 ---- interface/resources/qml/controls-uit/Switch.qml | 4 ---- interface/resources/qml/controls-uit/Table.qml | 4 ---- interface/resources/qml/controls-uit/TabletContentSection.qml | 4 ---- interface/resources/qml/controls-uit/TabletHeader.qml | 4 ---- interface/resources/qml/controls-uit/TextAction.qml | 4 ---- interface/resources/qml/controls-uit/TextEdit.qml | 4 ---- interface/resources/qml/controls-uit/TextField.qml | 4 ---- interface/resources/qml/controls-uit/ToolTip.qml | 4 ---- interface/resources/qml/controls-uit/Tree.qml | 4 ---- interface/resources/qml/controls-uit/VerticalSpacer.qml | 4 ---- interface/resources/qml/controls-uit/WebGlyphButton.qml | 4 ---- interface/resources/qml/controls-uit/WebSpinner.qml | 4 ---- interface/resources/qml/controls-uit/WebView.qml | 4 ---- interface/resources/qml/styles-uit/AnonymousProRegular.qml | 4 ---- interface/resources/qml/styles-uit/ButtonLabel.qml | 4 ---- interface/resources/qml/styles-uit/FiraSansRegular.qml | 4 ---- interface/resources/qml/styles-uit/FiraSansSemiBold.qml | 4 ---- interface/resources/qml/styles-uit/HiFiGlyphs.qml | 4 ---- interface/resources/qml/styles-uit/HifiConstants.qml | 4 ---- interface/resources/qml/styles-uit/IconButton.qml | 4 ---- interface/resources/qml/styles-uit/InfoItem.qml | 4 ---- interface/resources/qml/styles-uit/InputLabel.qml | 4 ---- interface/resources/qml/styles-uit/ListItem.qml | 4 ---- interface/resources/qml/styles-uit/Logs.qml | 4 ---- interface/resources/qml/styles-uit/OverlayTitle.qml | 4 ---- interface/resources/qml/styles-uit/RalewayBold.qml | 4 ---- interface/resources/qml/styles-uit/RalewayLight.qml | 4 ---- interface/resources/qml/styles-uit/RalewayRegular.qml | 4 ---- interface/resources/qml/styles-uit/RalewaySemiBold.qml | 4 ---- interface/resources/qml/styles-uit/SectionName.qml | 4 ---- interface/resources/qml/styles-uit/Separator.qml | 4 ---- interface/resources/qml/styles-uit/ShortcutText.qml | 4 ---- interface/resources/qml/styles-uit/TabName.qml | 4 ---- interface/resources/qml/styles-uit/TextFieldInput.qml | 4 ---- 55 files changed, 220 deletions(-) diff --git a/interface/resources/qml/controls-uit/AttachmentsTable.qml b/interface/resources/qml/controls-uit/AttachmentsTable.qml index fdd62b7c84..e7fe874610 100644 --- a/interface/resources/qml/controls-uit/AttachmentsTable.qml +++ b/interface/resources/qml/controls-uit/AttachmentsTable.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 AttachmentsTable { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/BaseWebView.qml b/interface/resources/qml/controls-uit/BaseWebView.qml index 6ff58d1e47..61f428e9f7 100644 --- a/interface/resources/qml/controls-uit/BaseWebView.qml +++ b/interface/resources/qml/controls-uit/BaseWebView.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 BaseWebView { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/Button.qml b/interface/resources/qml/controls-uit/Button.qml index ddadd0038a..1d31f02777 100644 --- a/interface/resources/qml/controls-uit/Button.qml +++ b/interface/resources/qml/controls-uit/Button.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 Button { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index 01ffd3c788..c10bea08c4 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 CheckBox { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml index 9950e4b47f..af758ee707 100644 --- a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml +++ b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 CheckBoxQQC2 { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index 74a68706bf..8ac92909b6 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 ComboBox { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/ContentSection.qml b/interface/resources/qml/controls-uit/ContentSection.qml index d6cdd92722..d41b5ad8e6 100644 --- a/interface/resources/qml/controls-uit/ContentSection.qml +++ b/interface/resources/qml/controls-uit/ContentSection.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 ContentSection { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/FilterBar.qml b/interface/resources/qml/controls-uit/FilterBar.qml index a9b3c350b4..dede6d2ded 100644 --- a/interface/resources/qml/controls-uit/FilterBar.qml +++ b/interface/resources/qml/controls-uit/FilterBar.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 FilterBar { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controls-uit/GlyphButton.qml index e6dbec69a1..3a0f8631a8 100644 --- a/interface/resources/qml/controls-uit/GlyphButton.qml +++ b/interface/resources/qml/controls-uit/GlyphButton.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 GlyphButton { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/HorizontalRule.qml b/interface/resources/qml/controls-uit/HorizontalRule.qml index b0eaa56778..7fc2269649 100644 --- a/interface/resources/qml/controls-uit/HorizontalRule.qml +++ b/interface/resources/qml/controls-uit/HorizontalRule.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 HorizontalRule { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml index f9bf73f6e0..b4f372545c 100644 --- a/interface/resources/qml/controls-uit/HorizontalSpacer.qml +++ b/interface/resources/qml/controls-uit/HorizontalSpacer.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 HorizontalSpacer { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/ImageMessageBox.qml b/interface/resources/qml/controls-uit/ImageMessageBox.qml index efdf513b19..484a17dd7c 100644 --- a/interface/resources/qml/controls-uit/ImageMessageBox.qml +++ b/interface/resources/qml/controls-uit/ImageMessageBox.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 ImageMessageBox { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml index 844cb0c560..c031c2f660 100644 --- a/interface/resources/qml/controls-uit/Key.qml +++ b/interface/resources/qml/controls-uit/Key.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 Key { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/Keyboard.qml b/interface/resources/qml/controls-uit/Keyboard.qml index 5c1622d594..2bdf682b9a 100644 --- a/interface/resources/qml/controls-uit/Keyboard.qml +++ b/interface/resources/qml/controls-uit/Keyboard.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 Keyboard { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/Label.qml b/interface/resources/qml/controls-uit/Label.qml index 824f6436bb..032367208b 100644 --- a/interface/resources/qml/controls-uit/Label.qml +++ b/interface/resources/qml/controls-uit/Label.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 Label { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/QueuedButton.qml b/interface/resources/qml/controls-uit/QueuedButton.qml index 4ec44c16d5..54d366a663 100644 --- a/interface/resources/qml/controls-uit/QueuedButton.qml +++ b/interface/resources/qml/controls-uit/QueuedButton.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 QueuedButton { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/RadioButton.qml b/interface/resources/qml/controls-uit/RadioButton.qml index dab757279c..46eb76e7ce 100644 --- a/interface/resources/qml/controls-uit/RadioButton.qml +++ b/interface/resources/qml/controls-uit/RadioButton.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 RadioButton { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/ScrollBar.qml b/interface/resources/qml/controls-uit/ScrollBar.qml index 71c4c956b0..1b21509819 100644 --- a/interface/resources/qml/controls-uit/ScrollBar.qml +++ b/interface/resources/qml/controls-uit/ScrollBar.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 ScrollBar { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/Separator.qml b/interface/resources/qml/controls-uit/Separator.qml index 6be7d7cb40..baeefc7895 100644 --- a/interface/resources/qml/controls-uit/Separator.qml +++ b/interface/resources/qml/controls-uit/Separator.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 Separator { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/Slider.qml b/interface/resources/qml/controls-uit/Slider.qml index 5cdd07a35a..1bcdbc39b7 100644 --- a/interface/resources/qml/controls-uit/Slider.qml +++ b/interface/resources/qml/controls-uit/Slider.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 Slider { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml index 56f360c286..c202a3997c 100644 --- a/interface/resources/qml/controls-uit/SpinBox.qml +++ b/interface/resources/qml/controls-uit/SpinBox.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 SpinBox { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/Switch.qml b/interface/resources/qml/controls-uit/Switch.qml index 77d1d4e081..96ff7f8898 100644 --- a/interface/resources/qml/controls-uit/Switch.qml +++ b/interface/resources/qml/controls-uit/Switch.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 Switch { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index 6ca7675db6..3a4932302f 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 Table { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/TabletContentSection.qml b/interface/resources/qml/controls-uit/TabletContentSection.qml index 0738426f4b..70075d3858 100644 --- a/interface/resources/qml/controls-uit/TabletContentSection.qml +++ b/interface/resources/qml/controls-uit/TabletContentSection.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 TabletContentSection { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/TabletHeader.qml b/interface/resources/qml/controls-uit/TabletHeader.qml index 689a6e1dfc..53e660d708 100644 --- a/interface/resources/qml/controls-uit/TabletHeader.qml +++ b/interface/resources/qml/controls-uit/TabletHeader.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 TabletHeader { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/TextAction.qml b/interface/resources/qml/controls-uit/TextAction.qml index 617418d992..ce5a973776 100644 --- a/interface/resources/qml/controls-uit/TextAction.qml +++ b/interface/resources/qml/controls-uit/TextAction.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 TextAction { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/TextEdit.qml b/interface/resources/qml/controls-uit/TextEdit.qml index 99d6b43234..783950bd1e 100644 --- a/interface/resources/qml/controls-uit/TextEdit.qml +++ b/interface/resources/qml/controls-uit/TextEdit.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 TextEdit { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index 8f44823c3b..261a4162ef 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 TextField { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/ToolTip.qml b/interface/resources/qml/controls-uit/ToolTip.qml index 29b98bf572..a5f24c7974 100644 --- a/interface/resources/qml/controls-uit/ToolTip.qml +++ b/interface/resources/qml/controls-uit/ToolTip.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 ToolTip { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml index 3eca0fa1f6..a21cdc6d47 100644 --- a/interface/resources/qml/controls-uit/Tree.qml +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 Tree { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/VerticalSpacer.qml b/interface/resources/qml/controls-uit/VerticalSpacer.qml index f05b83d9c7..42b48b003e 100644 --- a/interface/resources/qml/controls-uit/VerticalSpacer.qml +++ b/interface/resources/qml/controls-uit/VerticalSpacer.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 VerticalSpacer { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/WebGlyphButton.qml b/interface/resources/qml/controls-uit/WebGlyphButton.qml index 57201edcda..1377b937a2 100644 --- a/interface/resources/qml/controls-uit/WebGlyphButton.qml +++ b/interface/resources/qml/controls-uit/WebGlyphButton.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 WebGlyphButton { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/WebSpinner.qml b/interface/resources/qml/controls-uit/WebSpinner.qml index 4ab8a5475e..a20597d8dc 100644 --- a/interface/resources/qml/controls-uit/WebSpinner.qml +++ b/interface/resources/qml/controls-uit/WebSpinner.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 WebSpinner { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index 4d2e37903b..b11dee7f3b 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import controlsUit 1.0 WebView { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import controlsUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/AnonymousProRegular.qml b/interface/resources/qml/styles-uit/AnonymousProRegular.qml index 09e6c1d0b0..87a91f183a 100644 --- a/interface/resources/qml/styles-uit/AnonymousProRegular.qml +++ b/interface/resources/qml/styles-uit/AnonymousProRegular.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 AnonymousProRegular { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/ButtonLabel.qml b/interface/resources/qml/styles-uit/ButtonLabel.qml index 81f7928240..d677cd06ca 100644 --- a/interface/resources/qml/styles-uit/ButtonLabel.qml +++ b/interface/resources/qml/styles-uit/ButtonLabel.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 ButtonLabel { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/FiraSansRegular.qml b/interface/resources/qml/styles-uit/FiraSansRegular.qml index d67fba71f8..65ee891443 100644 --- a/interface/resources/qml/styles-uit/FiraSansRegular.qml +++ b/interface/resources/qml/styles-uit/FiraSansRegular.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 FiraSansRegular { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/FiraSansSemiBold.qml b/interface/resources/qml/styles-uit/FiraSansSemiBold.qml index e9f49a5715..635523feb9 100644 --- a/interface/resources/qml/styles-uit/FiraSansSemiBold.qml +++ b/interface/resources/qml/styles-uit/FiraSansSemiBold.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 FiraSansSemiBold { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/HiFiGlyphs.qml b/interface/resources/qml/styles-uit/HiFiGlyphs.qml index 129afb732e..646cfc2988 100644 --- a/interface/resources/qml/styles-uit/HiFiGlyphs.qml +++ b/interface/resources/qml/styles-uit/HiFiGlyphs.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 HiFiGlyphs { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index cf753d5c8d..24b24cf019 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 HifiConstants { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/IconButton.qml b/interface/resources/qml/styles-uit/IconButton.qml index 93f37e9d7a..e60ed9eb10 100644 --- a/interface/resources/qml/styles-uit/IconButton.qml +++ b/interface/resources/qml/styles-uit/IconButton.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 IconButton { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/InfoItem.qml b/interface/resources/qml/styles-uit/InfoItem.qml index 57397b4c6f..d09f26649d 100644 --- a/interface/resources/qml/styles-uit/InfoItem.qml +++ b/interface/resources/qml/styles-uit/InfoItem.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 InfoItem { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/InputLabel.qml b/interface/resources/qml/styles-uit/InputLabel.qml index 5b03ca1686..89d74afc8f 100644 --- a/interface/resources/qml/styles-uit/InputLabel.qml +++ b/interface/resources/qml/styles-uit/InputLabel.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 InputLabel { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/ListItem.qml b/interface/resources/qml/styles-uit/ListItem.qml index e98d9bab81..8d72762644 100644 --- a/interface/resources/qml/styles-uit/ListItem.qml +++ b/interface/resources/qml/styles-uit/ListItem.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 ListItem { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/Logs.qml b/interface/resources/qml/styles-uit/Logs.qml index 66d2eaeb2e..d9453afade 100644 --- a/interface/resources/qml/styles-uit/Logs.qml +++ b/interface/resources/qml/styles-uit/Logs.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 Logs { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/OverlayTitle.qml b/interface/resources/qml/styles-uit/OverlayTitle.qml index ec9cf33482..51fc11a77d 100644 --- a/interface/resources/qml/styles-uit/OverlayTitle.qml +++ b/interface/resources/qml/styles-uit/OverlayTitle.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 OverlayTitle { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/RalewayBold.qml b/interface/resources/qml/styles-uit/RalewayBold.qml index 2370401ba2..1373859b32 100644 --- a/interface/resources/qml/styles-uit/RalewayBold.qml +++ b/interface/resources/qml/styles-uit/RalewayBold.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 RalewayBold { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/RalewayLight.qml b/interface/resources/qml/styles-uit/RalewayLight.qml index 0641a2746a..9573eb6649 100644 --- a/interface/resources/qml/styles-uit/RalewayLight.qml +++ b/interface/resources/qml/styles-uit/RalewayLight.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 RalewayLight { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/RalewayRegular.qml b/interface/resources/qml/styles-uit/RalewayRegular.qml index cf601f5c91..d5a66ff696 100644 --- a/interface/resources/qml/styles-uit/RalewayRegular.qml +++ b/interface/resources/qml/styles-uit/RalewayRegular.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 RalewayRegular { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/RalewaySemiBold.qml b/interface/resources/qml/styles-uit/RalewaySemiBold.qml index a3c7cfe864..874a48555b 100644 --- a/interface/resources/qml/styles-uit/RalewaySemiBold.qml +++ b/interface/resources/qml/styles-uit/RalewaySemiBold.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 RalewaySemiBold { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/SectionName.qml b/interface/resources/qml/styles-uit/SectionName.qml index b3cb143988..819ed87d8b 100644 --- a/interface/resources/qml/styles-uit/SectionName.qml +++ b/interface/resources/qml/styles-uit/SectionName.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 SectionName { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/Separator.qml b/interface/resources/qml/styles-uit/Separator.qml index 9687fb0520..9708ec4c26 100644 --- a/interface/resources/qml/styles-uit/Separator.qml +++ b/interface/resources/qml/styles-uit/Separator.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 Separator { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/ShortcutText.qml b/interface/resources/qml/styles-uit/ShortcutText.qml index 921000651a..16e1b2f1c1 100644 --- a/interface/resources/qml/styles-uit/ShortcutText.qml +++ b/interface/resources/qml/styles-uit/ShortcutText.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 ShortcutText { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/TabName.qml b/interface/resources/qml/styles-uit/TabName.qml index 001c110a41..b66192e582 100644 --- a/interface/resources/qml/styles-uit/TabName.qml +++ b/interface/resources/qml/styles-uit/TabName.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 TabName { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } diff --git a/interface/resources/qml/styles-uit/TextFieldInput.qml b/interface/resources/qml/styles-uit/TextFieldInput.qml index ce5bc9edbd..1498dde821 100644 --- a/interface/resources/qml/styles-uit/TextFieldInput.qml +++ b/interface/resources/qml/styles-uit/TextFieldInput.qml @@ -1,8 +1,4 @@ -import QtQuick 2.0 import stylesUit 1.0 TextFieldInput { - Component.onCompleted: { - console.warn("warning: including controls-uit is deprecated! please use 'import stylesUit 1.0' instead"); - } } From a3dfc873b7caa9471c330cf28784f7c2c545d55c Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 3 Nov 2018 11:06:21 +0530 Subject: [PATCH 113/125] rsolve rebase conflict --- interface/resources/qml/hifi/dialogs/security/Security.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/security/Security.qml b/interface/resources/qml/hifi/dialogs/security/Security.qml index 96a554838f..08605033a5 100644 --- a/interface/resources/qml/hifi/dialogs/security/Security.qml +++ b/interface/resources/qml/hifi/dialogs/security/Security.qml @@ -14,8 +14,8 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 import QtGraphicalEffects 1.0 -import "qrc:////qml//styles-uit" as HifiStylesUit -import "qrc:////qml//controls-uit" as HifiControlsUit +import stylesUit 1.0 as HifiStylesUit +import controlsUit 1.0 as as HifiControlsUit import "qrc:////qml//controls" as HifiControls import "qrc:////qml//hifi//commerce//common" as HifiCommerceCommon From 53742e90f57d096f8aa73eec369e108f636e2927 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 6 Nov 2018 10:30:10 -0800 Subject: [PATCH 114/125] Cleanup after merge --- libraries/audio-client/src/AudioClient.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 788b764903..5e7f1fb8a0 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -395,7 +395,7 @@ private: void configureReverb(); void updateReverbOptions(); - void handleLocalEchoAndReverb(QByteArray& inputByteArray, const int& sampleRate, const int& channelCount); + void handleLocalEchoAndReverb(QByteArray& inputByteArray); bool switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false); bool switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest = false); @@ -453,12 +453,6 @@ private: QTimer* _checkPeakValuesTimer { nullptr }; bool _isRecording { false }; - - Mutex _TTSMutex; - bool _isProcessingTTS { false }; - QByteArray _TTSAudioBuffer; - int _TTSChunkSize = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * 50; - QTimer _TTSTimer; }; From c33f9b6ea311e8a88b7ad69e6a9c37da9fe56572 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 6 Nov 2018 11:26:09 -0800 Subject: [PATCH 115/125] Fix build error --- 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 07705c8cd5..bd126048dd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2955,7 +2955,7 @@ void Application::initializeUi() { QUrl{ "hifi/dialogs/security/SecurityImageChange.qml" }, QUrl{ "hifi/dialogs/security/SecurityImageModel.qml" }, QUrl{ "hifi/dialogs/security/SecurityImageSelection.qml" }, - }, callback); + }, commerceCallback); QmlContextCallback ttsCallback = [](QQmlContext* context) { context->setContextProperty("TextToSpeech", DependencyManager::get().data()); }; From 1444b3a18668961a9cd4cc3fea57c0442bf21062 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 6 Nov 2018 09:57:51 -0800 Subject: [PATCH 116/125] resource mutex issues (cherry picked from commit 750629a6194ec98534746a377b3ba027c1dcc20e) (cherry picked from commit fc5ab3289a5f88f245c2825edd085532bd4c97ca) --- libraries/networking/src/ResourceCache.cpp | 2 ++ libraries/networking/src/ResourceCache.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 7aad8d468a..392bc5b7af 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -740,6 +740,8 @@ void Resource::handleReplyFinished() { setSize(_bytesTotal); + // Make sure we keep the Resource alive here + auto self = _self.lock(); ResourceCache::requestCompleted(_self); auto result = _request->getResult(); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 33490301d7..c632399ad4 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -62,7 +62,7 @@ static const qint64 MAX_UNUSED_MAX_SIZE = MAXIMUM_CACHE_SIZE; class ResourceCacheSharedItems : public Dependency { SINGLETON_DEPENDENCY - using Mutex = std::mutex; + using Mutex = std::recursive_mutex; using Lock = std::unique_lock; public: From 378bf911d447d02611b1f65dabc621a93b4fd774 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 6 Nov 2018 12:48:17 -0800 Subject: [PATCH 117/125] Fix another build error --- interface/src/scripting/TTSScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/TTSScriptingInterface.cpp b/interface/src/scripting/TTSScriptingInterface.cpp index 6a5b72ea5f..b41f22759c 100644 --- a/interface/src/scripting/TTSScriptingInterface.cpp +++ b/interface/src/scripting/TTSScriptingInterface.cpp @@ -61,7 +61,7 @@ private: }; #endif -const std::chrono::milliseconds INJECTOR_INTERVAL_MS = std::chrono::milliseconds(100); +const int INJECTOR_INTERVAL_MS = 100; void TTSScriptingInterface::updateLastSoundAudioInjector() { if (_lastSoundAudioInjector) { AudioInjectorOptions options; From f22bbff505a6c6212b707f4fe0022abbf1aa7a81 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 6 Nov 2018 15:47:47 -0800 Subject: [PATCH 118/125] Fix MS19743: Fix case where user couldn't update certain items --- .../qml/hifi/commerce/checkout/Checkout.qml | 13 +++++++++++-- .../qml/hifi/commerce/wallet/WalletHome.qml | 1 - interface/src/commerce/Ledger.cpp | 4 +++- interface/src/commerce/Ledger.h | 2 +- interface/src/commerce/QmlCommerce.cpp | 4 ++-- interface/src/commerce/QmlCommerce.h | 2 +- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 271aab87d1..7000eede41 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -55,6 +55,7 @@ Rectangle { property bool isInstalled; property bool isUpdating; property string baseAppURL; + property int currentUpdatesPage: 1; // Style color: hifi.colors.white; Connections { @@ -156,8 +157,14 @@ Rectangle { break; } } - root.availableUpdatesReceived = true; - refreshBuyUI(); + + if (result.data.updates.length === 0 || root.isUpdating) { + root.availableUpdatesReceived = true; + refreshBuyUI(); + } else { + currentUpdatesPage++; + Commerce.getAvailableUpdates(root.itemId, currentUpdatesPage) + } } } @@ -176,6 +183,7 @@ Rectangle { root.ownershipStatusReceived = false; Commerce.alreadyOwned(root.itemId); root.availableUpdatesReceived = false; + root.currentUpdatesPage = 1; Commerce.getAvailableUpdates(root.itemId); itemPreviewImage.source = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg"; } @@ -1181,6 +1189,7 @@ Rectangle { root.ownershipStatusReceived = false; Commerce.alreadyOwned(root.itemId); root.availableUpdatesReceived = false; + root.currentUpdatesPage = 1; Commerce.getAvailableUpdates(root.itemId); root.balanceReceived = false; Commerce.balance(); diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index d32017189e..eeee0f7262 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -29,7 +29,6 @@ Item { if (visible) { Commerce.balance(); transactionHistoryModel.getFirstPage(); - Commerce.getAvailableUpdates(); } else { refreshTimer.stop(); } diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 0c59fbc6d0..3512677650 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -454,7 +454,7 @@ void Ledger::alreadyOwned(const QString& marketplaceId) { } } -void Ledger::getAvailableUpdates(const QString& itemId) { +void Ledger::getAvailableUpdates(const QString& itemId, const int& pageNumber, const int& itemsPerPage) { auto wallet = DependencyManager::get(); QString endpoint = "available_updates"; QJsonObject request; @@ -462,6 +462,8 @@ void Ledger::getAvailableUpdates(const QString& itemId) { if (!itemId.isEmpty()) { request["marketplace_item_id"] = itemId; } + request["per_page"] = itemsPerPage; + request["page"] = pageNumber; send(endpoint, "availableUpdatesSuccess", "availableUpdatesFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request); } diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 427395ee11..715d6337ad 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -37,7 +37,7 @@ public: void transferAssetToNode(const QString& hfc_key, const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage); void transferAssetToUsername(const QString& hfc_key, const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage); void alreadyOwned(const QString& marketplaceId); - void getAvailableUpdates(const QString& itemId = ""); + void getAvailableUpdates(const QString& itemId = "", const int& pageNumber = 1, const int& itemsPerPage = 10); void updateItem(const QString& hfc_key, const QString& certificate_id); enum CertificateStatus { diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index ffe89ffc5b..0ef26a62b3 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -441,9 +441,9 @@ bool QmlCommerce::openApp(const QString& itemHref) { return true; } -void QmlCommerce::getAvailableUpdates(const QString& itemId) { +void QmlCommerce::getAvailableUpdates(const QString& itemId, const int& pageNumber, const int& itemsPerPage) { auto ledger = DependencyManager::get(); - ledger->getAvailableUpdates(itemId); + ledger->getAvailableUpdates(itemId, pageNumber, itemsPerPage); } void QmlCommerce::updateItem(const QString& certificateId) { diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index 2e3c0ec24d..c5fbdaf4a4 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -92,7 +92,7 @@ protected: Q_INVOKABLE bool uninstallApp(const QString& appHref); Q_INVOKABLE bool openApp(const QString& appHref); - Q_INVOKABLE void getAvailableUpdates(const QString& itemId = ""); + Q_INVOKABLE void getAvailableUpdates(const QString& itemId = "", const int& pageNumber = 1, const int& itemsPerPage = 10); Q_INVOKABLE void updateItem(const QString& certificateId); private: From 87760282596840a780b275628fa6b3a1b366f32c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 6 Nov 2018 16:10:49 -0800 Subject: [PATCH 119/125] Prepend "root" to "currentUpdatesPage" --- interface/resources/qml/hifi/commerce/checkout/Checkout.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 7000eede41..098011f174 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -162,7 +162,7 @@ Rectangle { root.availableUpdatesReceived = true; refreshBuyUI(); } else { - currentUpdatesPage++; + root.currentUpdatesPage++; Commerce.getAvailableUpdates(root.itemId, currentUpdatesPage) } } From c40ded598da5392f4d49049667ab74f504667fb8 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Wed, 7 Nov 2018 12:18:52 -0800 Subject: [PATCH 120/125] REmoving any attempt to fix anything in this pr --- interface/src/ui/overlays/Overlay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 1bf94adfa0..22cf924727 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -247,7 +247,7 @@ void Overlay::removeMaterial(graphics::MaterialPointer material, const std::stri } render::ItemKey Overlay::getKey() { - auto builder = render::ItemKey::Builder().withTypeShape().withTypeMeta(); + auto builder = render::ItemKey::Builder().withTypeShape(); builder.withViewSpace(); builder.withLayer(render::hifi::LAYER_2D); From d316fe15ce24fb406f449bf10fa4ec3f6eccf909 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 7 Nov 2018 15:02:32 -0800 Subject: [PATCH 121/125] fix tablet culling --- interface/src/ui/overlays/Base3DOverlay.cpp | 1 + interface/src/ui/overlays/Base3DOverlay.h | 2 -- interface/src/ui/overlays/ModelOverlay.cpp | 2 ++ interface/src/ui/overlays/Overlay.cpp | 2 +- interface/src/ui/overlays/Volume3DOverlay.cpp | 18 ++++-------------- interface/src/ui/overlays/Volume3DOverlay.h | 3 +-- 6 files changed, 9 insertions(+), 19 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 14c1f1a15a..8599e05332 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -290,6 +290,7 @@ void Base3DOverlay::locationChanged(bool tellPhysics) { notifyRenderVariableChange(); } +// FIXME: Overlays shouldn't be deleted when their parents are void Base3DOverlay::parentDeleted() { qApp->getOverlays().deleteOverlay(getOverlayID()); } diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 6f6092a42e..6cc5182b56 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -59,8 +59,6 @@ public: void setIsGrabbable(bool value) { _isGrabbable = value; } virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; } - virtual AABox getBounds() const override = 0; - void update(float deltatime) override; void notifyRenderVariableChange() const; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 190d9c3895..fa4ced84a8 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -60,6 +60,8 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : } void ModelOverlay::update(float deltatime) { + Base3DOverlay::update(deltatime); + if (_updateModel) { _updateModel = false; _model->setSnapModelToCenter(true); diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 22cf924727..1bf94adfa0 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -247,7 +247,7 @@ void Overlay::removeMaterial(graphics::MaterialPointer material, const std::stri } render::ItemKey Overlay::getKey() { - auto builder = render::ItemKey::Builder().withTypeShape(); + auto builder = render::ItemKey::Builder().withTypeShape().withTypeMeta(); builder.withViewSpace(); builder.withLayer(render::hifi::LAYER_2D); diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index a307d445c0..0cceb44a36 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -20,11 +20,9 @@ Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) : } AABox Volume3DOverlay::getBounds() const { - auto extents = Extents{_localBoundingBox}; - extents.rotate(getWorldOrientation()); - extents.shiftBy(getWorldPosition()); - - return AABox(extents); + AABox bounds = _localBoundingBox; + bounds.transform(getTransform()); + return bounds; } void Volume3DOverlay::setDimensions(const glm::vec3& value) { @@ -49,15 +47,7 @@ void Volume3DOverlay::setProperties(const QVariantMap& properties) { glm::vec3 scale = vec3FromVariant(dimensions); // don't allow a zero or negative dimension component to reach the renderTransform const float MIN_DIMENSION = 0.0001f; - if (scale.x < MIN_DIMENSION) { - scale.x = MIN_DIMENSION; - } - if (scale.y < MIN_DIMENSION) { - scale.y = MIN_DIMENSION; - } - if (scale.z < MIN_DIMENSION) { - scale.z = MIN_DIMENSION; - } + scale = glm::max(scale, MIN_DIMENSION); setDimensions(scale); } } diff --git a/interface/src/ui/overlays/Volume3DOverlay.h b/interface/src/ui/overlays/Volume3DOverlay.h index e4060ae335..2083f7344a 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.h +++ b/interface/src/ui/overlays/Volume3DOverlay.h @@ -24,7 +24,6 @@ public: virtual AABox getBounds() const override; const glm::vec3& getDimensions() const { return _localBoundingBox.getDimensions(); } - void setDimensions(float value) { setDimensions(glm::vec3(value)); } void setDimensions(const glm::vec3& value); void setProperties(const QVariantMap& properties) override; @@ -37,7 +36,7 @@ public: protected: // Centered local bounding box - AABox _localBoundingBox{ vec3(0.0f), 1.0f }; + AABox _localBoundingBox { vec3(-0.5), 1.0f }; Transform evalRenderTransform() override; }; From cee1454f6e963598fe6409224bd3c7f070760a57 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 7 Nov 2018 15:09:53 -0800 Subject: [PATCH 122/125] CR feedback - thanks Ken! --- interface/src/scripting/TTSScriptingInterface.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/interface/src/scripting/TTSScriptingInterface.cpp b/interface/src/scripting/TTSScriptingInterface.cpp index b41f22759c..6b1677aecb 100644 --- a/interface/src/scripting/TTSScriptingInterface.cpp +++ b/interface/src/scripting/TTSScriptingInterface.cpp @@ -75,7 +75,7 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak) { #ifdef WIN32 WAVEFORMATEX fmt; fmt.wFormatTag = WAVE_FORMAT_PCM; - fmt.nSamplesPerSec = 24000; + fmt.nSamplesPerSec = AudioConstants::SAMPLE_RATE; fmt.wBitsPerSample = 16; fmt.nChannels = 1; fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; @@ -132,17 +132,13 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak) { hr = IStream_Size(pStream, &StreamSize); DWORD dwSize = StreamSize.QuadPart; - char* buf1 = new char[dwSize + 1]; - memset(buf1, 0, dwSize + 1); + _lastSoundByteArray.resize(dwSize); - hr = IStream_Read(pStream, buf1, dwSize); + hr = IStream_Read(pStream, _lastSoundByteArray.data(), dwSize); if (FAILED(hr)) { qDebug() << "Couldn't read from stream."; } - _lastSoundByteArray.resize(0); - _lastSoundByteArray.append(buf1, dwSize); - AudioInjectorOptions options; options.position = DependencyManager::get()->getMyAvatarPosition(); From 501746b156c9c839278b3fb33384e6dc43330858 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 29 Oct 2018 16:37:10 -0700 Subject: [PATCH 123/125] 3D Keyboard --- interface/resources/config/keyboard.json | 2476 +++++++++++++++++ interface/resources/meshes/drumstick.fbx | Bin 0 -> 63952 bytes .../resources/meshes/keyboard/SM_enter.fbx | Bin 0 -> 33248 bytes .../resources/meshes/keyboard/SM_key.fbx | Bin 0 -> 32448 bytes .../resources/meshes/keyboard/SM_space.fbx | Bin 0 -> 30400 bytes .../resources/meshes/keyboard/keyCap_F.png | Bin 0 -> 1670 bytes .../resources/meshes/keyboard/keyCap_a.png | Bin 0 -> 3081 bytes .../resources/meshes/keyboard/keyCap_b.png | Bin 0 -> 2744 bytes .../resources/meshes/keyboard/keyCap_c.png | Bin 0 -> 3421 bytes .../resources/meshes/keyboard/keyCap_d.png | Bin 0 -> 2648 bytes .../resources/meshes/keyboard/keyCap_e.png | Bin 0 -> 1689 bytes .../resources/meshes/keyboard/keyCap_g.png | Bin 0 -> 3298 bytes .../resources/meshes/keyboard/keyCap_h.png | Bin 0 -> 1616 bytes .../resources/meshes/keyboard/keyCap_i.png | Bin 0 -> 1581 bytes .../resources/meshes/keyboard/keyCap_j.png | Bin 0 -> 2213 bytes .../resources/meshes/keyboard/keyCap_k.png | Bin 0 -> 2800 bytes .../resources/meshes/keyboard/keyCap_l.png | Bin 0 -> 1631 bytes .../resources/meshes/keyboard/keyCap_m.png | Bin 0 -> 2714 bytes .../resources/meshes/keyboard/keyCap_n.png | Bin 0 -> 2230 bytes .../resources/meshes/keyboard/keyCap_o.png | Bin 0 -> 3508 bytes .../resources/meshes/keyboard/keyCap_p.png | Bin 0 -> 2282 bytes .../resources/meshes/keyboard/keyCap_q.png | Bin 0 -> 3681 bytes .../resources/meshes/keyboard/keyCap_r.png | Bin 0 -> 2833 bytes .../resources/meshes/keyboard/keyCap_s.png | Bin 0 -> 3405 bytes .../resources/meshes/keyboard/keyCap_t.png | Bin 0 -> 1641 bytes .../resources/meshes/keyboard/keyCap_u.png | Bin 0 -> 2537 bytes .../resources/meshes/keyboard/keyCap_v.png | Bin 0 -> 3024 bytes .../resources/meshes/keyboard/keyCap_w.png | Bin 0 -> 4149 bytes .../resources/meshes/keyboard/keyCap_x.png | Bin 0 -> 3327 bytes .../resources/meshes/keyboard/keyCap_y.png | Bin 0 -> 2701 bytes .../resources/meshes/keyboard/keyCap_z.png | Bin 0 -> 2394 bytes interface/resources/meshes/keyboard/key_0.png | Bin 0 -> 3132 bytes interface/resources/meshes/keyboard/key_1.png | Bin 0 -> 1909 bytes .../resources/meshes/keyboard/key_123.png | Bin 0 -> 4399 bytes interface/resources/meshes/keyboard/key_2.png | Bin 0 -> 2864 bytes interface/resources/meshes/keyboard/key_3.png | Bin 0 -> 3341 bytes interface/resources/meshes/keyboard/key_4.png | Bin 0 -> 2126 bytes interface/resources/meshes/keyboard/key_5.png | Bin 0 -> 3071 bytes interface/resources/meshes/keyboard/key_6.png | Bin 0 -> 3631 bytes interface/resources/meshes/keyboard/key_7.png | Bin 0 -> 1921 bytes interface/resources/meshes/keyboard/key_8.png | Bin 0 -> 3535 bytes interface/resources/meshes/keyboard/key_9.png | Bin 0 -> 3617 bytes interface/resources/meshes/keyboard/key_a.png | Bin 0 -> 3066 bytes .../resources/meshes/keyboard/key_abc.png | Bin 0 -> 5346 bytes .../meshes/keyboard/key_ampersand.png | Bin 0 -> 3695 bytes .../resources/meshes/keyboard/key_ast.png | Bin 0 -> 2196 bytes .../resources/meshes/keyboard/key_at.png | Bin 0 -> 4722 bytes interface/resources/meshes/keyboard/key_b.png | Bin 0 -> 2839 bytes .../meshes/keyboard/key_backspace.png | Bin 0 -> 1987 bytes interface/resources/meshes/keyboard/key_c.png | Bin 0 -> 2950 bytes .../resources/meshes/keyboard/key_cap.png | Bin 0 -> 2088 bytes .../resources/meshes/keyboard/key_caret.png | Bin 0 -> 2209 bytes .../meshes/keyboard/key_close_paren.png | Bin 0 -> 2621 bytes .../resources/meshes/keyboard/key_colon.png | Bin 0 -> 1640 bytes .../resources/meshes/keyboard/key_comma.png | Bin 0 -> 1785 bytes interface/resources/meshes/keyboard/key_d.png | Bin 0 -> 2962 bytes .../resources/meshes/keyboard/key_dollar.png | Bin 0 -> 3481 bytes .../resources/meshes/keyboard/key_dquote.png | Bin 0 -> 1918 bytes interface/resources/meshes/keyboard/key_e.png | Bin 0 -> 3029 bytes .../resources/meshes/keyboard/key_enter.png | Bin 0 -> 2201 bytes .../resources/meshes/keyboard/key_exclam.png | Bin 0 -> 1633 bytes .../resources/meshes/keyboard/key_exit.png | Bin 0 -> 2466 bytes interface/resources/meshes/keyboard/key_f.png | Bin 0 -> 2112 bytes interface/resources/meshes/keyboard/key_g.png | Bin 0 -> 3480 bytes interface/resources/meshes/keyboard/key_h.png | Bin 0 -> 2186 bytes .../resources/meshes/keyboard/key_hashtag.png | Bin 0 -> 2394 bytes interface/resources/meshes/keyboard/key_i.png | Bin 0 -> 1625 bytes interface/resources/meshes/keyboard/key_j.png | Bin 0 -> 2070 bytes interface/resources/meshes/keyboard/key_k.png | Bin 0 -> 2389 bytes interface/resources/meshes/keyboard/key_l.png | Bin 0 -> 1907 bytes interface/resources/meshes/keyboard/key_m.png | Bin 0 -> 2438 bytes .../resources/meshes/keyboard/key_min.png | Bin 0 -> 1557 bytes interface/resources/meshes/keyboard/key_n.png | Bin 0 -> 2165 bytes interface/resources/meshes/keyboard/key_o.png | Bin 0 -> 3054 bytes .../meshes/keyboard/key_open_paren.png | Bin 0 -> 2656 bytes interface/resources/meshes/keyboard/key_p.png | Bin 0 -> 2821 bytes .../meshes/keyboard/key_percentage.png | Bin 0 -> 4316 bytes .../resources/meshes/keyboard/key_period.png | Bin 0 -> 1591 bytes .../resources/meshes/keyboard/key_plus.png | Bin 0 -> 1635 bytes interface/resources/meshes/keyboard/key_q.png | Bin 0 -> 3012 bytes .../meshes/keyboard/key_question.png | Bin 0 -> 2727 bytes interface/resources/meshes/keyboard/key_r.png | Bin 0 -> 1897 bytes interface/resources/meshes/keyboard/key_s.png | Bin 0 -> 3080 bytes .../resources/meshes/keyboard/key_semi.png | Bin 0 -> 1797 bytes .../resources/meshes/keyboard/key_slash.png | Bin 0 -> 2580 bytes .../resources/meshes/keyboard/key_squote.png | Bin 0 -> 1783 bytes interface/resources/meshes/keyboard/key_t.png | Bin 0 -> 2038 bytes interface/resources/meshes/keyboard/key_u.png | Bin 0 -> 2232 bytes .../resources/meshes/keyboard/key_under.png | Bin 0 -> 1579 bytes interface/resources/meshes/keyboard/key_v.png | Bin 0 -> 2671 bytes interface/resources/meshes/keyboard/key_w.png | Bin 0 -> 3466 bytes interface/resources/meshes/keyboard/key_x.png | Bin 0 -> 2997 bytes interface/resources/meshes/keyboard/key_y.png | Bin 0 -> 3072 bytes interface/resources/meshes/keyboard/key_z.png | Bin 0 -> 2115 bytes .../meshes/keyboard/text_placard.png | Bin 0 -> 1308 bytes interface/resources/meshes/keyboard/white.png | Bin 0 -> 293 bytes .../resources/qml/controlsUit/Keyboard.qml | 20 +- .../qml/dialogs/TabletLoginDialog.qml | 4 + interface/resources/qml/hifi/AvatarApp.qml | 11 +- .../resources/qml/hifi/avatarapp/Settings.qml | 24 + .../qml/hifi/commerce/wallet/Wallet.qml | 4 + .../qml/hifi/dialogs/TabletRunningScripts.qml | 4 + interface/resources/qml/hifi/tablet/Edit.qml | 6 + .../qml/hifi/tablet/TabletAddressDialog.qml | 8 + .../tabletWindows/TabletPreferencesDialog.qml | 4 + interface/resources/sounds/keyboard_key.mp3 | Bin 0 -> 9089 bytes interface/src/Application.cpp | 17 +- interface/src/Menu.cpp | 2 + interface/src/Menu.h | 1 + .../src/raypick/PickScriptingInterface.cpp | 12 +- .../src/raypick/PointerScriptingInterface.cpp | 30 +- interface/src/raypick/RayPick.cpp | 8 +- interface/src/raypick/StylusPick.cpp | 18 +- interface/src/raypick/StylusPick.h | 9 +- interface/src/raypick/StylusPointer.cpp | 34 +- interface/src/raypick/StylusPointer.h | 7 +- .../src/scripting/HMDScriptingInterface.cpp | 7 +- .../src/scripting/HMDScriptingInterface.h | 2 + .../scripting/KeyboardScriptingInterface.cpp | 34 + .../scripting/KeyboardScriptingInterface.h | 43 + interface/src/ui/Keyboard.cpp | 799 ++++++ interface/src/ui/Keyboard.h | 152 + interface/src/ui/overlays/ModelOverlay.cpp | 2 +- interface/src/ui/overlays/Overlays.cpp | 53 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 + libraries/render-utils/src/Model.cpp | 2 +- .../ui/src/ui/TabletScriptingInterface.cpp | 6 + .../ui/src/ui/TabletScriptingInterface.h | 1 + .../controllerModules/inEditMode.js | 17 + .../nearParentGrabOverlay.js | 6 +- scripts/system/marketplaces/marketplaces.js | 5 + 131 files changed, 3777 insertions(+), 53 deletions(-) create mode 100644 interface/resources/config/keyboard.json create mode 100644 interface/resources/meshes/drumstick.fbx create mode 100644 interface/resources/meshes/keyboard/SM_enter.fbx create mode 100644 interface/resources/meshes/keyboard/SM_key.fbx create mode 100644 interface/resources/meshes/keyboard/SM_space.fbx create mode 100644 interface/resources/meshes/keyboard/keyCap_F.png create mode 100644 interface/resources/meshes/keyboard/keyCap_a.png create mode 100644 interface/resources/meshes/keyboard/keyCap_b.png create mode 100644 interface/resources/meshes/keyboard/keyCap_c.png create mode 100644 interface/resources/meshes/keyboard/keyCap_d.png create mode 100644 interface/resources/meshes/keyboard/keyCap_e.png create mode 100644 interface/resources/meshes/keyboard/keyCap_g.png create mode 100644 interface/resources/meshes/keyboard/keyCap_h.png create mode 100644 interface/resources/meshes/keyboard/keyCap_i.png create mode 100644 interface/resources/meshes/keyboard/keyCap_j.png create mode 100644 interface/resources/meshes/keyboard/keyCap_k.png create mode 100644 interface/resources/meshes/keyboard/keyCap_l.png create mode 100644 interface/resources/meshes/keyboard/keyCap_m.png create mode 100644 interface/resources/meshes/keyboard/keyCap_n.png create mode 100644 interface/resources/meshes/keyboard/keyCap_o.png create mode 100644 interface/resources/meshes/keyboard/keyCap_p.png create mode 100644 interface/resources/meshes/keyboard/keyCap_q.png create mode 100644 interface/resources/meshes/keyboard/keyCap_r.png create mode 100644 interface/resources/meshes/keyboard/keyCap_s.png create mode 100644 interface/resources/meshes/keyboard/keyCap_t.png create mode 100644 interface/resources/meshes/keyboard/keyCap_u.png create mode 100644 interface/resources/meshes/keyboard/keyCap_v.png create mode 100644 interface/resources/meshes/keyboard/keyCap_w.png create mode 100644 interface/resources/meshes/keyboard/keyCap_x.png create mode 100644 interface/resources/meshes/keyboard/keyCap_y.png create mode 100644 interface/resources/meshes/keyboard/keyCap_z.png create mode 100644 interface/resources/meshes/keyboard/key_0.png create mode 100644 interface/resources/meshes/keyboard/key_1.png create mode 100644 interface/resources/meshes/keyboard/key_123.png create mode 100644 interface/resources/meshes/keyboard/key_2.png create mode 100644 interface/resources/meshes/keyboard/key_3.png create mode 100644 interface/resources/meshes/keyboard/key_4.png create mode 100644 interface/resources/meshes/keyboard/key_5.png create mode 100644 interface/resources/meshes/keyboard/key_6.png create mode 100644 interface/resources/meshes/keyboard/key_7.png create mode 100644 interface/resources/meshes/keyboard/key_8.png create mode 100644 interface/resources/meshes/keyboard/key_9.png create mode 100644 interface/resources/meshes/keyboard/key_a.png create mode 100644 interface/resources/meshes/keyboard/key_abc.png create mode 100644 interface/resources/meshes/keyboard/key_ampersand.png create mode 100644 interface/resources/meshes/keyboard/key_ast.png create mode 100644 interface/resources/meshes/keyboard/key_at.png create mode 100644 interface/resources/meshes/keyboard/key_b.png create mode 100644 interface/resources/meshes/keyboard/key_backspace.png create mode 100644 interface/resources/meshes/keyboard/key_c.png create mode 100644 interface/resources/meshes/keyboard/key_cap.png create mode 100644 interface/resources/meshes/keyboard/key_caret.png create mode 100644 interface/resources/meshes/keyboard/key_close_paren.png create mode 100644 interface/resources/meshes/keyboard/key_colon.png create mode 100644 interface/resources/meshes/keyboard/key_comma.png create mode 100644 interface/resources/meshes/keyboard/key_d.png create mode 100644 interface/resources/meshes/keyboard/key_dollar.png create mode 100644 interface/resources/meshes/keyboard/key_dquote.png create mode 100644 interface/resources/meshes/keyboard/key_e.png create mode 100644 interface/resources/meshes/keyboard/key_enter.png create mode 100644 interface/resources/meshes/keyboard/key_exclam.png create mode 100644 interface/resources/meshes/keyboard/key_exit.png create mode 100644 interface/resources/meshes/keyboard/key_f.png create mode 100644 interface/resources/meshes/keyboard/key_g.png create mode 100644 interface/resources/meshes/keyboard/key_h.png create mode 100644 interface/resources/meshes/keyboard/key_hashtag.png create mode 100644 interface/resources/meshes/keyboard/key_i.png create mode 100644 interface/resources/meshes/keyboard/key_j.png create mode 100644 interface/resources/meshes/keyboard/key_k.png create mode 100644 interface/resources/meshes/keyboard/key_l.png create mode 100644 interface/resources/meshes/keyboard/key_m.png create mode 100644 interface/resources/meshes/keyboard/key_min.png create mode 100644 interface/resources/meshes/keyboard/key_n.png create mode 100644 interface/resources/meshes/keyboard/key_o.png create mode 100644 interface/resources/meshes/keyboard/key_open_paren.png create mode 100644 interface/resources/meshes/keyboard/key_p.png create mode 100644 interface/resources/meshes/keyboard/key_percentage.png create mode 100644 interface/resources/meshes/keyboard/key_period.png create mode 100644 interface/resources/meshes/keyboard/key_plus.png create mode 100644 interface/resources/meshes/keyboard/key_q.png create mode 100644 interface/resources/meshes/keyboard/key_question.png create mode 100644 interface/resources/meshes/keyboard/key_r.png create mode 100644 interface/resources/meshes/keyboard/key_s.png create mode 100644 interface/resources/meshes/keyboard/key_semi.png create mode 100644 interface/resources/meshes/keyboard/key_slash.png create mode 100644 interface/resources/meshes/keyboard/key_squote.png create mode 100644 interface/resources/meshes/keyboard/key_t.png create mode 100644 interface/resources/meshes/keyboard/key_u.png create mode 100644 interface/resources/meshes/keyboard/key_under.png create mode 100644 interface/resources/meshes/keyboard/key_v.png create mode 100644 interface/resources/meshes/keyboard/key_w.png create mode 100644 interface/resources/meshes/keyboard/key_x.png create mode 100644 interface/resources/meshes/keyboard/key_y.png create mode 100644 interface/resources/meshes/keyboard/key_z.png create mode 100644 interface/resources/meshes/keyboard/text_placard.png create mode 100644 interface/resources/meshes/keyboard/white.png create mode 100644 interface/resources/sounds/keyboard_key.mp3 create mode 100644 interface/src/scripting/KeyboardScriptingInterface.cpp create mode 100644 interface/src/scripting/KeyboardScriptingInterface.h create mode 100644 interface/src/ui/Keyboard.cpp create mode 100644 interface/src/ui/Keyboard.h diff --git a/interface/resources/config/keyboard.json b/interface/resources/config/keyboard.json new file mode 100644 index 0000000000..ba113da1f5 --- /dev/null +++ b/interface/resources/config/keyboard.json @@ -0,0 +1,2476 @@ +{ + "anchor": { + "dimensions": { + "x": 0.023600000888109207, + "y": 0.022600000724196434, + "z": 0.1274999976158142 + }, + "position": { + "x": 0.006292800903320312, + "y": 0.004300000742077827, + "z": 0.005427663803100586 + }, + "rotation": { + "w": 1.000, + "x": 0.000, + "y": 0.000, + "z": 0.000 + } + }, + "textDisplay": { + "dimensions": { + "x": 0.15, + "y": 0.045, + "z": 0.1 + }, + "localPosition": { + "x": -0.3032040786743164, + "y": 0.059300000742077827, + "z": 0.06454843521118164 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.976, + "z": 0.216 + }, + "leftMargin": 0.0, + "rightMargin": 0.0, + "topMargin": 0.0, + "bottomMargin": 0.0, + "lineHeight": 0.05 + }, + "useResourcesPath": true, + "layers": [ + [ + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5332040786743164, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "key": "p", + "texture": { + "file9": "meshes/keyboard/key_p.png", + "file10": "meshes/keyboard/key_p.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.42067813873291016, + "y": 0.019300000742077827, + "z": 0.03744244575500488 + }, + "key": "i", + "texture": { + "file9": "meshes/keyboard/key_i.png", + "file10": "meshes/keyboard/key_i.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "o", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47769832611083984, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "texture": { + "file9": "meshes/keyboard/key_o.png", + "file10": "meshes/keyboard/key_o.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.49904823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "l", + "texture": { + "file9": "meshes/keyboard/key_l.png", + "file10": "meshes/keyboard/key_l.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.4439973831176758, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "k", + "texture": { + "file9": "meshes/keyboard/key_k.png", + "file10": "meshes/keyboard/key_k.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "y", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_y.png", + "file10": "meshes/keyboard/key_y.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.30902957916259766, + "y": 0.019300000742077827, + "z": 0.0374448299407959 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "r", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_r.png", + "file10": "meshes/keyboard/key_r.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.19474029541015625, + "y": 0.019300000742077827, + "z": 0.03745102882385254 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "d", + "texture": { + "file9": "meshes/keyboard/key_d.png", + "file10": "meshes/keyboard/key_d.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.16272640228271484, + "y": 0.019300000742077827, + "z": -0.01747274398803711 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "t", + "texture": { + "file9": "meshes/keyboard/key_t.png", + "file10": "meshes/keyboard/key_t.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2517843246459961, + "y": 0.019300000742077827, + "z": 0.03744622692465782 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "f", + "texture": { + "file9": "meshes/keyboard/key_f.png", + "file10": "meshes/keyboard/key_f.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2200756072998047, + "y": 0.019300000742077827, + "z": -0.01746511459350586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "g", + "texture": { + "file9": "meshes/keyboard/key_g.png", + "file10": "meshes/keyboard/key_g.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27622222900390625, + "y": 0.019300000742077827, + "z": -0.017457084730267525 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "w", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_w.png", + "file10": "meshes/keyboard/key_w.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08203601837158203, + "y": 0.019300000742077827, + "z": 0.03743100166320801 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "q", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_q.png", + "file10": "meshes/keyboard/key_q.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026292800903320312, + "y": 0.019300000742077827, + "z": 0.037427663803100586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "a", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_a.png", + "file10": "meshes/keyboard/key_a.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.050909996032714844, + "y": 0.019300000742077827, + "z": -0.017487764358520508 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "e", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_e.png", + "file10": "meshes/keyboard/key_e.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13773441314697266, + "y": 0.019300000742077827, + "z": 0.03745269775390625 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "s", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_s.png", + "file10": "meshes/keyboard/key_s.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10659980773925781, + "y": 0.019300000742077827, + "z": -0.017481565475463867 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "u", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_u.png", + "file10": "meshes/keyboard/key_u.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36423587799072266, + "y": 0.019300000742077827, + "z": 0.03743577003479004 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "h", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_h.png", + "file10": "meshes/keyboard/key_h.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.3321561813354492, + "y": 0.019300000742077827, + "z": -0.017461776733398438 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "j", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_j.png", + "file10": "meshes/keyboard/key_j.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38753604888916016, + "y": 0.019300000742077827, + "z": -0.01746058464050293 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "c", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_c.png", + "file10": "meshes/keyboard/key_c.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.22023773193359375, + "y": 0.019300000742077827, + "z": -0.07282757759094238 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "v", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_v.png", + "file10": "meshes/keyboard/key_v.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2767200469970703, + "y": 0.019300000742077827, + "z": -0.07291850447654724 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "b", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_b.png", + "file10": "meshes/keyboard/key_b.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.33254528045654297, + "y": 0.019300000742077827, + "z": -0.07283258438110352 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": " ", + "name": "space", + "dimensions": { + "x": 0.14597539603710175, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27660465240478516, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/white.png", + "file10": "meshes/keyboard/white.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "z", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_z.png", + "file10": "meshes/keyboard/key_z.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10669422149658203, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "n", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_n.png", + "file10": "meshes/keyboard/key_n.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020292000845074654, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.0728309154510498 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "m", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_m.png", + "file10": "meshes/keyboard/key_m.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.01846799999475479, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.44464683532714844, + "y": 0.019300000742077827, + "z": -0.07282185554504395 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "x", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_x.png", + "file10": "meshes/keyboard/key_x.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.07284045219421387 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "DEL", + "type": "backspace", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53203323516845703, + "y": 0.019300000742077827, + "z": -0.07286686894893646 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_backspace.png", + "file10": "meshes/keyboard/key_backspace.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Caps", + "type": "caps", + "switchToLayer": 1, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.02603323516845703, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_cap.png", + "file10": "meshes/keyboard/key_cap.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Close", + "type": "close", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59333323516845703, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exit.png", + "file10": "meshes/keyboard/key_exit.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Enter", + "type": "enter", + "dimensions": { + "x": 0.08787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5103323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_enter.png", + "file11": "meshes/keyboard/key_enter.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "numbers", + "type": "layer", + "switchToLayer": 2, + "dimensions": { + "x": 0.07787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_123.png", + "file11": "meshes/keyboard/key_123.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + } + ], + [ + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5332040786743164, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "key": "p", + "texture": { + "file9": "meshes/keyboard/keyCap_p.png", + "file10": "meshes/keyboard/keyCap_p.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.42067813873291016, + "y": 0.019300000742077827, + "z": 0.03744244575500488 + }, + "key": "i", + "texture": { + "file9": "meshes/keyboard/keyCap_i.png", + "file10": "meshes/keyboard/keyCap_i.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "o", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47769832611083984, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "texture": { + "file9": "meshes/keyboard/keyCap_o.png", + "file10": "meshes/keyboard/keyCap_o.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.49904823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "l", + "texture": { + "file9": "meshes/keyboard/keyCap_l.png", + "file10": "meshes/keyboard/keyCap_l.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.4439973831176758, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "k", + "texture": { + "file9": "meshes/keyboard/keyCap_k.png", + "file10": "meshes/keyboard/keyCap_k.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "y", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_y.png", + "file10": "meshes/keyboard/keyCap_y.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.30902957916259766, + "y": 0.019300000742077827, + "z": 0.0374448299407959 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "r", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_r.png", + "file10": "meshes/keyboard/keyCap_r.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.19474029541015625, + "y": 0.019300000742077827, + "z": 0.03745102882385254 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "d", + "texture": { + "file9": "meshes/keyboard/keyCap_d.png", + "file10": "meshes/keyboard/keyCap_d.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.16272640228271484, + "y": 0.019300000742077827, + "z": -0.01747274398803711 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "t", + "texture": { + "file9": "meshes/keyboard/keyCap_t.png", + "file10": "meshes/keyboard/keyCap_t.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2517843246459961, + "y": 0.019300000742077827, + "z": 0.03744622692465782 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "f", + "texture": { + "file9": "meshes/keyboard/keyCap_F.png", + "file10": "meshes/keyboard/keyCap_F.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2200756072998047, + "y": 0.019300000742077827, + "z": -0.01746511459350586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "g", + "texture": { + "file9": "meshes/keyboard/keyCap_g.png", + "file10": "meshes/keyboard/keyCap_g.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27622222900390625, + "y": 0.019300000742077827, + "z": -0.017457084730267525 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "w", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_w.png", + "file10": "meshes/keyboard/keyCap_w.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08203601837158203, + "y": 0.019300000742077827, + "z": 0.03743100166320801 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "q", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_q.png", + "file10": "meshes/keyboard/keyCap_q.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026292800903320312, + "y": 0.019300000742077827, + "z": 0.037427663803100586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "a", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_a.png", + "file10": "meshes/keyboard/keyCap_a.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.050909996032714844, + "y": 0.019300000742077827, + "z": -0.017487764358520508 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "e", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_e.png", + "file10": "meshes/keyboard/keyCap_e.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13773441314697266, + "y": 0.019300000742077827, + "z": 0.03745269775390625 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "s", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_s.png", + "file10": "meshes/keyboard/keyCap_s.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10659980773925781, + "y": 0.019300000742077827, + "z": -0.017481565475463867 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "u", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_u.png", + "file10": "meshes/keyboard/keyCap_u.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36423587799072266, + "y": 0.019300000742077827, + "z": 0.03743577003479004 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "h", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_h.png", + "file10": "meshes/keyboard/keyCap_h.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.3321561813354492, + "y": 0.019300000742077827, + "z": -0.017461776733398438 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "j", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_j.png", + "file10": "meshes/keyboard/keyCap_j.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38753604888916016, + "y": 0.019300000742077827, + "z": -0.01746058464050293 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "c", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_c.png", + "file10": "meshes/keyboard/keyCap_c.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.22023773193359375, + "y": 0.019300000742077827, + "z": -0.07282757759094238 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "v", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_v.png", + "file10": "meshes/keyboard/keyCap_v.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2767200469970703, + "y": 0.019300000742077827, + "z": -0.07291850447654724 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "b", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_b.png", + "file10": "meshes/keyboard/keyCap_b.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.33254528045654297, + "y": 0.019300000742077827, + "z": -0.07283258438110352 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": " ", + "name": "space", + "dimensions": { + "x": 0.14597539603710175, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27660465240478516, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/white.png", + "file10": "meshes/keyboard/white.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "z", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_z.png", + "file10": "meshes/keyboard/keyCap_z.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10669422149658203, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "n", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_n.png", + "file10": "meshes/keyboard/keyCap_n.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020292000845074654, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.0728309154510498 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "m", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_m.png", + "file10": "meshes/keyboard/keyCap_m.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.01846799999475479, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.44464683532714844, + "y": 0.019300000742077827, + "z": -0.07282185554504395 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "x", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/keyCap_x.png", + "file10": "meshes/keyboard/keyCap_x.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.07284045219421387 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "DEL", + "type": "backspace", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53203323516845703, + "y": 0.019300000742077827, + "z": -0.07286686894893646 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_backspace.png", + "file10": "meshes/keyboard/key_backspace.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Caps", + "type": "caps", + "switchToLayer": 0, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.02603323516845703, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_cap.png", + "file10": "meshes/keyboard/key_cap.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Close", + "type": "close", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59333323516845703, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exit.png", + "file10": "meshes/keyboard/key_exit.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Enter", + "type": "enter", + "dimensions": { + "x": 0.08787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5103323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_enter.png", + "file11": "meshes/keyboard/key_enter.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "numbers", + "type": "layer", + "switchToLayer": 2, + "dimensions": { + "x": 0.07787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_123.png", + "file11": "meshes/keyboard/key_123.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + } + ], + [ + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5332040786743164, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "key": "0", + "texture": { + "file9": "meshes/keyboard/key_0.png", + "file10": "meshes/keyboard/key_0.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.42067813873291016, + "y": 0.019300000742077827, + "z": 0.03744244575500488 + }, + "key": "8", + "texture": { + "file9": "meshes/keyboard/key_8.png", + "file10": "meshes/keyboard/key_8.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "9", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47769832611083984, + "y": 0.019300000742077827, + "z": 0.03745675086975098 + }, + "texture": { + "file9": "meshes/keyboard/key_9.png", + "file10": "meshes/keyboard/key_9.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.47764823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "(", + "texture": { + "file9": "meshes/keyboard/key_open_paren.png", + "file10": "meshes/keyboard/key_open_paren.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53364823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": ")", + "texture": { + "file9": "meshes/keyboard/key_close_paren.png", + "file10": "meshes/keyboard/key_close_paren.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59634823303222656, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "/", + "texture": { + "file9": "meshes/keyboard/key_slash.png", + "file10": "meshes/keyboard/key_slash.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.4206973831176758, + "y": 0.019300000742077827, + "z": -0.01745915412902832 + }, + "key": "+", + "texture": { + "file9": "meshes/keyboard/key_plus.png", + "file10": "meshes/keyboard/key_plus.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "6", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_6.png", + "file10": "meshes/keyboard/key_6.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.30902957916259766, + "y": 0.019300000742077827, + "z": 0.0374448299407959 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "4", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_4.png", + "file10": "meshes/keyboard/key_4.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.19474029541015625, + "y": 0.019300000742077827, + "z": 0.03745102882385254 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "$", + "texture": { + "file9": "meshes/keyboard/key_dollar.png", + "file10": "meshes/keyboard/key_dollar.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13772640228271484, + "y": 0.019300000742077827, + "z": -0.01747274398803711 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "5", + "texture": { + "file9": "meshes/keyboard/key_5.png", + "file10": "meshes/keyboard/key_5.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2517843246459961, + "y": 0.019300000742077827, + "z": 0.03744622692465782 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "%", + "texture": { + "file9": "meshes/keyboard/key_percentage.png", + "file10": "meshes/keyboard/key_percentage.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1947756072998047, + "y": 0.019300000742077827, + "z": -0.01746511459350586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "_", + "texture": { + "file9": "meshes/keyboard/key_under.png", + "file10": "meshes/keyboard/key_under.png" + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.25172222900390625, + "y": 0.019300000742077827, + "z": -0.017457084730267525 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "2", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_2.png", + "file10": "meshes/keyboard/key_2.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08203601837158203, + "y": 0.019300000742077827, + "z": 0.03743100166320801 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "1", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_1.png", + "file10": "meshes/keyboard/key_1.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026292800903320312, + "y": 0.019300000742077827, + "z": 0.037427663803100586 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "@", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_at.png", + "file10": "meshes/keyboard/key_at.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026209996032714844, + "y": 0.019300000742077827, + "z": -0.017487764358520508 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "3", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_3.png", + "file10": "meshes/keyboard/key_3.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.13773441314697266, + "y": 0.019300000742077827, + "z": 0.03745269775390625 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "#", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_hashtag.png", + "file10": "meshes/keyboard/key_hashtag.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.08209980773925781, + "y": 0.019300000742077827, + "z": -0.017481565475463867 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "7", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_7.png", + "file10": "meshes/keyboard/key_7.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36423587799072266, + "y": 0.019300000742077827, + "z": 0.03743577003479004 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "&", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_ampersand.png", + "file10": "meshes/keyboard/key_ampersand.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.3090561813354492, + "y": 0.019300000742077827, + "z": -0.017461776733398438 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "-", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_min.png", + "file10": "meshes/keyboard/key_min.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.36433604888916016, + "y": 0.019300000742077827, + "z": -0.01746058464050293 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "'", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_squote.png", + "file10": "meshes/keyboard/key_squote.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.22023773193359375, + "y": 0.019300000742077827, + "z": -0.07282757759094238 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ":", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_colon.png", + "file10": "meshes/keyboard/key_colon.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.2767200469970703, + "y": 0.019300000742077827, + "z": -0.07291850447654724 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ";", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_semi.png", + "file10": "meshes/keyboard/key_semi.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.33254528045654297, + "y": 0.019300000742077827, + "z": -0.07283258438110352 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": " ", + "name": "space", + "dimensions": { + "x": 0.14597539603710175, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.27660465240478516, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/white.png", + "file10": "meshes/keyboard/white.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "*", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_ast.png", + "file10": "meshes/keyboard/key_ast.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.10669422149658203, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "!", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exclam.png", + "file10": "meshes/keyboard/key_exclam.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020292000845074654, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.0728309154510498 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "?", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_question.png", + "file10": "meshes/keyboard/key_question.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.01846799999475479, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.44464683532714844, + "y": 0.019300000742077827, + "z": -0.07282185554504395 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "\"", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_dquote.png", + "file10": "meshes/keyboard/key_dquote.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.07284045219421387 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "DEL", + "type": "backspace", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.53203323516845703, + "y": 0.019300000742077827, + "z": -0.07286686894893646 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_backspace.png", + "file10": "meshes/keyboard/key_backspace.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Caps", + "type": "caps", + "switchToLayer": 1, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.02603323516845703, + "y": 0.019300000742077827, + "z": -0.07285571098327637 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_cap.png", + "file10": "meshes/keyboard/key_cap.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Close", + "type": "close", + "dimensions": { + "x": 0.04787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.59333323516845703, + "y": 0.019300000742077827, + "z": 0.037454843521118164 + }, + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_exit.png", + "file10": "meshes/keyboard/key_exit.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "Enter", + "type": "enter", + "dimensions": { + "x": 0.08787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.5103323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_enter.png", + "file11": "meshes/keyboard/key_enter.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": "numbers", + "type": "layer", + "switchToLayer": 0, + "dimensions": { + "x": 0.07787999764084816, + "z": 0.020519999787211418, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.026323516845703, + "y": 0.019300000742077827, + "z": -0.127054843521118164 + }, + "modelURL": "meshes/keyboard/SM_enter.fbx", + "texture": { + "file10": "meshes/keyboard/key_abc.png", + "file11": "meshes/keyboard/key_abc.png" + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ".", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_period.png", + "file10": "meshes/keyboard/key_period.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.38794422149658203, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + }, + { + "key": ",", + "modelURL": "meshes/keyboard/SM_key.fbx", + "texture": { + "file9": "meshes/keyboard/key_comma.png", + "file10": "meshes/keyboard/key_comma.png" + }, + "dimensions": { + "x": 0.04787999764084816, + "z": 0.02051999792456627, + "y": 0.04787999764084816 + }, + "position": { + "x": -0.1630725860595703, + "y": 0.019300000742077827, + "z": -0.12705934047698975 + }, + "localOrientation": { + "w": 0.000, + "x": 0.000, + "y": 0.707, + "z": 0.707 + } + } + ] + ] +} diff --git a/interface/resources/meshes/drumstick.fbx b/interface/resources/meshes/drumstick.fbx new file mode 100644 index 0000000000000000000000000000000000000000..0243d9fd1b89f0dfe6572a7ec293a38ec571ce53 GIT binary patch literal 63952 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/SM_enter.fbx b/interface/resources/meshes/keyboard/SM_enter.fbx new file mode 100644 index 0000000000000000000000000000000000000000..119e3fc5356a9dcaf271190d3e64912fdba4a3f3 GIT binary patch literal 33248 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 SA;rMJz~JfX=d#Wzp$Py6*tu~4 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_a.png b/interface/resources/meshes/keyboard/keyCap_a.png new file mode 100644 index 0000000000000000000000000000000000000000..f409254292dbe07c27c37341049f2c1f584f1f2e GIT binary patch literal 3081 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_d.png b/interface/resources/meshes/keyboard/keyCap_d.png new file mode 100644 index 0000000000000000000000000000000000000000..4eccee2dd533ea24e6bca5e6ab3114fce37db803 GIT binary patch literal 2648 zcmeHJZB&eD7{1@v$UChvW#|LVs1|9gLHf{C6Ka~3eAGyXKF~y%3Z*bLZ|UP`%0%a+ zXlccUP_n0dw39>#Vc?Hv4mbJm?sFLc#1ZyNSD%=+H|_F0Z5|-*d^zXcIwEUgmNnA{e1g^?YvWf>tea*$zxV>& zMkUh{4V#d%Z4J+!eZMbr)dt(kgRxW60xt(=<)}kjMm`M3FyhcW!5EzYDBu8808Cre zzlY=De09_}(y?(={MG5-Ob7HEtovNZ_?`cv<4YXpPXDActL_-1UWK72OL_8PiuyxR zWkHWKMa0S%6Vu)EQ&LhgHFg1+8Qj*I8n;jNfr%8pD+c^N81JY z#1h5Pi!c*oQEle^yJgA16rp`(G|7XK@>{n`=1`-qq6vYGr92Mj#EBEL{iFP6kO**a zE^YHKjjw~~Pj}n8PeAYvhnf1T|Ir+R;CGVJw!uD%qJE7YRp#QU7WR|?BD?L0GOB)CPGCV-Pft%NPriNU zX|IAcYt{sK90BHKy2{9{Usf^J{w`U^X7%;h1MhqT@CdQ$8yIM6-Y-?EUZu}RA++uT zzd|0}jLT%3gjzvQK^uj*Hn>TN7)`H6-yEcFrmE^WHd>#AORSeedL1iu@( zSM;>Hy1Fxho{hmd%lfgFmX`k7i^{ejbI8c9*=nFEr`~2_U}+U+Ahk*NrxM_?cNTZl zTr8;~fyZOMvxrd@R<2mzB2QLV0gh@GP}x!bEVIrR zFRJL~ka4;_%;-R3oza8CASV9Gn^A!lPfP^n!DTsW{|TwUJh-8d^U~mrh6))kUkgT^ z#MPZr53QXtnXJwt*v~e{GtCkOtJdAN$4+P_g$M{>*jUON?G@0?M0#^jup(X;jP_Qj zDFiU|Zi(V>`fIHukF__F7V*O6)Wc;CO?+p&(-@rlnJ0T4Y3kfHWoeElgFr^`;1JED z9b}C{P@utJAo}CN0AmgTe)rEFa?Dj9Rw{Spr-Jxqq4JijwSu;)K$9yqS^+N2rEhoJ z3I!cKE?mFv0g5%)@3OPAJ6xG3Yt=MLxdAt3dT%pQmG#Ck!T;1V$ zd19uzJx^>PTRGU)Jq3kpS&~=b74D02 zpGU{A8#F$5`f6r_wDSI;-9dA+^8{9U<3U!>CU{)cBzTu_M3RS&7u;qNxY1Kvs@ zK>ZjMB1%)w(p459brjZAy=idRXEHBxq34Klux)S*g8_-MW~_Jc)o3f4aCFV+km|Qq ziZv*XmC&3zO%OcJD%IRm-SIXFm?FZ7v~j8bRNW~p7lF9;O#fkeSI>a}a?+*)!x#Q~A9=Q?%3_owMY{W9vlW2_ae=xh zh*{lUL0>iGxZvOYJT8d*Kjy#Xi}PR1>2VI{zI_5B9k>f;9upnkMFjw0yLqj=x;!Z1 EPktEd`Tzg` literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_e.png b/interface/resources/meshes/keyboard/keyCap_e.png new file mode 100644 index 0000000000000000000000000000000000000000..09bd5bc289d89475106bc4583820f10cdaa0f7f4 GIT binary patch literal 1689 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|T42r;B4q#jQ7Y59YQQ2sm7{Sk$1I>Q#_F#ldsglD7#; zOB;Gc8b2JW{yz1?jHllFc7$FPXE^^kmxY0ep@D%xfq{VoWG(4Ta`YfsMWJ>MGF*&g zJH^@=m?#K3utUfXd6++7G?^x$Yd^pq-P4fy_IKUsQdgI>Tk(3=SDg}>@#SsK+i9;~ z?d>;wqd&cvg@qK$3IxKPiu`Bp%)LG5yqKUF2V;Y<{ieO;#SP{%jEv~klac}ttUt=J zW9jwRTi^eF+AkF$zP-Qi=8|@9&o09@ zT{ngYKN!xOkMBQCiZu@^x&;l^md5fjo_p+O(9m8)T6AEEIxIq@#DfBZ8|6k|Bt+6& uK#}%_f!3}-Ug3+BdB`kC>wgO-FjNN{?^~8w#mvCKz~JfX=d#Wzp$Py-;?-vW literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_g.png b/interface/resources/meshes/keyboard/keyCap_g.png new file mode 100644 index 0000000000000000000000000000000000000000..9c52605428d9c8b82cf213068b1cb6e0ddde9f7d GIT binary patch literal 3298 zcmc&$TTl~b7Cs39BccH`aw(b!B8b!!wOoY+xfN{zS)_s(xrJCn5D2#rNCZ~_gHT1l z1q8Gfwty6}lC=SXEjG$UWLQ)#*&u?Dg>VhT07-W2OlO>(edyxDKAbtv02kl696`Qe&Om1fQ0vke(zEZUFrb9faZ3R_NSN#T2e?H1)RfULMeF9^C47< zFC`>ACH5)hFo4!2PgiHZ*K})hOCf@7i(NIH8v@WlnrbW8?xZNWs%sxhFsDf zwLM4~uQ6$6dZzaCH{VnYrQgOCju&cKcc%1+qkkf{G&h%mkE5z#BcK2fKmyQ)b;18H zUHjvb|9Cp;Bc%IH@%1bJd&7SB>wM`y=`7vVsI5Lb^78UZy7#pHD#KtfL}k^xlsf;_ zd9x+DLMr{+vPEl8W0%HQrd+zP5V~*f_<8P7ePw@9g92G}Hb#mooPLxxLqM}j>PQbj~^O@E?ITy~{OQX`$7!8h@4zEaY)5nF81nb6p^6;!I9=Ea~)0wRl7ma53 zGObI#+H+%=aD*GQUl^{BsQ4+=@BAS{xmQEjq)5;bVC=``a_yY@1M8O8)}~bm6e}mv zx}>JxqTZwC6#sD6GS$mIgh3u{~U_Hu5gitL)9BNK9LHy#4DB#>n`jq@vd5 zEHS?qw+}2VBKoLQYKr7|cC0*K7W!rLmw!E_9;{Bfx3sFbeoe`fukr}f2Mu%M0$f;F zxVP=#WU;b{CRO#u?D!=ss{MHk7vm3pfn>ckyO~K`dK!t?woCIzWW{7?htbvvsUUmT z=g13EB9$k!Hq5ngGR>z9oIPY8UEQY1I#S#-A;vU1yy2jkUhLTz#`iY2C*mF+S}}n+ zQHx4E(aU#yyOYdb$b2+XPMlq$MG{ zJB_wjhJ3+ve!Aypvh(ZJhFo)JwsQZnXGmJpM%(6rsKWFargNe~)i*vqKC=*SH7^Sc zQwQd^=>rq`;YFSIKHZ2U-D)3Jbrun_F{8R2`Oln#w|uH7BrMT|lNcmPJ}eTMIUV5+ zJuui+cA8`qI4UOw6gm=#4mG;9V17#=5VS8c2PI9tI23EalRtl~roYj^8O+P8s;b&O zF1*u2v3%*WZcb~Sa#_U!pT`gs6!hvc9twIsdD)>_!m_&2ND@|UB&P!p1-_%o^68uJ zcjUX%k!?~Oij{Lfu>nz0+nTQU%fi&e#6;6BG~6pl*VcdXtwY8mzTuWDSh z914^(D=YRzZlsWU9pS93?EcY5Y4nto2CQw4``vHTPhX+8w36$uE?0F&a7s!_WQzOT z-TuH}h_7ULlL^|2naU>CUE)cE50*-$KAisLmRFT{16K3EO6svtb&}~=!h=%%cuY!e z1+#Bf)~#qjvRnwrYrHM5qPbQp6S})%JZqskNB_Yhy*jK;%{aI_f`dM$tqmPx3n#8^ zMqA;CuNKc(Zhu(%40VO7r!CMsILYB~jBr1a>jgg>D{@?7u6DUb#SsTYr*!v$#drQS zubxR z&b0DJ8`0oaKpCLfe`OcE2@MqNKGw+%X8{d{fQJ5aq&k1jUJ_J?s=b&>Fv!~|h@V-a zlbuoUPeNzo$qn1aE}!8)NVIcwTw2%#`GYJm&E7Eg=zP@O6Ah8EQY3;kETy;TpWKxG z1>WN3n@^)jFyhT~K|UVlwho=3@QFxBHKK+U?l5>V?-VK?lq@eU##i3fMFTEzuXdkA zOruL3kIbWH$OGa$XnRewFDp*M?NhTF#b{(EBdrv&Ks;RN(lUy|bjd22wnl?WG#C-p zI>+OZvXYsbGK7-1NiO^}rvf3hgq8Q5GV+k2qm$FKiN*fNo1Jy)qgM6x^+S@TDPKJJ zT+?m08ImL>of1@4Rdq)!UUkeGNLyQZ{Me1r(%j4tg^N6$x0O0%9Jk}`Zg!Lo9Yq7+ z3NzaP2`m6S0Q&tH`oF{Va6Sly4Bq82x!yk#obhk#Nl(%M literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_h.png b/interface/resources/meshes/keyboard/keyCap_h.png new file mode 100644 index 0000000000000000000000000000000000000000..f29323da1173bea026ac0f1f5f0ff97d51fae35c GIT binary patch literal 1616 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V9T==L4kpR945(n z4%F)@Cj<*7ay;Nh^CkY&m%#Xs;+hqM7 zaB*OGpR~+Cj`jxm7`FpDYfW!Ab}AYBGCpuv*Fd7J7*S1{qcOD~;GozDOjGek4r<6z nsC^h_L*hc@!R~$rW`?GV>faV^YB|8bz`)??>gTe~DWM4fe!z+W literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_i.png b/interface/resources/meshes/keyboard/keyCap_i.png new file mode 100644 index 0000000000000000000000000000000000000000..721e6b84b1a7626aeadc8439c65bf580709b88db GIT binary patch literal 1581 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V=MPZ!6Kid%1P9OP|K5O8%g6-&}AVJmUUEZd}Jmhi~+ zz|7xq|DLMuc00m!;D3%9g8%~q3j+fqg98Iu%z@SJK$+ty*DgRo&_P0+?2v~=7>p*< zBn<6r+h#ZHoV)${^W$fGI2d}CZw*SU?7J{jvged zI2t%9O0Ja0IR4D?;z%CTvuQ!UKFCyUb88W0s5`;Uff2&^jX=*)6l?cE4nU-^B$`RI zLhQ+cLhTLYB{U>kDNJ(;l%;s`wV&5xWMepJC4PT@ov9oH0|SGntDnm{r-UW|>3f0w literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_j.png b/interface/resources/meshes/keyboard/keyCap_j.png new file mode 100644 index 0000000000000000000000000000000000000000..7186df71c1026d15ea1426e95e77882ae4318d6c GIT binary patch literal 2213 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|SS(hG8{;(RA-$d${;&Ov>W|(gxKG)|BSSdpIArFf%7)_>0=-L~4Y7JRFOg@_? z{P}TcsHF3z3%B2YcVqnbIr`%3qkneX*DQJ{B+$UXpuoVu0ZPW?Fv-z_WK{!$1QXGT z_JnI_C>tYVgN~_hvVZ`Ch1>b_=h+xRNxosbBo7-{%Jm-ei|@aW2{1P3>|}3XV5ncL z!PVTr;4p35?c6i{(^E5f1Q||D@^+BvzgM^4oa@8;^)BnhKR@1m_nec$97cz_NA_$v z-`p7e{y|8MT@=2rHMv*BvG z(zqA9cGaNJ=LqqfTI}4co`0zm>F4|@?M}ETIxZ9B_&$|^B zQWH1*I~-V3Tkxrt_g{vI)zAA?e?FX_wXCwTl5_pU{$D@meyqsxf5^iSWhTS-SUjUl z>qy%6+c$%sobXM!B>KN(TX{ikZLYr}!-Y*(v!?#A=4t;w`Q(v#P7DiDC&r)u#3$C> zde>#&9cG1x|K}FoKY!1(yS{*pVeX5Jqkhp{i+1eazkRjO6r*$7abfw>z;YY*I^4bsofXDu?mE43keE^;j4_S*U05#qBr!_m;c; zP>Fosm&4in@9Xp1Tw!Evc+<-B=bhBVPB|8ahWs^5Oz0Vx=o|qlvnkf@Oi>ZQz%;e}{g3sptlC@%BU|{fc^>bP0l+XkK^(*8l literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_k.png b/interface/resources/meshes/keyboard/keyCap_k.png new file mode 100644 index 0000000000000000000000000000000000000000..69667f42e0c4bf63668dee1391701b5659cbddc6 GIT binary patch literal 2800 zcmd^>aa2=P9LIleXT^&UwlpCRxj7;&!C~&0A#4kQT#6_sXzMTop;}T=X<~R z`}y5_?%ltB-3zmP=lKG_EZ*waBmfc+4@$iD%v<#t06dl(mz=v%{7P=Va927!pC;as zPUne)8R<#s!nA^%i|J7SzVGm2pWh_CK58`P7~%Ag$T%^Ns&=Lq6K!4D9pff}U0LAMx3bExQv^^5c!l%F4<>Xwa!@fD1qY zAOj#XYW}x!K<;bu_b=z(2fAtEGbVqxU^6S+UD>^IbhJEFaq_Clpa36$!s0skU*GasNuNv->XLY z?Joem*RFF6ewUx=#3Z6LXwrF>%NRiU`eay~#&z`fulOovQRvVLr3ZzQoHK&d)Kpum>!?|*C+w@8=eNvr z%S>An$#8xxZtv&FyXAV8cbJP~(n~6jBt>V-T1o}9(Q?`qj#r8}(;kL;b0wVBC*X`a zHnX-oL4`ue=OV+HH0&9-LGwie3MGd}OU>a8lP~wj1$CDjRr07~H$ZJb+j!x&F z<2a8EeUMSdra~FZU7qEjbzUJ#_cp^SEm$l6vp=y>Yu#hn4_v>dAI5rF2Z_9~N>7u< zrL$!b#a-~NyV){+RExER042*=dwpE&3;_zSDja9td6zA6#(Ecoyt8|!vsLsOArUIo zjIEx4{HwDOMNsce=1KFigL?@}w6Slc;OEoxJFgTnfU@dXe*30rLc*x!vbp`^99;_m ze|G|2J5>JNcTBfNY^O$-Z=?X) z(E(&==(}AQzN>2y3SYH^b+Zz#B8Wmd9x!Q-07M~OQrjJ+I~;h*n4N+Z1Sx=f#4KMC ztW=w)*tR6IKi%k)oJ0oFiu!e?)KPZDPR~oYo)zA`Lr8_mzTTVo;A`8=*>qgU#UwrD z@i4G45oi4>pR6OKv(p6qRAP>Go&cGtRa95t78!!6#e@ld$dk$EAI9IaIY!%GB?E)t zlTY$YV6NE}Wn36&c~$unZQX=@akL*72Ad5v3}H}+rSb0Z<6q}o!fCgNgsb)V;Oi@ft-fajC(P`#ss~7btL#*EhLba= z|50b&NFo_g;7o9EzDnHE#aV2>KtN+WEr~4&EL{K;U4&nPK27k15s?yjqT+gdB?1-+ z#y0W!!)545TnJDS4H{D_-KO#aTpcF})--=&`-lpty6>eA2`u!6T%YyBWo6A4Z~O?E zX~a9B`47^8l33LeQ=6Vc40^7*Fle9*CBuzOgT^}Lvi$Cu_hux6geVRj2&N7av~E1L zYxB)+w@^VOM%C6SS750zyVbo{OzeJjSHQj~KQ7Q4l9>K=>WkhQ=vfI>rO1@~hvxNU z*!S353*=iqE=5o%*>pDrpC8xb8?Dk2{ORv^3?9<{LF4qcCFad^8)G_4Qd>-Y;6dO< zHC2>(8B5<1cFA+D{gSuaOQt%K!7%zHuO$QH*zx0C(AUe>;Kx;eF)Z1MJ_GlmD+*pvgV*ha$lJ$n|(dl`R_pfETwewmD||zTW_K C1v!ZT literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_l.png b/interface/resources/meshes/keyboard/keyCap_l.png new file mode 100644 index 0000000000000000000000000000000000000000..d59c24f7e24a7223186b2907e536504da6623385 GIT binary patch literal 1631 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|OhQr;B4q#jQ7Y&vLdHh`3!W+|V#3iT#*Y5_i}N)g>a< z6`RYA6_ozX*)0C#^dl#;Or=N62Y%Q1GAJ-GaDXgqU?7V*u-Y3ab3EnR6(|TgNQjdi z@~{Yl(PWy0q5V!oHq*NG*LJOY{qFqkb>5mY1M=Q+GdXSek`74-&Rs>;9j3 z|9fBO=bcPUL>u>@LayZ@o0sPKIwRHRZ>Ctr3k!xZR49f!kQs$=Z4{;@rUWMP0}~Pl zWXA5#SY8q@V-&SbB~z?aTTM_Tf<+MfNQGihP^&@&0f7Ly>e$p4 zh)64)u|j;HR13jYL~4W}3ZWEHgknVvHby`xBs?#0lXNbOfBL60*qQ$6oH?`m}=gKv~%OSDHo zOyu_H(CEmhl(_on_W=ZNoJzN>dPh&28*4@2`U=8b*j2Toq8ef~ReCVK` z-Bp!JRmq%1+OPZA1_ z>8t2CU2{*UX>82W^F@#ui_DWsrJvEx$S(IE!ra|&Y_^m^DTq4_hQgC9UP05|6=MhK zntRTIFph`bq(e0*zRe%k^PM0n0*W(nb)Ad}Ot|lY^Cfw3&fw#o}IrH%q`CCQCP%?7-?CF+Ow^10vC$XY42FJA_9!5yv!<%P~A0E z-L@T(&tk$eHqu(jMVvsl2brf(DBcb~7CJ~s5HTXS))`wT<3g7dY2l%P*kq!?LGwUo zu+`ZCTr{O4C0B;%2%eJ-O%@LDjEv;uq8GFb&6hH4QILYF* zOyap;sA+BehJv)E5*`UPTP|u7ccslJuM~j@jHWpC07FYeH0I3su($8b0Wh98k)NMr z*|*nR`;jrobM9u80s6$GB&TJ*+g*`ry=-hIDf0tmgH6}Xn>TSzM|bz4crv&^eGV@= z=DdFq>F6Y75#~}L&cDx>G<8~QBRz%aiI$}@fAI3q~gKY%+X8L zs0Jm>1Fo*f-R?n?DXwUOL~t+*kF`yqN>H=~nU`CCW*~PN+h6d6Kk|5|8%2p#B`TG4 zHj?x4^rPB_TU5nKgK>6BXvPKBk&*ef)R9wTDf2yucAI>29g5GD>%wK;=gKG3*C_Cy zN3EStpVm_7?hJ?e`uh5sn$?F{4(cHTK@b*8Wq|}0LFR@hDjTzv!H5`h8NDF@@#&4^ z?xoL3m29{-zx$+hc=+;wJg6YFWPG~-IyTOauw%@zt{7K28X(iIYJ#3+rSwNPVq>*RSJf3Ka}k;<A%Ff_8`^WCM&|sjH)fF%!xKTQcA-G$R?$Wr)7nIG8 zKU1&W>W*Yv)4U^SXN|%498|K!c@6bZN6ouF;X29X^5Qf{4JyuTu1R=uU0W51mA0Se zQ*698C!_88P<6F$tyfgpro_tO2C5-gXp$^zpGjA!5W($VAN%MTU${@`(M3#)swnnJ zcc5spqsKT~&5PfzUx!VIex32UmYuh93nI8G*J&Brc(NQyL3xeUL5VL0%eb0_hfj8w z&)pIBqZ^GU`b1z1nqC$5sY}J5g$hc6ExRtTI${bgLPr=o!^a&-nH1&{Z`9rDdKjyu zXV&dr+8%{O$=>*^C_x#P_qEw>phqr2%lcs z7n+ZvQ|$0B!d}=Z&U**=|F`fmKc#xlpW0c+O#m8=pU F_zNQ|_;COL literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_n.png b/interface/resources/meshes/keyboard/keyCap_n.png new file mode 100644 index 0000000000000000000000000000000000000000..950fb88e41b66abd8f22d6ac9beb1a036f6cd853 GIT binary patch literal 2230 zcmc&#ZA?>F7=CXl?FCD7*;x1()f?CZ-GaodQ;~&REFdh!f+ny#D8&pF9? zpZ9&v^St+*Gt%S)AMYS<0PsoND^3Mq0s3$>eH65(2>=2Wap{Wvd50B6biNEk*?Czq zGBKAvB1@Ig*&oXLWzhh5dZJi#p!n`zV<~d$UcXbn);D2?@GL@fE-;;UjPqt$AN~sG z1&_~8dj3EJl?4=cwt~KOxcb7-TXy>1WTW|XsdYhSGdgCTZBP|26|w*W3V;A$!@8FL zFP!ZjdAH$!xgi~+`1+N18RnYTnQ1j#mlad5YPpqba?UM`)G0FT$4NM_ZTau<>Di0V z&O7I#&ssMvov`KQWQ;B^+Nz9u>`#-Fo?=16(RXWm1r%tf^4m+F_$R2~k>Hr=_t_Im zqgyDz!@gET)tfQ4O8anjK9ctkSd@6wAw1HN&&RslZ2(<6dS@5sr5I^?JZq7u_`5!|-dU8^IeIFhQ(#lEo zb>k-?AgydwKWcq__>K0hEGYQY-fkg3A7_8#__Gm*k%`B}e!-ggt{n(*8i}DBxfZDL zg)50By)(M5M5vBrfpC{Hd?sg*n-&K;zKly(ZHNQiW*K(^sYrp2-^-ma1GC%9rB}!! z^Cq90r&AikG#TjFH3H3O!HgHphQ^da;){lUvH~b!dCc~^%)*;**yvvllw^Q!*zzi> z{#zt!fwae>I{_-7gadp-EwCXc^$GGsTKO@T_Mw2hq|Y_V6PwMxlZ7EyL@w?i)FU;Z)N}R7POO~7$e~SLaX)>8+=ewRBR(_YcEe-I#7S(@# z$OUuF>%0jG_o}-Ocl$Y7qhl_eEZvKtJy3nwJXNqYegFU< MF)mr$9DAtjAC2nT7XSbN literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_o.png b/interface/resources/meshes/keyboard/keyCap_o.png new file mode 100644 index 0000000000000000000000000000000000000000..2c1755c8877805917a20d32661f936d5988dcff2 GIT binary patch literal 3508 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 P000NOlgFOgps)M~_BGVb literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_p.png b/interface/resources/meshes/keyboard/keyCap_p.png new file mode 100644 index 0000000000000000000000000000000000000000..366a9ef3ff594dac68e666b81bfa347f8fac82d4 GIT binary patch literal 2282 zcmeHIYfO`87=6BvQc5TGLv&$4X;Gt5SVxqR2?k0F+KM8VmB0{6)vBO!krv8S_+$f$ zVi;bi=%ks>IbebUTDdqbZexfH2}1@7t8yt=UwPlzZecAqFoa6|}Rdt-MC>DZxuIKyb+nVuJ&-#$tRIhC=RxvWF@Y z45R@eI(b3CLj=LCsX1FIrVfA=w(RRWO(($a<-vxjj}I?>nrBr%RGPOo?Yyw7DJxuX zt3<68xry~uxoWa2fA`bRtmvK#T zz}dOH%juA*TNEx>3C_;N9Ywn1gSwR1GbC8DbM6u%4zFI34RzyRMqN}TH=h#e%}p?v zCHn+a)HDo>Hf$jPvC{m2=0t=$5FjJkL$1)o&j1G2MbrG@Jytl>(Ql_(|J+!QfmCFr z{3enB8SA;r*srCntRA;pi$ZR+#_oFiGfR-hPZ^%A3nai53!DlleDb?q-#al-YsB{x z?nQ*9oDZ8TU4|mKt>oy@gBsGK11?4>8T-q-Dry=Ixo$M-8q{bi2jUr{l;tJ}UK-pf zo|743fDX!1=`}-N9~QH9Kwz(jQfAjJy;AJpX8hMx#+j(xEQN)jr2luFBuOMxI&e-bzJq4>!A| zG4~r{C1lcTk^J{Q{WrRH!1$Fl$2{`4>$Q#TF7LBvPgj2xwH=|edQtSvRyPL!@1BKr l_TN>%$mOvxI&PTu#Ss`v40Xs2eAx^D!1eL-E@4Hc{Q>m=0S5p8 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_q.png b/interface/resources/meshes/keyboard/keyCap_q.png new file mode 100644 index 0000000000000000000000000000000000000000..2d58bc2b46cb5d2cfdbf58abc76e3763756e9d1f GIT binary patch literal 3681 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_s.png b/interface/resources/meshes/keyboard/keyCap_s.png new file mode 100644 index 0000000000000000000000000000000000000000..365a833af2441a593c95a3b45e1b4ed3dac1cf6b GIT binary patch literal 3405 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_v.png b/interface/resources/meshes/keyboard/keyCap_v.png new file mode 100644 index 0000000000000000000000000000000000000000..1dbe395005c5da1f53826fc94752310af14369e2 GIT binary patch literal 3024 zcmds3X;4#H7Cu?%1QV9D2+}A47X&8?ibEq?0*bg{L>v*t4j@an4M_l77LgYbC*8x0 zAPz=oBsf-!3L23Ta0Ff?0%Am{Mx@z5dj$0_Jn5PSeRJmsblU+RC9Cxmd_fUu z8PX^e3MWUtRz+SdWsR}3gER(M3`rVD4%j`q!fq(uDOhfT#|U9&FGXDm`F{`G7N}sC9kvikoHO0*9Srq|gO6Q4mKn zOkfxKR}aJnOw;S8?eS9chMt7mk*2oj$_7pb;DUG8WDAla&eoVb2tXd8){rOSzrLQ; z=*cehpWm4^z#$4sFG-SLIZ)oeZ7H1G=7608{)L*yJneM!#!AX2wGTM^t2){JFYp8P zsI!KK>n*m(OjcEtvJ_aif3;24gm}|_5 zf(DA1WFVH=nOb6fBTa{+NWR`3G4GSA_U&j9%B(B@W@~JTZGP579fzCp&=RuvJUzv3 zUa53)Q_EZFI01UTlqIt#q|Go-A;-7h5ZruGTkCzlu%^4aJ6Q6v>^L?Mo6|M5BEzR1 z?KK1~&10##VS}(z@ig8`FCL!n_9*7&MtuI|A1)&?B*4(rb?hBw8Zt+obdA5z)OuoI zBm<Xtl9#*235AVhK-I`6h8a39u#kZ$rRH1GHrga4&IBPv(`Cbt?B^r7pP6daf ziqG`V@F=wEm{W;~Z`sdvI7v5GM17M{e;hf@Sm$_fjNPh@(pFax1mb5I`lD}2lnkUq zTB>SWTeFLpct8UbTQ4f6><1BJNVs_FUf#QR zuj)6mrY{Fp_xzCzk!$kwM#^wwzc5vQHpIN3cUjE_$4A|!tMafC4FL}_gS%Rp8JieJE@6l&R=)lnj+eT=`sK({*YmDoT4YZI6f5RI%)b1!S#Jwu_pA6L=|< zIjY^P`6D?493uSVGZQ4bdh6F&nT?K!ClQvcEoCW`(IH+`j0WGQ^$&-U=}MW-&8;xo zt5n6M0ge!{hUT+x0&~Nbwe@3U_$wnhw>ruS?U}gbsGs%aau)P58yNfBSspo9iF!|C z9=Yy)P*9Kwd$6m!yGm&p`Y^~8^%Gg#2z;dL3I z!SFAkCAM5?krT=H)aM3Gn+Z6CRJ@raVh;KS5(Qq*E#kV zi(r*mJT!}{#U$SgNX)z$9^6Mx$2Gs1HY5y$s{KzMTeY?0Jl!%; zp^ThytCeB=@)O38aJ4V6KN&ELbD4>hw9(7?%4?sTbzVG<`!zM-i&4RNcj( zRMg!lHwsJC9Y_I4uNc@a)bI#cUlA(PK?0{3&E)||x)_^!ziw%`>(>!KB^V7U$FC(2 zoP~(O5b6TdjEJdLX8T1GBDgrJBd9u|Z`MdVOCDgZ8Q5yrpKjgr^t;)}pBZ?^ZMAzI zOTu-E?iYpR%e%Pcx|i`~_>~;<4_mL|UPY|bo(I-Xzgp(Is?*Kyk)KE1X&db;tx6f? zJ4ZQL&^M1m!48eP(LWOhm+pXq1DXv4U9JTuG zcxbsKA)a*2CgOq6V8%P9$)Qi+_#0oN2--7|j&>2K5bw- ze;fD0?3`caERFBM<#JzFou2EE+@EE}h0~Vz?#;pDjRA-NIA8%RX1W;6N1=>?j{E&x q)xUkp|6BO~VTr~V5;$>|&b literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_w.png b/interface/resources/meshes/keyboard/keyCap_w.png new file mode 100644 index 0000000000000000000000000000000000000000..a71de5124d9c153c076ceaa22550a6617a37b514 GIT binary patch literal 4149 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 Ypx$t3IWFmu2LJ#BQG)&JHpQ_14`e)Sg#Z8m literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/keyCap_y.png b/interface/resources/meshes/keyboard/keyCap_y.png new file mode 100644 index 0000000000000000000000000000000000000000..ed68e21384a58128e8b737f998abf650aa2b6bb1 GIT binary patch literal 2701 zcmeHJZB)}`6u+B)Sw%`f z2qZWHa|((nBB2Zhlv5XOqy!iWsB0q@o5afod+Cghed!yVPG9=)oO{o6p5J}${oQ+> z-}7m3kgtWgjX3~V`1^V90KkK3p(c}V<8=uDmZ$jaObJPdNlA@Nj)pB!3HzeS{)Z!D zqjy9{MlqA_L~jN#clP(*@_yPw<)cBmU8wD?L3a^e5y4$pF;58;%sgUU@H*)wpzNeb z7kYT=AQ+bkhv)5a~v?~RB4&3WKdUJfOw0RN6I-&bRF8Q!F zpW7bk@#ITwj37LnCtbtCsXgO%HTC!PrF^92@dj=P1mpIth_Lf3iCngm$?~E#>v%zY zYNk%T(6qfaw@(++S8IVwO-(&zM1fUdt6!SyqaoLqC0r1T#R0^#vg9E#6$O!MYHAa; z=SVENMr`~EbNMVZH1zQ@6dYDjA0LTA*KiH0{uZv~j;)nW2_Mp+BB-wSkZWnSWdVDr z`6aQ@_2r`c+k5ahq1CrserN{6K+$p_G5IdyQXbT9rtRB~AC^5 z!O>R4Y0X*FUNpn!vWBB$A_Uy``tdtfkr{?!Ws$5!W!R6$Y@9gvf3*r0kM?(x7K3he z3!O&sqG?yP|MKvCI{lQ<@T3F=R6a@0IhbzO-tomajD5SoyQrR-$^Y1rXJ;D{*K1fn z;A4tPU3=F#N$r?y^Q$GGC{5NZ+>R7wZ`g=D6SFBTE;!-M(TvCr3*6u++hy6s?)ZMa z2muvwb-ToGg$rIK(j=ji$KBT>+0=q^MhQeX)O1ycc+oN^ueko~F|{9$ zm1jHj4X?y+^2o;sSXEc~3}qftjDQuo@g#+uw6NikL@l*6kVqow-PhxU#I|+kimE0a zJqpvf?PW0Ju}1yt*3gqqXxZsl;QL);qyP0+o=viN9Whm%w|R?jnBa|KaTL17R~?0- zD(xB7LBI{9$VAes*;FO@jI&X!c~Zke!PaSWZnE0S%E=ht*#Fjs)&@*bs3tm^Dcd@n z{jcr5)K;^HLjZ%uK04XDV_puXzW>zq>P!j8vK!2M;?dg)0RxrB5~z5SPG24Wn;-{M zpUWF;!rOx;kCT=ho++B?R%}vlY#;>lla)ae$A9-gvc*Q%b2D+a=3vj$SnB%aZ#OxF zp5|@92rxluv)^dpq}Led8yXV&3rLcC&VeAksQv(_DzCCkUGbfQY7YnYx*{y5(UF}t zLDgLlB0y-Bf<#8op*PcS(i6ORYdHk4u^xFTeFgq5wV;Z<+}l>a9w&708|g8~t-jctRn8de zY%Io}CZ?ySGnq`am3n-*``H=Q;Njs`1oG7{8(NjRNc~aMgKzT5oe%c}Hu5eJ1M}Ql4eM_$|HO(50Yoa%d zxx|+p2vW~GwY4kV_RE*rgQBuQJiVbomuecbm0f+!E;GmZ?hVwMq33u6?5aA?f0_|l z7#bda5(}Z()^)_V5%Y%i?<$T7oDM#JxDSs7)l{!-qzfg!t>k(|~FM`Ok$ zX)%m>dB$~*A52_sZ>pXq`CRl=E|I$K_BEgq^TWDaG*6 zBV~YsmFGG!SWyu_l(Dv4GGrCmiNF{vnzVsKz%8~x_Z{J#mv)|9=O6FA&-1?T=kt7? z&-0#oCwBYtr7M>L!1AcKX}bXI0bN*hJGwHW9soRYBNKAt53+LeQ*)RQo_;WmNr+;n zW-xa#Q`0%wx0qxAtR;#TzPsR_e&FZq%X?h;sTX_gcW?Xk*wqdpvf zQpdSFB9SPj$259)qraWq^ZlF-UZ)wF&oE@x8&2v&TP`ZKBXN|XLVJEuNKNb!UY25` zcZ*Bspdk^5f>$*5ny2w|N=1Jvb(Qr|YRwcZ(0; zyU0q6_(1s%s^P=By4Hpri4wKjB_PxaGhOoMq(k`o`y5ROSk7|$;#h{#IKX#rDa%Xt z^-r@UU1X(P6HsJmLpmfhApA1UaQ)$-1A*yW%oxA4gKEggOZFX~#fY&W>uF*Svnf^E z!`1>mcF>3s>rZ-sCCgL(*?LswT>*1YIaydOJQq2yy4TwCig?X*v>yu$noP>6HXh4R z3e+tF^kE;2nD)C1zAYFRF;hIdoa+o&*7-gzio>$bYs1_vF4Z2em0od0LbQjrc={a@ zZlT=`-&qUf#8oMnj`q`flJh7Q$>sl=1eF`Kx_PsO~Hr_PA!NmIYGy9s*(^*2-Q!~GDpIV z>iqWczL1FIE4)GfE}ec2$6fo->+Gr=hmuvg2G3XUa73%oNW;Mnr~r5X1fCWy+y;+b zi0EM7xPF}+78c@Yn45fy#IMP5!2TEZ>5NaDE*kD>|5)M^#Ls`a(BaF0Ba64-*~5Fj jmYzBL{}pb!mv4uG?vkT|L)3sq0D!2-SX#rjl;eK`%4um; literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_0.png b/interface/resources/meshes/keyboard/key_0.png new file mode 100644 index 0000000000000000000000000000000000000000..ff852cda9dbc378f423851fc44298a9595a6d1d6 GIT binary patch literal 3132 zcmc(gYfuvC8pmG{)AiEP)I6EHaLP?nldQZU36|PwNv7p}H>E^t3C#=P70k+XQ_fj4 zDpM;fn#>F@*;y}OUOH;X8k&)UXcsd?F_eW6*r}QQaK7e;op;`Oo-e<5{_}fgp0_9j zx5w1T#s~ml8u)`>C;(9K`5JyM**3fD008UK{Ug)Ek_qXj;!_iV@6lv@0wVB4d}2aq zLj2LplYI$j0E|U}e!d4OuV#hP0~?~&({L!W*1_ZW&v+>63XM#3jFwWDu1)^_jtK z&CzwL9MWc*6}Wf;RY`6Y=5n=T%iW>ZIW=4akp0YHFj&&+X;ogKx!(Esm*4t*E714b z(sM1QSvuDlxAn@t9Jfda=jm0Ajv|6v>B*%CAY&1zrdSZy;(|mv<#oHAvo2T+K44r5 z30d#)A@Xn~&zHHF@p5wt6D<=6eV*ETW)A zME50|G!xHr($<>(S?J9Vb8~aQ2}!{Q>{u5e$`V5JK%r2MIdb+;Qd3o~cG*j-QYs(k zg)4CtghrK2Cfav|(IXNTaka!j@*T!x2^!8Q{O$w4YQt63H3I64XRn)Y!w38<% zYrBm=-tK2lSgOOrv1pqT{DwR((!1AbEv3`94etzHzG-Lm2C;R-YHaZAGcBu~9bJca zUFeVU)DpK{ab^tcB1kF53jsM5kr1q)LRX1P^r>c_aHK~d-;nuhA(0SH@?{)zrjc4d zx>xXTx6gi+aVn%hNwZP~E)B+4ut$Q+|CeLtvmoo8qAc)#o~ zSK2L*gVcR^GjN>9inR0TO4{KlV`h0*;?VI^WzPuPC!EXi)@J2p)2hx+->IDTSQlgX zp;jR-is5N_!3r$zx|A?ep*u@=+%(tCEID(F?PdlE#W0b9fV1Zb_ zNd1&frw^=4oH}-AwH^Y5JbXU*U~}CL1c-!OWW{TEwgn@fgGTHBi|F_vW*$YbL``rK{ z-k8b=HUr1+2iT7?$9YfPV1^(E`PQYvEgJIpiUSn*JIb|2r0F7n?Oc=mKKxZ%n}DcL zy88l3;hIyWj7QcE3*@R&2m;93yVbJ{a>d) z4(Qh|a0y{ge!A{FpjcYv3n)As4j1~JPX(8}^GOe$_0+>q|I^~);+wGRD7;07ki=F^ zatZRu$@o)U1M+3D81Z&pcx|~q2nVv$muer@ZD%r>oBzSiepJKNb?@b@nXs`Oa0QO`xtMHX5|T0SWX}kx#X?Ue z9rK9DkJkqn%dn&MqkCgv+bp5IRL}*O+=$S&ETYyM(5yfk`MK@t!Wmv;dy~?%jB+UmTLYmBQ4Fs%L{|i^(Ek0BXFu2HGFtB zSq^ss6!iv#=3}jO2CDKhqlL}Oqf+6>re7q572J2no7Vss5N&wWuh!%R8(^w=7RciT zLYDFQdy<{oQy)Ub8yyu%DPFef9wShEW#6vZ}1Fn{gIkJ3nc(IO?M7f%?%}p~w*iKKzR(C$+?g>#B?o4r}(@ zuq}mzYm8Zz)^{3#pyr?I)f%k} zY3-s7myqUD-4ea!Ff1H|Ji43{Q9IJpEus1Mq{ zbVMHUnG{@o$4$<97W_u%`W=OJhuIol79741CpBz2V(1SPM zNg1p3YmsaF`U00nztI|k;pbWHr6%JO6Ukb|RJB`m4?#OQ8PDXi+UWquR(lzmtnedU zG42Be5P${13;-DTs`l^2VJi#2VjMtLpze$DuQ4xfS^k~_Vs4-LBe!s>9{>OX{c(N| IcEz3k6MG5?0ssI2 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_1.png b/interface/resources/meshes/keyboard/key_1.png new file mode 100644 index 0000000000000000000000000000000000000000..7115e92be8b2bdd84f739daefd0f55ed8623ff6d GIT binary patch literal 1909 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|UFbr;B4q#jQ7YFXldS5OKJeI+aO-QR~1u28~881JM8` zXI2MRGg$}rcM9(qra$1DBlvrA|I{!g5AJ!z|JJUFTT-6xVeR7GW@&Op_?oE`UfjZ7&aVc(`9L^}THK z=f{(C5|TZE_rEW5Q2J!^-;<$icPz(22}z#y4IW3+Hvc^Njh(UK`{mNRSzA}FI;HWz zdOoAUpSSyC^ydCO$lI{xN~!H!zubBOhD$UOSgH6_^B8!-ua_#eye!H3-7L+(X(`wL`|rK>y$TE}C9n3!=oNn5 zo|`SDz@Va-_h;2AzvYvI?Kv1c=X{^NY~|};ReOK+$0{%^QEJ(6|NU9Ud8hpu896|i zvw?v@fq?X)P!Kuo=+*a6`9UuG zSRupzZ~pxG+w4I+`}{x2%sotZ7(6n*-p|{d%_GA0OAfp0%v<$H$X*nfcdUf4xYf=68juyxRfk%-h@6z5f5> z>SA`eIp?46E8nBAzJd6}RAE;y&{n=FLg&xshmn6gJsMmpSy>Q4jTGv{Gbz^YM!6BN zv_h6i6ndb6GTSNFu7HtcNc9q0jw4q4|8J@X7!2DsT@_%<6=h&xVDNPHb6Mw<&;$U< Cyhs-S literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_123.png b/interface/resources/meshes/keyboard/key_123.png new file mode 100644 index 0000000000000000000000000000000000000000..07a3187a70ec9b39052d4d5d7445c9c8c75a7613 GIT binary patch literal 4399 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 Fe*p@B`1k+- literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_2.png b/interface/resources/meshes/keyboard/key_2.png new file mode 100644 index 0000000000000000000000000000000000000000..99a7e650f1431b60189211951446818a086a3b61 GIT binary patch literal 2864 zcmc&#X;hO}8h#Vmj37&)ATC5uJ4ZrUMyV5)MA=7CVXqF7!1fBV1S{3k%A)0FbK*b|(h1GCqw8WhBt9(&2bWR4^UyafKE} zKS!s9B*pa5?E!F4Je-evB|e`M3uB(U12R7O3Rk2u4TphfT&t<3Coy?Ud_?%c>UteF}j*Kp{od6G@u^|V6O~K!m zj%-c**6H{ySof3Tn|A*Fg}slP%(O*1V1I4_AM^|x8KvlV&M*%5+N>)^`B7aH(sEYC z7|IUxtE4WwtYkZNd2h7d-0z}0&6o=^nZBA_X)IygKYaY2;NlTnON^H;xO*8*o9x@S z&%}iLjw#aCw$|8X#Ob=;)>`Ze_~IB=gP3Y(A9tBQS~EiEXjRk2LW}272|JLaAnkQk z=KN)CEhP+tK{&C*eA~Rf|{GNh~sT_;n)C!*c4)T@UM*t2?@D9li9na<1Mwmgcr=k zpnCu0kR#ixRh?%A)9;!Mt*M;Q9W|)cO0(Q1!rf)Xyk>;(f}=D1VZRv`!ggFpFzOlz zC$E?%6`;#&38Ye%W1(9kR#+Eh6KfWM5o$)YSpVx$@}8Idy?vn{yw2RID5j|b>NP*L z{)S_5yH^}(__}$mCnOl%hd~Y)FOWuUhE7vt(q!8_mmUv_em@=yhMqmF)#shzGTuaG z<9M#aV=WAFAngy#?p98za!sP2oFz@1t1^R}H2Z!UlQ$0wl_=|@LQ>$$c17`i(5b<=P*T5BYl7&1L|}woCh|9x4i46(6Zy`TzxI%;x4Z%JcM(@=Q&ObZIR6{Nk`L~s301~Gd{ zam)9No~k+AqZrBBJCtwy6oFc5*ZGaDpaRVDcd5+*Jj*dDi@wNqty1mq9F4He>NJ(u zrEt}68p}6cjA&kg&d1vgFpG3WYX7m=DQ`Cvx+$$*4=jpG$EvW%~`BSTX%Bvoacp%eNs=Qle+ur&tE4?UOH*&=o2-0cfqzmPz$;BXZM2-Q zA-)1G-k6X+n_-lsdaZRvDKiC!L+bsBHD&EbLC-+?9F?X!;grEUGMumQ{nRlkNM0;4 ze>P=g2Oi3}!=E&6!;&AJQXYpo3WvkliPZoqAQ1ig*~A$$Je2ht1nB^%fH{x>HV|U| zMkbrYBM_Y>+J^h_bJEE(6y)5ICW_Ey9y;~M@yd_|2VR;=9pxbcDgc=G)J?$6aDLSL zQ&pAL{lA?4V&zxEU%l~XfBx@~TcndWy#Fm7)Y)3~P1BLUwUY=2t{hU{x|wmT3;@8x M<+Ss+CxSlv0qVJa4gdfE literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_3.png b/interface/resources/meshes/keyboard/key_3.png new file mode 100644 index 0000000000000000000000000000000000000000..5ec80db3e01555227ff2b9b95886f333a519f00d GIT binary patch literal 3341 zcmc&$X;hQf7Ci|OkVXk;5CTfnYA8~W;enzO6qGp#QbiU5B0(MmMM@bI$fsxl!&FlW z$YiA{wLZ#IOd+L^I0V8&zzG9J#)RoXKm`mjA?d=k+P>fLJNa1rJ=nPE)ZO50Kh2WU|_wetEoPKjIMpD`1>{g_4&~k5=X&&=SOr3XsSF=LgH&a_%I{*z%>N~3e1Aqc&030j}{(tGX zl^uTNbj%8@`{?-co&Pgof5v4ht&r~gciA*}tJ$j?Xd-DMd)i#mJlyvZKpahbx}_zi~$aY5E|vhz(A*hMY_u$eALP#BJ2uJt}ui>7%2O(EnlPL#VKH!PwrJ*?y1 z6Ln*8)gxhK-9*MiEa+#S;|7?IQ0M3|ND6QUO`v4c;;8|0%-988yG{8g2$zA^ovRv1 z9J^f9PQ4R2=eGsoE*r|cSpMm5>ZYI?`_hio>k;352@!r(PX&r`)6rQD6IG1j=^efO z1i6ikTyG%k_2B>wt#h5TnXq`xGYzncOFG`b9`WD6E-e?>M zt_E2CG+;H%rQdAV5=vZQbXK0=Snj?3R%;k1xaD4p)>Yu?5poaZ2rBz~xm>RLkq^hO zyg2oThW{i~KhIvB>&yJtxwVM5@!HJFo}~Uzx{*`;r!LBXsk(ZR^q$HmN-UUZg+9hj;DcU+EcUZ6V$IJ)rqJ+XFz{ z5gt+55oziWMt{A1p0(kXG|}8k8OlqG)rQpL8ZF|tT2<S-LZpJun zuzP>o@t2hhNeNHFM%_79QMJ|fyGuMQ_%nyge>)vlQKF*_KUm2Bv3F@JiT{J!3k?2E zBR~VkuAIVDCY#IjGl0j5wJ*JFi$t@dZMQ%H+1J;1b?E)HhzRD$jUlT>HOv3Ap>cXe zV>b^A&t62?={G-k_%Nd*3!FRe1=UD|>RlQ+;H)~NmgN6Het6(3A{1Qjh&)m&Wag{b z8%66-NU9-Lv(S_3R7pxLUu0Uu`rdsU9`{Dwr0N-=r>Ezg^-sB<&ON)v4+A#Q>>z#| zH_#%SPVRHFSE-($1r{nb>EEZSNE0o$@T9m~s&z%|wS9GSHv~e<5CArSz6!vZPFijA$dTGB1W2(q#IOZ@{Deavru*^gTqu4>x_wVf!u-(5Tv(!c1-;17$YZ**jLzBbUg(n~vPS(K)u4G{qtqRr0AE}8Gw9&Za7{NZ@c zJx>f8OOb-42@VHFTR{3WBesn^tIGo9+@{2_E%bglXaI}5tY7%OqpQ?$5G3J~2bd)m?Yk%&JzGga5>xAhYG69q5 z#y-`;Tk&&ph}rs(kPzA-47`);jw)oIlBX+;ONMbclg&f#IyV_+gLBBXU{UW8vEI`2 z8qa5yX)k>m#tL87{Whh_fC7?8`fErniSEt-o`VckU}gPb#%*XQu7_sdrJP2yX4D7O*pef6^gGI*l9rO~#u!qWx}|r%Mg+bOj{Vdf?c|1myt<&8 z{*Cl1R9owahMQXA@OFE!4*`SRuIoEvoZG_fb1{B?$&(fT%qv4xmYPa=z<1pb4UX0y z=zE)6n^Wv=h@=(a-(TsG`7RLu?XVWL8RA|qToCY!K0dkv=c$_N!i&iR{Hna_Nf1S&a1cc&xKQNgHW- z)2ZJd&RU#~W)nii3f(-O>*vF{28@HLUtYzRiu;6<>=TYosE33LZS?z7*fY(5De549}HQ!OpYX2-g` zK1^aWU6#z!nvf~RLPuv^9n14dxdNL^L{PSPQ(fI)GFuib&j23D_(_!Ay9?$G!IoW- zwpw(-u1rL5)6nr0bWGeKE=hQ-$$UtuX4VeuzDb=US(%`>OpeaVwc7We_LTfAl70~2 zz}+WDRdY1PrX=FMgM|UZI`mwJu6!7W+kMz7HnqaO1V76rld@|{#?$2 zuCA{22hoTd3{LXLO}7%uNbw>1DgK{L<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|Wa9PZ!6Kid%2)Ud){nA=2>hs}}16K8;490(N8BHzfuk z2X-*MXn1*`gh4fcN%aC__om-MnNuU~w4eR>cVD`)rMl4nAB&9ly!ri)o#E%t%Nz_W z3=E764h#$e3}i6}R=WUYj;CBZ3k5+34sr5B9u{FRnoN@@)b4;tHa}+VlVtpJWRuSA zZMDzTSFh6QI&?&U;Rn~T!aZ^8=l^+_IqTIfGZUsl7MB0HvDa_EP2=NWc#?X1+vCmu z)%oik7#_UNn3WbOR_wr_pkI66e!f4Lb)5bGo!_Ax9-tPEq{mY#}!Mbdp0K?q5L~v;0W2&47G{A8yMu+N%*85vrSSXr2jVOs>$#a6`sSTq4pO)2 z@j-nK2Fp#KS(zM`_3hrjxL$x^)8+4fE&86X%e{S#L6CtXbKdjI?*B_;?dAB5T^JPR z<=$R*{q@f5?nOU-{4j84VQ})g`P1s%KX>Lc0*nn6L1p`Pty}&27LOo9hkEMs*F`(; zye|jqn=*I(CY|;~kd{ZDcYYRq>1S{V(X@^~p8kK;Dwy7Xzs|vQ{&;Y|bgrZlgMj~| zbJq=c`u+(raA>AJfBoal`xo*o4HJUO_PzVQ8{}Y+3yQw*Gl7lU_l}pPVa31757ob3 z)JwE&(&-gsX{gBCetXwCke9v{?VO>=!SK;WuK%)i{Jk$sOb#KRZT9cqKY#bNi}oB0 zfooq^RXu-g;5!)^KL8j#oeL?#$l%l9i=l#ijCnGJl)F z4xA2h$0DzrKR+!KhcZg@6&PHm?)d!FS6_jlWy+4vPgUQ8L_R(EUbp}EE^|zG`&Hch zeplp&?4Eu5=Gpz>T#_3d@6)@V;Q_nloxhGm6mqcQ?8l>hl8pO1GiUv~AD<98ZCR#Y z{JqlH(9r#VHg3}S8?Vgpp?TLj=l}nX-?jf`-+0G@iHV$i^q_c;h}^$W5!X}Ecl%GD zoe&ZCt=62ifya_O5&iA-?x`va685O8N#i&3{rDD_)AMl}l zb0VVod~|emteKn8ZhU&Sv|K@8=qVD$KR88AE0re^`s6G_56GX2!2oFRRoj0Sr_HVS zx#Q$HSohiSuXp}S!@lTOo-{`s`~S0%Z%inkpV}#loSGgF`A=_;Vlz_@g*t9aaArfn zy@-enh8*!v)#si?X{M%b+qO+grh&KDj-r*#< zYDVE#6g+YoD5YYtI1DKnro+vCEuJ*&Weg3R!vwv?I&3YAd+_OIUEJadiXyx+#F79A zEnMn@9aQV0^c^7}!7-pzHk%1KE~4iIg!UeS;`20s0{y>DoBE@G(qCDzNH6|PqeCi} z>Q8|}lX1-wE**p2bs~Jh0-%BSCh$NWv;KCr?7w(aV@ePVY|(P_#^Qa8uNyD>xWTnh zXnp$J_L@Vj@-3L<@5P-X3P)Cj-!?nDvw3*Z;dOECVVgvqZ3H6NJ1(g%zI)f|q5R`V zrhP73mS}bPY@0)uRn$5nuYGuS>Tl6C6otWZY1qqAW{QCazuj_(-6jxj)m~Dez98GR|TlS(2??1ZbLFY*F(uNau_m5D(rijnyHz!S=>)Erl^k(PC?T$tr zC+PFG>LtC`in!A?dcSMb?)C!tk9QD?D1G=XqoFsT$cNiHnByUq)M|ldW*pLYrB;i! zOXH3v4R#l_=ZGy{1z8PGo@6dXq3%Rt;)S8^?-no6rUNg^8nkKOEk13kYW;b5AI3Ve zHAja;sMfWHn~gHOS{Ava?!!hD(A^KiD@Vo=G6ZE!TMHQ!7gicV2Jd4e${ioGwP#Gs z(}YHL?6z5zcdJ)s%G2|cCv#*K0_qKY zrU~Pj znhNx+@bK_3A`uEr1{a0Di>(|;DTh&JqPz+IDf_&y2C>v0!a|(isQIv@-iKEoD0tD2$gcl zpl_)Z;JPxQ9o}#m-^^`^x~dY$*CUN$Q<{5G=SV_iq@boI$AizWl33ySm_?7or{>|q z7kSwT{c%2P>n`>!x@2U+bA$5mo5bH%Wh4F*_|x6XZlKAh2ADQRlHhVDwM})mM2vk; z!1OMtABR>Hc8+XuFrfr5lBMZyR95<#=Q{*L{K8apeid#>S$m+;P9w!^gMg%*@2cFGetj zBb8mr#cvZ=gy5#;M<7db${x3hZ874wxUia`k6bPdvoU(2=I{Lc8@OaJ;&+V>;PTP6 znwi;_O50g}^A+@3!N7Rjp9+cASnp6e(4VXvnwgo|X+WqQ8p{`uA;{nYuXE&hj5awR z=i3!&5iyI^ZW&dE{u8dit0#q3c9}dUoLB!*_XGV8S)|Rx$(m<}3}Pf9nWvXRN5rva!XAigM^*-MYF!lQZlI9%e=$!FQ$X5n{FW49T$~ZVj9MHL2@Xv)&fpR~F YK$iZ&p3I@_5dZ+U9Ujhio&3-I4|zN9)Bpeg literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_6.png b/interface/resources/meshes/keyboard/key_6.png new file mode 100644 index 0000000000000000000000000000000000000000..bf4e81a7a1171dfa395e69f9b845ef44c11ea632 GIT binary patch literal 3631 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_7.png b/interface/resources/meshes/keyboard/key_7.png new file mode 100644 index 0000000000000000000000000000000000000000..5d9765b37e87c4926961c38bc4d514643e8eab52 GIT binary patch literal 1921 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 CDBSe` literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_9.png b/interface/resources/meshes/keyboard/key_9.png new file mode 100644 index 0000000000000000000000000000000000000000..89a6397c8210e9b324bd21f6bb13e2482927de97 GIT binary patch literal 3617 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_a.png b/interface/resources/meshes/keyboard/key_a.png new file mode 100644 index 0000000000000000000000000000000000000000..74d57d5bd4b1bb2b884a76011c21cb117ad612bd GIT binary patch literal 3066 zcmd^>Sy0ni7RFCvz_5s*En+~4(qgNa78*jJfdoY`vIK1rM8%cG7(tP+1(GOJW@Hfy zw3PycmKLnBfu^I-VaEc3Mr2SB0tRH86cQ335FnELr|c>o=LH}7VIFRM_vLr)t?%AC z=eyGJ$6!TM5SWMj6=umLK5OppTPrZCL zJoHHD*$W9*UxvB>801ks+>gfh$s|LU`{?G}A=MLP!pGmvy$y3+uW&ua8lThn5<|5H zYithw!i_9F{QA3ojS2sF{Dhus-$Lvit!7)yB{}Q3S-Do7?(ek>eA>Ml1_}@c8c;w1 zFb2TDinf0*j`_OFe>D!Mudwbf$5-zB@`Ekw3M+jjZv4N$O<}5=7$d6_Gb&G}k!xoP zo(GpMg!9EdS^!`*HS5gNZQwut zi{*NbT@HgdecHs}Mr2k0c+7AC(E$wswzv$rTz*a-HB|QqQx_7o*Q|NC@nHs0gIbp3ujGf0>!uR{iu!qm|6=Lo1>18jEwYfB6N?zl(nsTH|w=@dj9~!lSdn3{I8}kY(oezwalUn8;=Chguh}=zfj1wqlfz87Kp|9kYt>8#+wgbx zdy-E7IAA~EEKN&7w2~ayB?_P%J@tD2{!Nxl(IHj#iU=bIy=xh@Ol{(#t_k#hP&DYZ z$?LAMSSLQYt0E|GyblXUD#|loMPvV&7+?Q`N7lEUDSSSEvQ`l-NUwUM90@78QGw>7 zO;KXof>K}PH^-DFe8=vt;jq{s6y>q6b#}f>;`Uf%t&tH?J5i?dLLX-Ox>KsoncR_< zN~~}wE^Z>m8O?=8)-$mp)d5i4`7J=hQlPMP=H#p6HD0+}feUy5Sw4-3V#(&EbxbZUJRYb76sxbzEFrLRsbO z$X?^}Y}Yl#oe>^-&iQ_(y5^fuT>7LU)3azg{$y)_N?g?RN3K(NrWwD<1bXWpys0Rt ze|}<;_MQ=of!Nv}4x26Cx!WIm|N0U0o}L~ak5^0%LQU1jE;X<R z!`@ouFP}@y_ml3S&1x1Ka!OrYTzst8dmF2Ih28Yf-Tpf()(%8gDKoY9zRC~Hh?DNf zH0jWt9S5hW1Og$6e>CG79KAYE?l>Jq%pI&xHaFremT*+`z`#J|Vr@5lk!}p%bw=2~ z;+IP%V-*U8WY`kJRSN_H@q$8?Sl8cwA}&f7RmlE>U!JRx$bvF1W6_j7V}gm9E2L+N zwP?yIR~HwCYGaM0Y($HPuHIJii046W zb)X!N9@rnV3m=P(eOQk#XESA-SaESR&G#V zGFL1)&=35w~?j#p0Is#c%UAMPY%l-v==h_6vyiH(9a;WUg)(^EM8{O;*(gh76R&5!Hhv zv3W*o*=^tJFtsI=my+5}NuROz<#!@9+OIF)Zzt$Ks{dekyE@p$)X~w=d{Fd5SaMar ztE+1}xyh^Oa8j1zz0;Sf!qTX> zrPXI3vzOv;yVrI~I|}%5w5;Z66bjDF=dD`l^jsdg%eulWWemA1MVC)p))gkQuf+dP egV4eb#9%Mj<;D5XNV5q50MrA19%cK^-1slYRmbE2 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_abc.png b/interface/resources/meshes/keyboard/key_abc.png new file mode 100644 index 0000000000000000000000000000000000000000..5b7f1bcb0fac2bbec87755d94c1c430b2a6cde6c GIT binary patch literal 5346 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_b.png b/interface/resources/meshes/keyboard/key_b.png new file mode 100644 index 0000000000000000000000000000000000000000..50b607cbd3f6e5e6bec11845cca09a68200da6ec GIT binary patch literal 2839 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 KT-G@yGywpG#*C~0 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_c.png b/interface/resources/meshes/keyboard/key_c.png new file mode 100644 index 0000000000000000000000000000000000000000..da8a4a4f2d531742844622ed80c5de4646949f6f GIT binary patch literal 2950 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 Jm$nEi{{oXY0UQ7T literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_close_paren.png b/interface/resources/meshes/keyboard/key_close_paren.png new file mode 100644 index 0000000000000000000000000000000000000000..6a93b0fe05348fdf8344a6162784517cdaea991d GIT binary patch literal 2621 zcmdT`ZA?>F7=G{N0xgJEtFR%$MUmha7Ly@k0(SxyBH&O_6qOdK1huG$2rJMQu^>3w zxCDzhi63E;34Tn`QKeYL&LQGt+CstN5VgXf3IY{-yIh@#Ci^k<$Fd(M=gm3qbIx;~ zJmD9W`+Hw5{Lmf*mHkHxbymJbA8oN`ii5&8bHJ~5@ zzyW9!tM?B-1oPHb9|@;@0OPlI|7bX%A0gc5M?Y-tQO+p*BP(!;Xv{j2b>=hb7#ktSij<~ zbj3Nb9B}Rx)@;Wj?dtyBTvU`7$u?FbCv0*+sMIdMs9>V^vTHDSLRf6{#2@d>tQjK7 zdo~HEC?X$%#)|1MZwlH@ug5?FEJWlE5x}7V6FgX0o}RAlANFN1!Q)2JWe$ojTQ|mr z2_`#B$Pezogkx2emD-)CXP~15h&EO8uG&d^foOYDTb$j?b72Pq3iN>mUNkV(DilR? zdgHYOrA8K)1_;i+A9q>ZKq*&n~GcY(P@jC4g7D@$+2?=0udqoLv{A^plU`@QYlY*!df<|+CH@TIC01h(dN-+b%;zpdr z6d_;$U;qdZ(Amta+dr9{9zC)>WBlHW7Pv^0&|gn+?yBO49h8AK)SE-(GV9|4y~d%S z)Jxrh672Pt1WCgTN&u&T4l9o9MO)&UWtbh0`jt2HR<#ikAUkHv?w-0uK z?D!UaU4!Gj_I7Om2B`<39ogX9j(M{GwwZ9A9O?~0G&uIqV3<*392&aY=MEec5YaIg zsal&~UwFhg#tQ|yeZ1v(Pg|cm$c8XR++!Qc5Sw(qS!OjR0z$RMf*?5i3hM%Cg%ZVW zHQ&rhGutAfvs1?AYZtPG7~Bptj}uyT_VwMgnF_KFgK$;JI#>jFOtwB>NOI4uZhd7a zegBnBWfiBzH1J4vQ5y$>T7FY{*$E1O*q5$Yn^C1x-jL7D^|7>A8WP5jn)16-GyT8o zb@zm@=Yv7%gUZxVCnk9C{ms$fDF$i~-aQ)5Fs;bH-CR3|x0xI&T()1YHnl`Mbe7RJ!L*3<5Q<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|OhEr;B4q#jQ7Y4JWcFinv~UyH>D)MMZ&eiXtz!$H9a5 z3k{sNTs>>~(?9OQ_O2!CJAN=eu-UK1Ai%)D!oa}D;J`o@b6~YQQ092bwF^)XbdV4y zJLF*z2BXO|i9+oRj0BRvgZjNb2RN@q6`xx>ImpZFWC;huo!h@(-;cU&C7FCB%J+D4 zB@+`lspx@q9oKomqH}w{-xc2dF(=$5g7JaFyatL=GtLC@?m;S(--c_miu2}92@3LB z=FITm-o5z$*KS=qDY5ukmX+KYVSxq)1_cHN4p8PLo_YUca7EvQ;&*%B&%4o^zFX-K z7X$kz78cSCLa~%$?W5%ax%mm64Jpjn|Bpq2;k~KmZT~0xDj66U7(8A5T-G@y GGywn=Zk|K{ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_comma.png b/interface/resources/meshes/keyboard/key_comma.png new file mode 100644 index 0000000000000000000000000000000000000000..69da507ec60038d15f5183d3bff4c588e0f70135 GIT binary patch literal 1785 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|VP^PZ!6Kid%1PAIv>uAkh%GLP8?|>DDiB5-|^&s zlLeY~Dl!BtD3tlEFT=+kyeQ}ObAQF)1!k8`w&u>RT$nlQ_~XQY8;lO6_W%FeC@?WL z)W0m+`6RyJpEIuvW8mVudE&`2Rj>ZATQg_`dueX|wSLZj1%`lCe_s7Bv6}0{$=Ja3 zG{3=_qxSx6AtnaaQ}-FH|1Xr&Yg~D*Y`3qbuI=8q^|9Ay3Y9Sg%#uh>`^v8v@mG*R zBlsWFfuHmdKI;Vst04nX{djJ3c literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_d.png b/interface/resources/meshes/keyboard/key_d.png new file mode 100644 index 0000000000000000000000000000000000000000..557f3816f070ce467eca46e65e5bf8ce64f86204 GIT binary patch literal 2962 zcmc&$Sx}Q{7QKHGFb09N+W>6lk^G5TgyUCAKI64KWI%AVd^HR3HI?1Og;obQRU}IE4@W@YOjl=exJgt-9x) z@%H>)pI|}&0DbpuEe@hSQ|HgZRU|-w20#SB!JO!ya#YmEOhrYq;o;cYpa?Bzg862Um1Tb0zd7YQ z=*eE7j>?m~eY>95Ox5yA#2*9Zx3;!A7GxNa;4atyW_!G2fk-CU+M(r3{YNj0BVBY8 zYlEhG&d1qzVMBtSyiQ6^mN3W`*^Tquv|2xN^1@4>S=Gb*uya83*h--kGN%>og{rd` zU2;O7X8*>WO`JE$8jjvKH;YJ(cK(6B_Xza^sx=O@DP5S}cHBsFsJ+B>RvHtr*MR48Z zDvFJ544WO%_vp&Ry$*({PIR35ErYBlP;-wQ*<`dp^5rUT{G;Xbc*nJ41}-j<<$>c> zKVwyJUHpk#zLJ?gMx|0kbiQ4k%*s9MU5t@n;iJ<4uzR6UZ7%S1 zKW3zVx@!C?5iF06inx;%;>ZQMCwx>JKKm%YM%Wum+su!U^TZ{3Yo;X?} zwp7HfB!YpfhHVuT6yz^>V_{)Yw;2a42kf@2`Y;C)4E&VOMM55V62Tx|x5sAz9_BSI z033=9PazXQ>7!Y7xf{_%dT7``R;5rAqhp3cu~GsB4;*ieT_Xy@pe8|%DINygO2kAU z0*|2?EL1!%~ZU>%cmx1I8s(Wka7JS^^cQ^9noAU+1r|F*KZnA<`RPt(X`vXj=7JoCm(Gb*Py7ZG~#c)XL0 z2uji4k_+qht~+NkooOAK4npqeL_qX=%G# z7{8pk+`<(u|L86(yQ1VAq!bCHww~Kfs=DQ`%7U9U2g?{4afyl1Yge+K`O@09rHO4; z=dh!@Ivpt#jY_>}_)2DGrk9u3j&q+C&kb}>G-=X|ap>;m)(%6NHUM6);Gx%f_I4s| z>LV2H@}Bm_wr(o6;;)~aj}azs%le>H{vxzE{;wC&4~lozt;d1EK}I^Ih0a$85OCV#c$04 zLE}=ppX@Im*?dQ^Rki-ZAuHlanRyJ}DXf6+4Hli5QTcjV1RXy~CC7k&5={ zty{L#C{>N&w?gM(KpmpPkZ- zpSHWxwsf)t!Gm9^IP!q8rP9N;ykq!izW%hLlQFP;jCYM>*z7doUK*9iW>@T8M^!qe z7^yZFv`VGntpdfqvRB!SANueO0)ZRa~eHjrf-O_4y>BRq-KaN-1eRrjFI1R003}z^>n%OUC8m@0g|(h APyhe` literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_dollar.png b/interface/resources/meshes/keyboard/key_dollar.png new file mode 100644 index 0000000000000000000000000000000000000000..0105debb68db7fd3bd1c3d209956013ed76c3b52 GIT binary patch literal 3481 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_e.png b/interface/resources/meshes/keyboard/key_e.png new file mode 100644 index 0000000000000000000000000000000000000000..1b356d9d5b4e2e207f7027410a16925164a01876 GIT binary patch literal 3029 zcmc&$Sx}Q%8ohrA5)ergP{55vK@o(g7&alu;($m55hKbfF~}AI$P$pmwrqq5Xlr*s zgy7aGjch7Si2`AhfeZwsK-oG1VptQ95Wpm4imvI0d6=m#dFZ-z>$@-K)>r4d_neb? z((Bj;H9a)|zy|k!xcC471FIXcT2l3#O8@}639k5rQ>2RtNg;6r;1os*CFr|HhY$%q zgpjZlax1|efb~i4E>6D5{nJCkr$@$ja6)Q`b-3r)=fTCW6UtcSqkpV$Aa{mlP4us`c}-n2$I>VH3*;;fZ&)g}9^hj~1~9I|^ttdJM> z)6KY(3)SX70?v-yC3J5~v138|2vJ5ZVH{I3T*qRuh(scV(-OZVlSS>Dtv6$ETf^e0 zR7IO7HK4*cywbn@;=%O(1_bc!NVc!7sWB%HDhR5!g&ob!u4Wohv2b9D79%qn^Q5Gi z?M6xr2IBw?a7^^R5dcKnUf{)&r&BM7@H7Z#6Uk(>j{5XQI5=FoCo69uC?Mdpz^G?x zp`c*vmh}dp+zCA$+?8vDVL51kXQAA(6cwM3)nx}(9Lxlr348iQ1{+UGerYGuh z27`eXmq{D&YGSF}=fEvOUuZ?FH|pn+Z9v;d~;u?Ynner<9}v^vKhjLx68 z$JuPwBKw+up)^0UmE)~Ni!$g&TPYiC2OHb zbSkj6ogL|i(V{GJNhH!t3>>uOZ>jPxG2iKH+RK z&*fUOx0aWZK8U@wgeolysuO;`V6xv1P8<5xXk4teDWjn;>adQwgxobM>_h-R;J%MG zL9daPrBf&`ruqyrXqH;SNC_5LYQbQD256U&2;l2G5})hzAJKkBipQ0zKs1km)(z9x zRT($s)T2H+EW#dcs!dz|>FZZBt^{qc7x^Mna*20016Mtrh`5oJ#}j0` zjSgAxUQA0DMTd>>P&svSfzswAd${M_Xhk~$#tDq5^xtc-QzPRy0|ZFjJ4g5Cj>Q$w zX*~nYI6Do9=DV;ZlTIr+LfGx5h^b1Ai+dk_=$pKjd+Vc}7wnWd+uLdkBc{}Jg}=H! zK~EC={Fc4{HYwK~pnLjkNyT~i~RFR+zw`>H6MB=JCRL>lQfjWYv z&aR@4Y-n+KI3&I*gKpL4Z?g3v#3o!VC^7`I_wrUSdf82P*aeZC>PHG3)6~x-lb`bA z3u{%+v8FvKDJl7Zx9xc*cidkSJapwqiR8B)1c<`1y4{(^Ey+VMRZ@BS-Ve$rSlxKW zuE2|WA@L5h$ymgVERVMbn;d&0Ds@tfOhu{7aj$P?J+?45ytKogrVZYf1eP%_TM=)5 zVy~B6SgGxe&|fFF=Z#)}vdyEvq4{aBGN+|0riyzcXZ0J(5&yEtJ#d26jd&Mzn7(K} zXGcJPTcjMZ(}HOH({0*LW#&-jvdt8^&=~HmWz>#p{kydZ+fCa1>H#ltzG0jf7^{tvMmR8&-VC(|ysaz6GnzU@HkNpZ z^R7fgCEM*$A+G9-rs9f?Bjg9twz+zr4+sbd3L+`!Ftj-6{CP3uNJ|Krj0rbh=NcqN*7=DO$v)L>!L4D zb@s$xi9QDh5;u78%E&+!iMum!S;`jaHWgFT(#}(dt&uRmX&F9n@^8*4P`T<_O?HhQ yy+-)|vG~6j9oHBV*BsOSVw?ZP6RlFVmjlM;&TMpcRVxPo0C!g}m(rubKl~e4s-)xq literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_enter.png b/interface/resources/meshes/keyboard/key_enter.png new file mode 100644 index 0000000000000000000000000000000000000000..cf935fe07e68d6d899b9ce200a13d0ddf56fb590 GIT binary patch literal 2201 zcmeHIe@v8h82`NQ9d|Dn-AmaV=jOe$g<)-8LrOH`y*y`7N+TIZirhJ0qXD}^Kw_QT zcSehxt+s5oMuwF-+j*QLCW4GNq$ygNweA{6a+m`3`s2zj;pFbx>!t(7ZJqRIe?0qs z_TBS&p3nF5-Ja*|7uT&xPSPd;NX~iAm=Dm9gOQKdwX2Q*NUdINs(#73wYt`_O@LvG zb+f?bR9IdS@&(J5y2?J`X@FUgV>GOPt^dj|zIXb~bKg8)X`~$LcEu=a&AIxA_{3^H z@nFJY_hDsjvqJqu{@h1)HP}<$YxR^Zy#Q@FtU*N;k|Gs zwVEZJxd+9Qy3d*pdKT8*780lPyUoK&aZX#sic2T=o)vQYQg8KjxT+HT!u&nLs%K8T z`q8J4HXYSjDQe0q`7JE&HQp$!iHHl$?8&H!W3W7e0fVTy7` zWGyk^G$Ao7-FeD1rISQYg$jlQ)hr|p2anGGRK}t)XD~1Q+mRdL`s=4fzQ|kS3ZzS# ziT(1A5A4@jsiGK0k>Ififyn{N9qBr-r)eHbY*I<^y*K(< zV9n5}a?$(isou2EL?n=&vQZZaw$$w#JTzG5pcpT*7rnsVj2aI8c(}q>GWfv9`5oO@ zeD;T`zuWEez8^~quQh0~`)e=K>hAw=wB}6LWA%kf4qC(t2HN<8s7mF6%@jc?QGsrz zwD>od1ac-VQ~t}i+n$j%ak8enol0lAoDkYx&RW7?NUJX{1m)jxvp@6(*rSw3rzDNZ z?g$5CISHyMZnMIf%^+pIIcz}V&yX}U->Wh?SS)-}oQd+*xy&j$l*t|V()749+wO8o z%L~Dr-oVE_Y^Ke?S*`@y|Nl;#=IEVW5csY>Ne{!0jE0Il#|HqAvwEG;^{jd4KSw<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|Ohgr;B4q#jQ7Y5B4%E3b;BZ`!cjRaI`q2wXn_HYJ6kr z#G1@i?`nTaz4VM(=5mC);rAY21_cHN4v=LH3}i6}R(k_wj;CC^0tG<_330MR9u{FR znoN^WwNE&l%yj8?wfy~W_p;ZTsVo%|3{&_S9(U^F+oX;|dIF3<3-c zEDQ{c3=RxLG8^)3)fDzxKL41TeYPPbROt{GgE;X%v47fb@MQaUyM1r>UXRN4@T%iD z%o<;*d3<~JTEjM8&y&Ies9{KS03DE*6P|Ey&gUQ7wx&63(h^K$ZQ!nCVj|gls6j!o zc43MN1j_RiCB+dWCMe8P6l&){%Tq+B2Us$K(Ih(Lz<)a?9tQWfO6#x9|7FF%z`)?? L>gTe~DWM4fmm{&g literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_exit.png b/interface/resources/meshes/keyboard/key_exit.png new file mode 100644 index 0000000000000000000000000000000000000000..4c06660d569be01afdaf2bb290eb1525c39f5ff5 GIT binary patch literal 2466 zcmd5;eQXnD7=PYg?dtTTn=_7LvP&>VO>~lM;3lI7cG+g)n2f{*m3FwYu45@5(+MQp ziw1Ef>P%!yOr~eZ#JP@^NLC5V7RMUR5?3v@i`~`%%NQK6sX0gv?`qz+!Qf}K{X_n^ z+*PIIgY)!Rw9e@tVWhA%V<|hXLU~yZet*zd(xvjlnyAxJ7 zdNw(^n%0IU=Q?LYwmS?;@G>jxv(=ga!He0g^7mG16+WuC;#I&Q4E3OE2GDUBhc<^LOwvjd-f zIF|)=X~r{V{zk(vgpys(W6Ak%KEhX54Yo(i7A^*sPABBxM&IybUa9=;gUp3pYH|1I zxgTAYwb2=VD>I#xl6zeJ-wqB}h+!pg?Q{hoM_jQq^KZ$*cc8~Zk`En-*%=c|JtwfA z9M#Mi7N(RIzE_Te9$$;unTH301`bXHG;>|ES_)`-NRH#DBp)(jIjBwFTPlW?7SK6V zVGZN<1`S4t*_rKeJEI55hmJ`;q~qV*cHjPn$1i?gB8HU}m+Q}*6m+}_Ug=X5ON}bK zf9eMjnH_2#l<A%6+! zS@jmuEba*D5%~~p!on~PLR6n1Jelh=E4#I9CB$zo& zG}d{X$dA85IH%2fomtS#uG&GK!kfs82nnAftZED3n}L*N48WNr}QBu%c1U| zfV~1`4a;NnmHuOJs&J3rjKQIn@{9?*L4yl)SdIhBgI_a?h>mm#ppepn4IJ16mJloX z(BB7O)FPD8QNkiJv#WKf7~KGcQzLUw?G$_4<3r{56$^ zlskwT?_T0iZzE$!=7>yqipUM|%+=)~%-=Ww`OTLoCBg iH_pHe!{JbLKQM4ya~B+PKS2J*pr*3cdU%cF)n5UT`Lr_t literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_f.png b/interface/resources/meshes/keyboard/key_f.png new file mode 100644 index 0000000000000000000000000000000000000000..93306c60359d04f8e1c8a07dcc5167bbb0da082b GIT binary patch literal 2112 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|WabPZ!6Kid%2)KFqxpCDD+$;*))jKsvKgV`9?*mI<6P zY7IUDhK_SM53x0JKJpQ;alCQBmMcs2<+SSJKQArFr*@*)nd4Xiz*!&te&{?w+N z%{O(XpT1u|>G{v2M~{B{eo;NGmEpj}==ImTk0x!6YAv;2^Dn+8Z2k4_$2)GnpD=Q6a<}f-; zRhxYB;r%NQ?U@)vy)S?HFxy^NoMDA|w1df^r%#_&?X~;!d*A;3zadH?$AA;H@3hkyR8ocH{7eA!&a8TL#J zQ*T~>WiQu1S%uU4zrFmedlorcqiXMEbFCC$$U5hm;CH9MLg!|~hAH;8wzY+(=Qrip z&0n5rCBbuO^BH-@hFAYOx8GI`|Ns2?b9=cT|GVGrn_$e4bxurR`I2pV8NaXJ%cIH6<~;gtCdqB0EMBq%_5X6hUx< z;KUXz2m*s_hJ*s5q(~7lgb)azM$1lE14-sEb3EhAJQUyN;Xn7jeE0kAJ^#7)%W!jb zQc}=X001TDFAuo`AYuKX*TV&Ehx-74jXnHL?AMXOu?ha?gTOv8G9U=&e9k{4$UVqE zFe&c$l~H$?pSmc5U$|+mR;%<2CQ3H!bz$w)L*`_V!}IpqorW0TL(xH~{pz zu)m&e1oHvaN5at?Q2YV=N5cX65u^LJkd06NeIM`R1}l9e9QVJ!4N*@rVklq?Jeuhy z50F|y(t-8#LVps8l*w$K+mYo+?+c5wF7&O`B;y5o!{VzF8*w$hQB+d0i-w#&Jtz|| zjPv>Y_@<*=*WcyEl9c?QN}9%P8jZQ&)5xDX-!OkaI<^84HSk8X(%#HgDT)D3_3sU>Ade zE0MPP9d8YkO5aQd$VbJ+saKh)6G0FJ#_Ef+0 zC2nccc5oX_jX$@U`@`4#Uv}c)k=g>Gt+}a*EgC$S4#b?tW3z~|aF1yt!dHM0@K4_N zC^3J~sK{~_*+N*!rL9bU#8MI};n*%D)SA+|+4z;-P>X8g@mOoK`3@R+rQ}xE&Y^j! zbf__sWTcOF$ZDHSY~t@*)L0F!Ns5V4qs#Ut;1nzhU5_xjhUNTAw{V~|$F_T`ZqnG7 zgzu#b$&$_Jm1Wq1F`q%L z&ZZIS=@$ZWx=@&rHkZt(m}9Mtj(+-DlbwS89t)quEYpI#+Dn3dwc?|Y6j*a)4p%19 z46V9?dlRTYylOy5x%1pJHHom7AnK|rN5l=ce1=SgsH@L8F-5K=c8(Zu zJx1^Oa_s<%WOs~I%JM!}$D4P;rU4OAW<5MKq)fR5p z9W$(rgiS`TTBg!kif=L)jg52Hwu9SsW43A?W^j2`5h3w0_K^`&HKX6CKc83Cfl|?+_r67RgQb*UJ4Lfaw#?Zi}q%uj+Rvf zQ|$`J*T=`l*VkiB=UWW8AQjX)rZqJ+b5c3wI*t5@vEl4!LZ^S>@gc^4wnwe%SX@#E zpC3`z`_3YvNcJXJI6GT)Q#~cXAfNrSi8g}b*KVZSA(&n!xIyWb5=!aVGG15tqB0Ih zhjO2OCb2^Nyk{oh02QPec(E)UiuRqD|F+fnp zLTgVBH(%WQY9wcKrZmT3eNs*}Ot87(@~f@hCr`a9InP!NA&S&G^5Et@Vh#7yM7?k` zg3{Q(t&9D-nm3tm(O2KlVD%Ea3j@npl4~Pw0#m&+H@bqy7m{uDZDxf0)mA6gy%5|4 zXO>JN*@mmgw3g_fN>Z3NV)*3UyNP!#cu4g6^%?nsr1xQmkLrD&S6hE1+8sZ+yv z-*;GebZ(v;L3BZ1iTfNkbnv3a^Xa9F4KJuXqE%Lp%&V;Lq_IXHLC<&~rvD3v?f@ER zymnf1Ki~t`?*#Qs9WUeCS?~5Lve;&5aBw-rOpYSwBr8Z}3qnJc zM-ct#zirf3B&VMnZb|Pc zEmb?Z8p2u5R{^8*^W@#^Y7@Wa&xXRVO=g9zYUIg<9~Y8+B!54P0as$CH2rpPQW55m zoOPkAZUQrZ~ zy&t*Z`9U{&3{aqCDo%r$7VW6P9fT|?8g!4kAT@q$-|cIY6-{RN>PS=of2xwRx;V)@ zlXbUs#B`DSIROoK!z^!^6&1B9+^i~NzkVJ@xUE~4>YlJFw9jAizJDmEG@xKRaz89r zdX%Yuor-U+;Nflsag!PP*&UAL-=X*vZ_=_`^e#5JyyS~!UGW%eSR8?d6NRZOBz>$A zsqf+NWfhrNZ0$&g4tO!>B$$|J1?Jk zbMoX76gsz3q`PBzMI@9yz9YOwlLeJWPLQN)4Gp44VHRkB>7RH+1PTBFL;wuHM$(O7 zKA=LeoHlCO-g~|G@vr;#|9A6A0dO-UZ!v}6o)D_!jMiQO05~6ZJyh|<=}Z3tP+HC5 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_h.png b/interface/resources/meshes/keyboard/key_h.png new file mode 100644 index 0000000000000000000000000000000000000000..c73f37c271fad09d1dfd91c54c42528baf87b75f GIT binary patch literal 2186 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|SSSr;B4q#jQ7Y?*=|fkZ8DAdc68_<3{E;3M(B}9Ef0y za$LZ)foUt#My7>K&a6R9vMKu`)AkhHo!ZL6WcmD1_tgDAZ&c3z)2N;r`Hx{iZCw-- z10#b21A_nq0}BIL%z@R;LYdm|9|@Q>BoAzVMx?MX%u?-nX0Sk8{_p&G zO1_uhe%EqfSYXtAFu{EG^Tcok6F24zyKu(BKVia^Ml;`h@;q$ZHa~J&Wc?e)1NWBI z&st_Z@1HzxfgJ;5L#^L`Tfc{K>%Tu;e$s*A!Hnhg8zOX$J>K}8iLs%~_y5%2zkmPI zvtnXwIREb5JE@{SwO@ZSyyswI*!|&0baCAI6r(~l2F8Y4x?~H#Qr5`Te(U`|aLEFHTGQEWiA)V8=gsj^ESPw=T$(VOrEX`_KCI z>+|#TBd7h%{Z)TQ=g0Bff8IBi78q(UG5i*E`r&wu~q%EaHWC@>VP zI3&Oy{a0G1mH+07EPDqA0frk}bxt4pa{ovFw)U6*|NPnW_jCaV2V+C5|Nr*(c3pWD z&q;aPcRy`D^Sn5por$5{Zh6C3ZX>i^C7aDu$f8yWQ?v4xz_MdFlKQ8dCICE+-6BEPK&c{3U<=-uo zVqk1=%l~&hN?f~#!NJ-5|7M-l??0YDe_q#!iNW{E`|r9;91OWz{ys1Lf7X(Lg+Xf7 zyW}tHn4HKc2q`K&VdYQM<10cJObTV5SH64dc-QaLrY&3yx=*N?-H>t~(aZ*hh|%hS r++r108pCKZ(+Qc{f85h&WMc?yv=V4+Yq4TrU|{fc^>bP0l+XkKroGA8 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_hashtag.png b/interface/resources/meshes/keyboard/key_hashtag.png new file mode 100644 index 0000000000000000000000000000000000000000..df673653b0416114f905965f1ade51d11897a0b3 GIT binary patch literal 2394 zcmc&!Yfw{X5Iy&jU_h{e8LXJpMihU5j}(hMhCmc8;se8@z)Tf`5~+ZM$kPGJMG@bb)2#v61}`=0^en6fM~B~-pCB|Unp42xsr8)SmOEzz;E z5LtB0HpLa055O4?T)H?cqqFbs4MnGrua3T{!8Mj*PL+qcEQaky2#M+#@*CS}xpwDs zAI)m?{lj^0h*fmcfY-^#tFHTX(9+6do8wUHcZpAefEeHcER5^;|H4@lYktCT5EG!z z%=q}39~ErWjx*E0;XU~xPuAR9zo%Hw%%UIYYCg!HnO0&n8c%#TFh9#%D|KJJuR`r4 z^7Z|MEv@e0PuX?CS)Y>=<=h*gch>LB^46;72@+^JvhsGFimq1hv%NWp3DawRV&@D+ z*gtm5g*dOMsOXMitFERl;@n68%IjUJ^P$OoE>`KKVhFYq;Gl+fTyvGAAWpS`7)k8n zC)xy15U*>qf&EKp9YCVPLelffYYwUnwS)j&6^3PxLz90z_yZG?x`i$aDS|y%czP-s zJwy@*Vd-qiCP+<15J$-@Aqyu6s(@!`_5c@=`EiqArKBKkZrs+{>@&26N}%n~>Y69% z`DM6aMFMQV#ff-R_cRDFh0sU=l5(XAc*)%mqkO(B@hT4jBpGyc^DIh?$b2`Ie&fQM zsfP);XC`rK>+^QG4i9U;O}a5SG!!l^sdJn$BfwF)`4FFdHeRLcH)gi{cGI)4<=zcN zIaJhZ^1QyR)W!3<{$m-s{zY*mXCK*zcO)zdR@z(^juZssI$?0vzg zr|jrQ797O*xF2xD(K93oyAATI#}6N{WmqX>gfl+O+M6l&T@c)pX(>RlV&jpiSWoz) z+`EY|Qzd`G5pc0zkSCEsSZU8g1US%M7?H2A#VX1Ut;%b1v@0_#`6N=;5l8QqD(FcB zRX@Ay&;o-)RntJWch~o~K3-i&{@l>iG{+}qo)@e?(y6Ztwyu5Z{Z80DO-EJ=-$D>>6DvtxZ;sF z?Of|LU{N0T!0`O}bFbcr!j_GFuJvuF$qS0fU!MBZG&LhOhlQtKW&34zZmVl|Vg6hm zrFT|e7C!r&i&ir!N2=Pcw_Fln(9c8vHN%xg@&M9l`88XZ9SKWe7pKMhra9=COfJds zzXAkR*8S3h8SAT;fUn4trL7n+^{4St&}i|9GLJ6-Vt@12sky6NFQ5o8d7^ r{<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V=CPZ!6Kid%2)9^`B>5NNwN_b5|Plj@R|lC(_E^kmM{ z&z}9bXFFZ~;o$~}nLimH)b3Mb5MW?nVPIfna9|*dIk4ItD04jJ+65>GI!K6<9rCaU zgVAJ~1k>JdicdJ;UR3t)ocDXTra60co!P>4bpEG~x0{R48Mg6io)i^mU|>*SVBi3y zOd^?o&iX%GdA@)BL1~aC1qRII)}a1L)!^&et=rCjyBD2pwq(hNsWx9JwHC#RL^>5k z50VcX7-qo3h{7O-YnOX%!1y%w`qtWSHvY^(p()Em8RAZm6TT>p9)unxtz>~EMR+)o lrk#Otq?Sf?yICa|3>y_Lb9_0?!oa}5;OXk;vd$@?2>=FFoU{M{ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_j.png b/interface/resources/meshes/keyboard/key_j.png new file mode 100644 index 0000000000000000000000000000000000000000..f723de1f55df97011773af238e7fe85d00f387bc GIT binary patch literal 2070 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|WaGPZ!6Kid%2)KFpo%z|e4U*(-*g2RRMu9P<>;v2>~z z%wRBbym8c#5?%Fj5e7;1DN2h^Sfy7eBeo|z^!d>%ip}|HB6aQBg%4ldxXxcWiQKf0!30IrJU7H>M9IyVq-L5 zA<^F$VT3!_4n*AAX3C+!P~)c9a__jBXA*-T!;g~9^;4FuG<0Ejpp(1(wqkoUqd}s0 z>~%@jWvg;;pFVwB@jSzy#spS|^vtbMyfgo9)-i75VCY$|b9&XPe_7wwzpgqVY0Hps z>kiW+n-+m*pDX{Q?u@asvpbiz+5XS-imUUV|Ga$p@`1_k%B;H?SQy;I(*O7Wu6+0I z-MaPblX>dq{(8LU|A!9-HSg;usjOeW-c63N!7wybbp4C^-~O4i_T^vcoc!;A?|k{A z$8yZ1oBJ6KWbm~+@A+f@Z+YeS-+R*;d}g;Fn6s zh^tNaO?hnwi;GJg<~{%a=eEKoo!dEP-+n7IFdbOg&3Gek`~N4!iKm|y?TxEG%gw>? zd`a%@UF+u0pI^P_g`w}|+qZ99{NDYFo$nw^b)2C1W@p=3E#-AS-%~-v9^_SjC zc@~C-`EQt*$jq!TZ7`b5qFaE$nfT0$k%+;0p6I+!6YUMemOR*Pr(C-Nu|?pA`L*H+ a4ENTmO$eFAah!pHfx*+&&t;ucLK6VsNvXB~ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_k.png b/interface/resources/meshes/keyboard/key_k.png new file mode 100644 index 0000000000000000000000000000000000000000..aa3e806a8d135bf1b79703fde235ea1d067f9b96 GIT binary patch literal 2389 zcmc(feN0nV7{=e*b=i$fq@dv=OiO8CP9jQFn23~*A{Z%?iAutD!4=$=p+#Y^fl&FX zfZ7g(3D}h_C=7;d5MeNFMZ^}IA_E}^6R0KbQniH@TP*EeZk@(te~o6@A2;{hbDrP( zKIb_%r|3P-j^)c-mI1)>i15&801A)~O1?5(LYe@;IhDOX_5H(1sp$zv5+R6t_&_2( zB01q;Vsv5x_luO?#BBhmJ|Z+|PsY7RzYnA+_pan8oEuGlVEguQ+ml=Q%idhIzX9r5 zM_#{keEfJ!?#>!&95Qf~>Fpe4Z(D8fn$VvabuJSddD+H`rx$r!#bGcmnQ%q;0CWJ9 zw6m1qQkYj-S&0M1D)ZLkd@G`R9>bE8f0^Ra4(=uP0$=RQ_M=E!r5d69K^o@&?$JVR zxnK}itu(Y1fA>e{lg{Q3nn$W{-Eh9PFAhPhwEa)~>an%OO1DWI|6Y?RKLISC#=8yb zPKD=CT7Z}%W>TPXPt2tWjl1N>vBwoR&zFS)y(~fJ8|ko+KV3!-v}cR0L9>_qwCB5D%2}TRdHNl=ijgEc4^sshc4Z z6jasbu7YMf_0uUEgwH+HqMe_eonq1;e2Xd12K!pDJz!1*E`1sJfNJu}Ap3ls1@p{+>I50@DFAxP%c&ttq7_aP2WtvU*_$&?j_4@2pG7YI46! zsdoC?C@MsAa%?a+k$dUoD#^;FxN*CmNzgIT)`Saz)q-Oy+1ii~F)Tn=1S}7UMADTuQPSSt zK6xKdSgfnMP!vTBtf}t4?SSwzhxAu`&F;XfYa2`E6*y6$rC1hhONHpIuD0F%$Zdqw zY>Lz-2h%}RUP7nBlc5c!yv80ni0&FIkFtQ?s8Am(TtoTc4R;Xz_Tb`)5+K=fp7C)8`q zNAEibfYpcR>>iot`gA-iYaeTR0i0T&Yw7dZGwj!-))ud3GnNtMN`bX?M9-I3PRCI< zM}8FGgz&Qlb5slz;bUR36~xooZzkCyvV^oiE|;&9zLP=Inazht;Tff4(AeYQOr;%! z_mV#J>oRCVX<||x-Py|)>94ziC|b+-SQU_hf@;-e)Fqoz-_W;_0iviyM*Sx)nxYV( zpRbOi#;ynh`uR!rohcrVj9}+%NsTF6LvpniH%5}aKpgmqcal7F89LK>+tPV$HqBC= zJ58GQUnN6ljQ1@oxQAnqC6Aq5B{g5w1=ApW#=|kFyB{I{k1XZit9xflYI+V#F-{Nz zD|TE>e>yiex44)!?~4b{YqqQwj<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|UFLr;B4q#jQ7YFXr9~kZ8EL>=Z*9(+9SD491PNOg;?~ zjl8E=W^g7jpJCh3IFrGcaj#N-?iGzKr9HLhlp7;{E&cI%>-rO?-c#5?%Fj5e7;1DN2QBhRH7 zKXzhR5PV1}!D!~5-I^MEB{IP!i z=4=kehCD9yzxMHuxf%SA>nd1j`(9S#FSn36Bg5DbCeiEWEBEgEY^MuF`rB`x6?w7h zU_!w4(&YBtF?xX_lGb4iAHGWM<4zH5U~q^|iBw}yVEA&e#47jGyfZILjEY$pZY9~x zH!st%cC6HsW4c^e8hgC(&W*(h^#Ku#4$&@wTjJL1GC453SZs5C`Q=RGg<;+fmW7O|k#XyFVdQ&MBb@0R0&yz5oCK literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_m.png b/interface/resources/meshes/keyboard/key_m.png new file mode 100644 index 0000000000000000000000000000000000000000..2e83b7b214666b41f24db1021b1ea570d9ece81a GIT binary patch literal 2438 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|Tdqr;B4q#jQ7YANp!#iZmp?ezmHWrpLbSvZyhx={f)T@SQhqHbO;lGaR2?ud%z&@ZkB+ z)3FQ+3=A9~*EBGY#T;1e4U{>aa_tHf1RW&A$qso~gu!SsO`=dc0~78f@Z`nz-3?+t ze$V~?@BZ1dXY;n-uDtpo;r#jYn{Tf9GwW)C0>hG!yj}0>!nb}Ex6iPed++Yuqx`+~ zIzM7J7IZJNu&{XXwd&*hUs)z{{iko*@m^ZR*4+PWg2$Q-T>S?g7VP-ZUr|-HYu~;; z-JO4w?ekKMW~%)8n!K&MySu_||Cx}De}Dh|_xSPRc41xthHJ_U8-@M-{U_U7_wnfL z&wVVGl&wBTtL|)mso<(Yp&55g(%`X6jUCG45eX$UTzRtA4#V_y2Y_olhnE zYLf2V$Vp}1!{@;8LokW^_$lwY9rXv->VT$u2&V$Y|y@mBWs{|hFh zo6UY|5Y6+fu!-S<+02|9=HcPt@8gBvynp}P;OdvD?%YfctB-s;|4QZg*|TSF+eFT7 zsEt<>V7PYg=FP~w{NkS8_BBcX8iRl6E#mWHh%aXL?*JbB_DF3@dTU0ND?|9EkoVT6z9W>d2n3=`VC zl{VbsKBTiFvHkGF%a<=JCHtsNKDKVvj#US%_Rc#lefl$3!=2LIcQ0ma`BB=u*m+}? z^bd=jqI}PqAA(|KQ(0A2Rek;cyQ_~K&*}?}ex2~QuD-R|lD~)HLecf>*Tu!fr_P_p z-+uTP8@D7M8Rxf4%#|=>F_KTU;g`bYRHn{59>(vDb!k zPTh|dajng^x*8qg$PkhIUVqUf(f3y!85jf@7+4q>7#SQ$U<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V=JPZ!6Kid%1P9OP|q;Ba*`{N}o%<=^TAVeb!1-uZCO zKkVHx(~{xF`AjATMg|841_1^J76!7I1FM~dGRIS_oq>^ppaX|E`5_OBFc?jyNfc@q zATQY<*-BChYKRU!YQ2M%iJ^gkL4kpR1C%=mGB<2I%~BIyp1w@>!OR(w+6*}-xH%|F z^c1HX2L>PFosZprMEjm5+8c-sd+fGTu3dqsn8#^5<=Q#$8csyQM0MK-e(wfGhC@Gi W<;|Dv&tPC+VDNPHb6Mw<&;$VfXLQd1 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_n.png b/interface/resources/meshes/keyboard/key_n.png new file mode 100644 index 0000000000000000000000000000000000000000..be1a6a9e7a52b5b517fee281f23fd19d6fe9d257 GIT binary patch literal 2165 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|SS+r;B4q#jQ7YF9tr!5ovh%b^cy+)gxR-xTbKma82Nv z!X>0Fpyi-Ff$sv>6|OBUB@=9wjjgT!S+DV8n*Xd)Z%+Y>k-YJ9UQdJS(%AnD3#$KZ zW?^7rXkcJaU|`??xrlToIeL(+qEI^r87@Y$onq|_OcVqi*dgSHJj@?3noN@@)UJR# z**J97MKM)8ei|AY+TY(_|F=1A{q*ORf3}2iFqmYXeG;p$Rx~Yje)Z9#Nguy_5m{ed zUjF?b`@A`GWaQ=f*_aI`j*y&DG~?&*8LaekI|cMIMIH~a{1!_&z?Oi-dh>m zbTOl(tnAs_$J~W{4SKuw?dv-xBO~LZHu>!G|5{OxKU&P&JTaY_u_4T@{>)#AYw}iC zKL6v9k(Kpb9{jq8!NDoXYQ5Y4)2F>x>x<0SdK9W-@v>NG#{2cFR?RrkeKcwIeB(#Y zYwWiF;9z)DbiCd|#*KN~pIuT8zhsymS!KSxaar(s^g5iQFH4)lqoP=So?B+(f3ocn;0BkpW5V;{%if4KOQ0+4EN*T zWUE``Ca<2u>AoP=r&Ek>UKO4Hf6LkR3mX|OSZd0rb>@Dq zeq;scCE~2oo@Szii#f>Vvk+xn6N`agt1|+W|xxN z|KIQLN^FhLo9=t}bs&QP!;QupH~a7Yys`5Ck>tn1&dS?s%SAXC8*;m2f83aOKc(aT zy?beq+a9K={5EA}Vc7TfOMmPS-s9!-o8(*`VwgMo zpMBhVZYdtNFj2;aD_^Vju3o)5ZT{ykUrHvo?>1y$Vp!X~_v_jI-~Z!-?{hLRHf$4d zy?CN^cK(yfdlexL3=2*g-o3|p+iVvT6GQLYf7`!5VQOG-*tPq=@89Qb`)}R9zx@OU z!;*~8>$4jf4otuDZRsyQMn#4RD{Rh3*VctIDw0v$k(s?<+934YkC%le%(6M(y=X(_ zg>8Fxty|0Fz;4OH0#ikL`AdyLM1f%%g98JD00RRH0|O&j%z@R;z(|qfDc9~mLC`@$ hoXn8__jWo1Gs8q@CF|#@%RtQu22WQ%mvv4FO#n-)*EawF literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_o.png b/interface/resources/meshes/keyboard/key_o.png new file mode 100644 index 0000000000000000000000000000000000000000..883feec8715fc0621edb9d9150f2f51643195407 GIT binary patch literal 3054 zcmeHJYc$l07T+^xXmY$WLSC_SDsJe3$jne)d8O2$bFNojx$;UVGiGp4Xol&Wipulk zNDqw8Tt$OXIF7_5hh#92Ms770w@AtU|s3|mX6uj z@EfKBY6H^!Tk-WP|8-$sah;tuNLT#NXH#?qSu*5{tLy4A2{~;Om+FuS$*a))x7&l) z9N4_28CxhC)=#SDa2Pg}s?&8-A+$T`YMMGaPYPFpJ}rG9;PIlr&1E-zNHWXi4GlFF z);v}>ObsQLc1_2zg(FF=Sn!Ofr;y3kw_F}CE18NMzUkm{%^j`~!EgDnBy|6P=9saA z5q6KUv6hA{@U`T`gh2lL_b3qu7>cYqeNtPzT;O)J<#^GU)#g*s9lpRwt~#DZMjXoE-F(f%FO|y&L;~yl zdzJDHGBC`WlDK-P(3+or1`PNdyn(m0d@w%6=?piUoUf5S<|>c!IC~&}mBJxKpWBHB zW7Wfx+brn#TQ2ydwW-11QZ&(b?wIwSh*;FG=S0Upbl(!TUxU_sXQzy>AsQ%^KFlA= z-#Gh@e#%R(9-NJ{%nxRLo{!Sh(NM2uvvb>n`w5l-`wF>amS>y3CwF2ZXqYmY8?tza zvgvNu*`-aZwV|7eFsH!N?PWb9D{F6uh*?q5(b3@(uNG#a_V%<~Q^BD_hpP3`-$yCm ze4(CcudYzrbaVA-CuM$|);!ijK<2ma2uZ!KVhF<@7AO{)e+&`Y?(VqeWF%#Tu0&Hc zTqG^y(G{J9COuuVP$eo3JjtTM=#PbDtz#;7Mrcy67pj5sGMKfH8O!FKqa(Q&h`$+F z$9N;R{iGUlmAfQO&JjlwkkcZo&3YbP)7`z9I3VGQ!$eX!rpqHsyWT8J*p)$;n5vI~ zz3lpWw;@|LFROL&@$x>rf%y3`hh#-q`CinO@cRg%woc(`O^q;bh|kxJ z4)A%`7&yXDA6->tD{oLEuefc~!hO-agnh3g1WF4kpKcL})Tmv^Uo1)~7 zu?5{$p-@o&mM|wQGKmF z78!y?-l+H=y5Q+J7HW~(eiVT^E6BA=&ugA!R(h*wo^PDUkkbXu|%3rIZgf+WOz8s zzL>6XJ4@>(ce4IR3?L>e1qB7~c)@gzI7z}tyZL73!b=g8$z*ZP@JC0VP5WX1DeIjn zAwP9$OAfnhe<+}vCkA`&;64z*n8bWJDZ8>3;7W)sNoDs|6&Qolw~4yYcf zHGDTW7qxF;RZ0(_c2@-&z0RlkmG%|aX`vM1{|0O$=JUVdpw_cOz8#|E2U+19Dv6bL P4gkQ(&h1ErtzXJtJnXg| literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_open_paren.png b/interface/resources/meshes/keyboard/key_open_paren.png new file mode 100644 index 0000000000000000000000000000000000000000..9dcee0b9a0b0e84bea376265a735a0a07238fb65 GIT binary patch literal 2656 zcmc&!TToM16y5g{@bZq}qgoypwW%$JfVLJzNkptLWzbQBwFrb~w1_+dQo&q65lTl; zoF<&gkMZs*eWs~??_4?oV?`(v-Q*IsAN zF5Mcw+0kyX9RN6nh6HZ|K!G|K6%H=mcm)6!Wo`OAYkO)^R!(e2B5X)VjZ4HsQ)2mv z+Y)0Fa?^iITnAu>h6ZogaiCu}@JqTXVgV8BJ)h%F%oo>kwpyd!^xeo6yFFZ7f`-l) z*D15C3%&8RzH52=Pb~VWtE;v*Ft5Z}pYMDwfpI`K^t3|;1!diTjM)&AlWi`5QF;gf z9Ns8>yKsDV;%5)XW?|io<8SW#zYq3rd_yKOIG`imTXwf5{lTY~f zm0aSxGiJpW5JaV3S2d;7NE%j(fpl|ng zG{3&|f{MQJae+>H{^-%8h7olnhX&jFM-1BWwP`#qw`1s+RY4b@dq87$PR_!BtGg`$ z(@UY^(dcNVZthRjd*yYFL;~NwRT~k zP@yuL^L2j;8z1{m$D>4TaqMytSzr}qiARYs5h?vUTN7>R6-4=vOq>DY0S%em{KHpb zP-@u!Ts(=0^suzhVAU;?LGR3fv`J?Mh#zW%s-`Xr)j2cTw9ALo%A1PCfM3#=v8$@n zCpH5v6^TTB1yv8*sgb$H#u!b%A|=wk1e7`-GBq{zibwZzhVS+sgZaxJzINA6uow`c zd;Qukn@2HYknr)bp+kyt_k)?4@yJ}bCplw|-y)3QJ`$X@4yO7}2x~o1*yY&vvmsyi z`7|4YWeWz)1|u5<*%5>AbhY#=ip9jtt~Gnoz{P%q+`&yG=ODzL)_P&ujK<7!q)aYv zATUS}_`9`soCZ3C=pKkzQmd-!*W;Thm>Ov!i+~fCukIKmN84Fc2{X^?g5*Pl1Guz4 zMj;Kfc}MEN4Q|Jm?}f>Kk;?HeWT#$nYbj!GKrIx+%PK!|HyROq6@Fn^KYy zKZFzUtnx+_HF>j4R_eZIA%+mA!ePiikj-N~)Eg|2indcCtfJ2!XDvG}LT>b-$N%f6X5QN4Ga*10+5 z&wR9o{(Y;T#d~^sGV$&t`;yY;+qaz*FD4JH>k*_l`XNePF5C1Hh!}8b##A65OM4$&p)(7+td%<;J#<${ za7SC&cB4sH7*s^ADX)+4-Uwn4pUrS_R@*Lrt@o!hAf$7M!$W!1U3qTz=!7jeAUu>f zyiwWKR^$a?(7D4n%t^huWS%QroERIM7dT{>Af7e8`ni<6eP zYs*@~ikNtkq)t1W{cb?hH%`;$!Nru`_VnFXBBaeN(M|=n;1clS_|MgQ9 z@X23^5%LQzSJf154y$6~#s?WP6MyODGgA-Pefky}j5er-<#nU2d>RlRG+NVUxxQx& z{}wm^bO0Iv@~g#$7x@w10>W?|jmj{mRomi0nAUpU@=Swk!G?*ae_-4q^N7sM<4 E3thGQkN^Mx literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_p.png b/interface/resources/meshes/keyboard/key_p.png new file mode 100644 index 0000000000000000000000000000000000000000..8d55d351cedabfda1e3a0883cb13f8d2f5f617b0 GIT binary patch literal 2821 zcmchWYf#fy8phv*ATfdjTah4Zf+7{_5~wUviXo!hrYsj55fKQcTx!)2E(VZG{t#SI zK@b)gxvGqWl>$XT!eW{@5CQ@MDjRE>k`O3Rpp>RXAPw1JJJZ?j%&VD#^-Y>s- zpYuG=xfbB(WrQ=w0f3Q@_wf?|Xjnhk^()o7oB8oZ(L7U9c53 ziuh=6eX4$tK_<@KV6M}C$~XGqu3S5;+Le>cj^=iI9~$$owwr#DA=DW|`;^cLg#eHM zuj7~YT zEu`hkgQ{tcVV_E+DkalbwQGcrO!08wnBnj|_r=gsgd?o+1WPgYY1=dYJ=UY(Q=c_Yzd}Idb}i7kXWa3R{Y`$I@|%A_!VnPY4@9?^ zT_GnsS=XZFOz8{>15fv8HQF_?u!qsizmQD@`&GJcS*YGLf z3b&9T;EwZB(;R8H(KNer$pj56<-XQCg&N8iRjJ2J;6P;ZNgF)WHQHA5+(Ge;mF~p_ zryJqY=oT9_&qhH{u{3PIA5?$IrBb8Om@RTRO&{itoGWXT&Wag<-NtE@BS*v!{L@lV zZx3!eTUVeC$0n>kS&OjG8PRofgdq(#WApp8=MR>Cs425$;d1iH+m}iWc=>CRTbC)- zLV;j1Rg{so`+j8>P?erFfh9+OHp`z$EK|vdVOyziC)goU@~X1mXt_&iZ(1z9!YBd^ z;E37;*VKdx+{7p{H5H@5qL6!@x~~@I2I89aoPTJ_UM&h9k^Uk6PC@h$J-9h0Q184+9ZSlK}(csC! z1D`yB}VsBl&o!dG;~}W5sodC zetyq+X^0|Y>6@Vt*Wa7o*x`o})AniH2LKPfoJ6}H8;2K@E7Dhfx-8cR48%W@&A*;! zA6+;@ZwTu6iurOTan1$qg!rU^+}uP9D!MR?F_p!tRYBz#M9n9?;eVcLBp=1J%4`uJSVS< z^PtOybYLjarOgBny*kBT$9X~Hug3CqBq$J?X}xL!i#4>#rmG;jR6l3Q?w*;P6t~ea zfTIn@v0W-xl?E8NezHkArA`?wo9Lip;Aw%CPj1J~mG`=o!_)pzDccd_Y3FZB)XZLv zeWjoLr+g3%{y}t`_+ZF}M2Xic>5p2GW`CR)8yoA%!GN8OPode3{>O(~U(Ecxyx4Wb z4GW>+g7Ewl>Q=WWrv24K{umL3;0XB=ab?N-lxz!wwXfATf$G6SlgVTUU#~4cxOnSy z!^F%uMLviwMUvL4(nT3*JXIP+x1#3Vt)8893Sf@JVce=rLVkClT{ak9R68>!SU&7i zm?LSA9lIG@*lc71{L6ir^CF{o9F_`z2fzTJU?byinPoghhM{>CTE%c4g*VBN@9nCW zG=c_TaVya1&8z{`rd0j|FaH;@|FxdqzV-i&|1T`qc+S7n<3HDg-|B`j1LElY1A)D? Q5dZ)m55MD$?h#pk2i4?4z5oCK literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_percentage.png b/interface/resources/meshes/keyboard/key_percentage.png new file mode 100644 index 0000000000000000000000000000000000000000..fbe1fa959975b50926d92b6a67e70340b36a9493 GIT binary patch literal 4316 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 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_period.png b/interface/resources/meshes/keyboard/key_period.png new file mode 100644 index 0000000000000000000000000000000000000000..ff726df39b64f8e6650da997339242afc8383173 GIT binary patch literal 1591 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V<#PZ!6Kid%2)Zsa|zAkcQv;*>%U(_~p?BQEJVi@yXq zWqyvUKO6sJaz;(cGv{rdXs+?Og! zRqvNQK4!znNKVf9AaCEA&b@R>M}*bNt+#rst&J{oF-U)6VHw!0LP0pef)_@U7|bwj zFq%y54&+5LlC9*V5hU#bWF#?aXm7ah$H>OOsw8pdSzFp}1_lNOPgg&ebxsLQ0A@&y ADF6Tf literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_plus.png b/interface/resources/meshes/keyboard/key_plus.png new file mode 100644 index 0000000000000000000000000000000000000000..3eab84c34d621c9b3431a897f0b740cf8bd3fee0 GIT binary patch literal 1635 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|OhYr;B4q#jQ7Y5ArrENHko$RlBro^9PJ3(6=GWnTfV#~t7z{udhz#zcDK#*Bya3#vO z@wBJr-`m-`hR5b?UVKoVuvL)s^x>-!)4Gg#&ogK7>D~KYdwW~kdY#ioeUA?wRb+X7 zXlsQ)+rOznL0-Jl#pev&n3sgFYar|%u!A3j$h9o2S8OSby=^v2PWH0`&jH_^OiaZ1 z2aks-)~-ys5%}CkFvckKfCFW=Q?6Zrgmj5L9*IuY*tN3|4rBb0;K0DJWBzsqW(JAN X+Ddy0r>bP0l+XkK`dOi4 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_q.png b/interface/resources/meshes/keyboard/key_q.png new file mode 100644 index 0000000000000000000000000000000000000000..fc733fd7fd9918056c61702f3233b4ffae05893b GIT binary patch literal 3012 zcmc(hdr;F?7ROJ1gohC@T13q9h$0VN3n~F4MiLZ-(2^p>6;LXIAgGvt7$1=2Cn^dG z8BwD&25=Z&?G^-z6k|+Wgj7O6i-16RD56z}LfMdnN3vr(&d%=a&g{zU>>qc|y?5q& z=gjAR&zzGP%<#9s5pV!tK@ac?0YKpWGJ79V30uAcfTby0!&1Iv#-$wFn;Z+i`);?^9?=TGuk(s)DuCFJyW-SN~<82q|=Olv>U{&UbUtI z%AgE(pQ0PzUmR90`@zUNYT#_1Xf){U#TX9p(ZtN9hd*x=>SYjM*QQ+fB3Ku@grZFqn!5dZG$>bDe$avwm35X zm7L0(mC|!l#C<}en6n`3?mb*LqMjNac5!%FWzl6pANTg3JjJ$)%(f+qqiy`1ViLY{Qz3{@$gCi^*cT4xY2Xvul{iFJ9Q zB(>p?Pl22nUfI=Ti$U9a4|z$xVtR`@t%YK-IP5?ejtH#4q(NS4>MldKF6_XK7n*dC z@%Rs7a1|<fOyS=r<(YzR%Kb$>sjj=WAZ>WsyQ{r`OMfP9LZZ%zcpm7aTNy z|5fF;dI59g%ZYl?S)e84lJO%%^6# zUzbSlshtqKR5Lv6#I2p)n6SY8%*}9b5u^S~R_=;$!6phNZOqR7G1?ft{dHZA1fP+? zvOrD3zx&g0t3K(8VQR`-sPxg}nzWbRnfCXb2O1|Zt#g;Hmg)w!P2l4A36_5q&Oy*&&+Ig}3O3#-F-vfkLdf zuR=RZJ<4b+HGYi4hcf6ao#^wpdabRGeH^~gFcFfFtG25Obh-KR%uvH?JF42vz zpudrNxLDh&cP9g8Fn4j9zODSNBE=n#fQuzfIVAU1qF`@v}1v123?N!wa`>AVz}InDkTCd?gCn*VsEsy zj9JBx(eWuBZkCgdHkY!=W15Q_-1>FJ`mwyZGW0$N&Hnq^;WZK}4n!x@IG@Yx# zv!rxsC2&BmriVMCpD9rwqGN58NVK8WC=6mP;!4wNU@!q2TzP`o~SuhbE zd5@^?bd5wtMm82~Gs2)=)t3L?-SZq^!PZ%%kqX}7ZhSg z)F;Wd+^G_x!MqAVk9gXc_xJZ#ulBGC!_YoUtm|!zUN}FU|Fd1nkw+?aWB?5YlSkQN zN?V<&+_2~qPVNe+-VO_BM7_!H1^kZ`K9u!;a{DhJ{&(_>k8STCaDRv&Iqc0*4w$*l j8UMR6<#!_FT7%gdu#6#iJr$G{0RYgqGW;qwN2UJ_a}S@( literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_question.png b/interface/resources/meshes/keyboard/key_question.png new file mode 100644 index 0000000000000000000000000000000000000000..57f518721333f4698abdc2e415d2ce44a97bd49e GIT binary patch literal 2727 zcmds3ZBP?t65fO$DV8KkR2nltl`j!=v-%df7H`|_v4-2nRlPvXZL+) zcZ-7pc37Ab%mKiHzSAcJ011l+y(n@Bw0Z#eAj3BzBQzy0Ba@LH3*KL*#KhXtlNj-_ zA+d}vv(xUydI6Zz=|0}OvmTGj)k%+;R-C~hzR53^-HBict53Wa;{XmEKR5&N$h)W$ z@7Y=&vzT1s(Qqp`Y-S84OGwI%qFmT@Tzc2%S9QuhQALYmHaJ+x2UZ-PjOugCUy!5g`rAiPfjPBEH`&QdiJej&c^bCQ7&9bh24ZT<_ zmZ4d-5PlXV6biq&X_6GXNxm?XO2u3yqF;f~NGCZp&9{O+G{EZ82^H~_6?D}C~Ib%zM6_I@@#Hy{%L$# z#JaZr-aSA;`_?mlC6q^_HAzC5n}1$JcRr2UO|$x!{%g@n3KQ?^;^LCL{Eju8x-z$q zYqMF>bUS8nt=Jk)@miOvmF}7umONQ|EmSbCH_Xj>jK_Z;y(%=Y}S$@o$NSOSgaDuy8AE;oGt4H@< z(FyB6gdj%-(P(%-saR_Kv3G3v+7CMb2Tpye_tgyr=}tbyQsdCkM30mQcE@?K*@mv%iBc+W0UA7Rls zCbfC_{ZG@H+$fw`1NVb8&p!oTaXRI(^mYvzx@BT9 zt;O{E2UZ9UL6$6JU~Sv-NuT)wuRmQ>-alo70%oE+afGZN8)2;P{A_TL+vR3~xGPM= z`?e_`c(z(Eev*CHKjC-&p(pFi;;-LWK8~Q;`#DtPOGUk7XAHX)j&;!DNyIRwSFuYa z8J>bojb3Ybz=+ZAsEzUe6Nezq{y356%sN49N+16a5~k~H-tK4c2oS=kfi z4eJWp(=nWpGQ;hdG&Z|>VqFSF5J{?<_XS>8ms^#egMFD{zn8TmJw_g~wi9vRS*G6d zcSDzr#%4SF(SnfGXa-^UE80Raqh*w3=+7l%D~%6b1xh>D<0sB1)b*^-)9ncU`HOl# z_Bev-yLG{_f{v!D6wUJFZDXZCJzUEcDU3zEa|QJVRmSs@Kok(%^h$TkYJ+BG80l`t zQL+0AjLE`}^^IoG6E@@@O<7T3$R%gC+8{W)_ByY$kDjz_OS95PPu?0%TWiI2kTiwY zg|$bu1mllyujxyStB=6~b3miHcuF%BFiH=T&r|#d zaJW^&Q&S&ln}YEjqlW^gfAk~7JtVtsV)`r$$5FD0dYO?yl!J%*$f+UO@S>!AsvkkeL zkTzv7Trz{>vV|0(Y|)$lP`eEZ2+t*4mvE&2NH8w4$f=|=FZgp}+r!oVBX+J-ngMnajF`U4QEK?uJ(X1IIQ}LwY+N0^-qK@9)QPNXwwZkDanhJmeKmj1&4b{$(bCZ!} zstl(6jrr_#UT;9-wPXC%S-b%}9PwJCR~x+g_O*M#JHr3(=Jp%!Q41orsTJgNI1GY` RSrh;O-8aDJ;-~xbe+OGEGsXY_ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_r.png b/interface/resources/meshes/keyboard/key_r.png new file mode 100644 index 0000000000000000000000000000000000000000..6277c640978d83298abdb27c9c8db0ee56a9c428 GIT binary patch literal 1897 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|Ps^r;B4q#jQ7Y59Zz~5IOL0$tl_X2ZCqV(wIK5888|& zeq@=!nZUe7%2!kaEOy1@~{Yl(PWxLp>_e>$>u@*mo%0Sp2r`5 zEZX_!*JPd3U5gAPSs3;?MIFo(wEuAA&$Gs*_e<|SEV%Lhd;Py{I;ZdEJ$H3vc+kyv zn7#QRv;UgcRU0Gz{Jx)WHv4MU*N7Ykh6mAJtM_Nl`usCTgoD9l;4zS zhLuG=0C|@6xkd1{^_^)V`o?x8=g-o z`&(e4leeatg<;KPC2`lYc8S z2=p1-b1=A=%s%_>{tE^shQ^f0ubP0l+XkKpqEGn literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_s.png b/interface/resources/meshes/keyboard/key_s.png new file mode 100644 index 0000000000000000000000000000000000000000..1fc108539141e31c575f9ff41b1eaef9c22bf2e5 GIT binary patch literal 3080 zcmc(hdr;F?7ROIwf)S%UL}{_5FUAS*qzS!$L>rQ{^;DfbIzT6zW3bw zIroo~{q?nEnCxFE_JzSlA5?{RQ8HssuWF@mNct~x2EG-6`rkk??4hNP)d9yca zcEn=amXmGBl@{00oUY~^`nryFcl0f`ixq1MIwrp{XdHUJeV4m=fWQBj@Ig7nTMhaE z7yuNIVNUK}D<{wI^7EJL&ja0mh|ih)9|ilv&heyq$^rGizd-gIWGM>CQY07O2v?aP z2!nB+gDA1Jej~!N{WBaNn@eu&zS7YLXHt`rVvG{2E?OleCuch&^~XXdCw}WxC=#f= zQCBF%$TVCoH`0NxQ;YnzPDBFhEjsv51TBBJWZ+uF{8z@c{Mbt*stz28~W3zMz z8p0fm@#_ja?4`k+&2HLua+SCMg@I0L{V|$jF||a&2Q;)kl`DC&QAvPXj)pBL?}S_=R$0o$C}wI7 zPj)xwX097IBi))P2J75@TQ%pVk4=T>5iiY}*qC6qnV-7L<#JlQ{a&?v8&@ zS63%_u&tA)J^65GU(hPT=Sz_@g;lNQk6L-;^=V~gWs$v!2vWj-qdiU+BsvyHITUGs z@M`hl_Hc0UdSmwjGt#^7G~;L1UPvSI0yeH7RgTTP*87pVDAP_AHa$u`C(n-4iXsxO z7uNJdWpMk36T(@f?@?rp9kNSXJ|vr22rZ=nk~@>2B~W4SN>geEm0q=_rdew z1eCcM9t~ysY^2HG9ItZ&{lRQo`hjy=uPr^eiDES`p`jN_=R8MrQ*2s;o)I7b{}OE2C`3Z&LhtP3{aVPSG?sS10CMyDCV_$_4V5& z8Pg|g{aQ*Tk*mgxO=hX)Xavk<+Na{JmO?ZJI>QOZ_zZ5GdP2Qm;1Zyiya@Mc-^LJm6F43zcHQgiWn70!XKYNcG5*A3+ zdxh~1MNfvtEuFAG`BsdWuaKW{25J07tE9xlOj1{hX(b8=s;0HS00wjqqz}X|8fJXTNojb`kO}0=o`lO@V}52geJhHdw59EaQv2`t1E-0PLxu!Z0i0sIN}UpyrRz8k_v( z#_Uti*4h=+Z`Rmy@nDx7r|o5og&}99d#cl1Dz_iiI)lZGgr37eG_4oXSS%K$tU#=T zA7wKdraxv>R#i1+;UL&xLGrn3x$)g$MY0#JO%^j~*iy5G(Y~6EoT&AC<(l2G6?DI7 zdvBbsr`;}!s1=-O=}0^^Bo4T8BOI?DpHh9;(b-SG<-0+?1`W})>oW}Mu}UAC184bs zz8FbmFwS2kT`Z8T{rlVa2)#uZcopeTl;BuA_JEO>?hF=e7K^n^uFS7$4eCR~?mG&8ra!ta3 literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_semi.png b/interface/resources/meshes/keyboard/key_semi.png new file mode 100644 index 0000000000000000000000000000000000000000..5cb1b495a4b4f79db88a8a913014169269d1153c GIT binary patch literal 1797 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|VP~PZ!6Kid%2)9xS}&AkvVi?aa7{`9%X?gjNIJl~ zl%%{PW?%35Hh%D19=!HzaQlxefqAM542%p83=9Gc3@i-9Gyk1E#&IGca@ymM8GIjh zul%0J#lRWK#6+Sg=r&^4{)5lV&4F#}HGP>-FGDM)*;VhCIsD=OWm5Z4<33-~W)>Fm z0{?*iU;d?2J}OQ>ee{k11ILPMR`(v(*v%GVVt9G(-#Z2YhK`k6zdgKJKQF!6i9uo3 z>#tV#4*ueo^x0b;tT6j;-Tw+3Hfx*t{^blim>V+W?0L&J{A*@#@R9$=v`42~Q;|X7 z!rLTxNve}U7Q^oE- z_3!Hwo>kh(@VU8Y)iE=8oDvcsy%a%7gcNI+V5Haxa&r?n9Z+cdFe+38ZmniuX4t0L Wsxx)s?;Q*b3=E#GelF{r5}E*Qq!^|E literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_slash.png b/interface/resources/meshes/keyboard/key_slash.png new file mode 100644 index 0000000000000000000000000000000000000000..be7b0fecb486566ae909baecac777cdb4e78e496 GIT binary patch literal 2580 zcmeHJT}+c#7=Bx;{S+%7!d6h!4jB?tEr<(>MCvR*O%Vo)0tyvqVa%}#gF!QBD{=B6 zL_lIeP@6dsBLM>_3}JjAVilr9$Y>NTEr{6QPeEKM{af0N-Rur8cC(9<_gtLkoaa36 z^PcwS+QmX08rQ=tVjSNSO;PaKd#wZ4*)Kifzg@!1<9G&2^oCY!xJR(Y3#Iw z6n-Q>fhSCV!1o7O-oj?>Igs=8_4Dqm>S!m4n)k?wxjd45kQ_0fXM!;nK)>`P(ik*z1FPdbiQB5?OgYE{D&k+E%G zdHIVxXF|8%sRRT!9E$p&)vCON8)Pz>>>`m=H7RU=zSVN-2oE2X4Wtwl`U1wjG#X9{u%Vq0zo0B*H0e#4nTtm%RtxtB)hOMsZHg z4%xtZi{3Mcsr_`{&`Eo7SZ#TG01>=ho_<9?wgH8g6fCKRURdlXHFYw zbA?UxylDzS@*;YE;*1NSTU<3MY#oWap(rh)x0+L{Ts^Feg_p=mQ#1X2otOk}J{?F$ zNK}+m)D{!O)G}-bdvfqAT?k@MJy*lEJh4*(ZCgb@Uud*zbVcVs5 z{>@|8#jvo|1W9FP9M#(SB_Ux$+tnQ0tStg2?_T=r+gr76opbl{yohd})zM9F zb%yu)*Iv`hz&nPoQEb|P0v2l3Tpb+5bnhNyAW-P%x@vsKsUzKk44oXo42Kk1s^^w+ zwY?H(ep1mM2stoE9i^KxZWK#?P2tBh%=G$hDgNpxt`ETsH~Xw>#wajW zKH0|TJOQN_)Pbr%X}cx4Ilc0k_eR>=hnqRLeEhMm7zM1jhC3G+#C&$HVTx|b_l2{~ zeS?ApJ0&J+sPVlBwktw$tA>lhS)VgWL)&e7ep4*LF4b_g%b1t@nr_b85t$jM5?&d0 zYu%(GcDV!UP;D)P*!iUbaG#u>_SU||{pZ%2X@I(LaP&TcdDhQZ1xF-+%A%yAfyrjM zI5L^U${miK&(a0k%pH)cX}!LggC8ZaLT&6;-|{yco?l9!?dHoBu0&j) zq@i{YP}%K1xME^YQ_erMqr1-FcTxy8AF@ zqDWv(4K^@7o}_wuNXicn4;Sf|SOySmWLpQ)f!Pq}r#@6o3RQupFYj1ivdU3p-G6oG zZ2OaMucpq-*((8KE3(uaymM|Dk;&Uy!LcR!PMtF$-SLO^IMq`{#+2;p19rYIBsTdQ z3K*vO-vbaRi{bwzkTy1mnd{EMRTK&99N#5Jr|b1aVeB9K+X<3Mcjc7}FVhvz(=BQ1 zJP`11oVDBV2*K=+YN#ZMlU^oL2RRd_m2^`+l)f4l&rO<3<-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|VPkPZ!6Kid%2)9?U&tAky$KptNCw<4lGd3O;Po2F%h2 z*wUE$CkXCw)Y{tawU)*I(Us@#t!JN}d`REAam}<;Nro5aXA3fLfUIs{U{GKni#f2` z6)1B&<=QzY2s&_xlOOW12!qjNngrACkoMlqAwO&F+O5a4?e@(O-MMyJsv+yHO>yhz zvpuM=ndhgzS;^ep;mgt~pT6AwU(KEn#=)?pX!l*E(<}{g7EFuuXP-@*_k8*381Wm7 z4qBh5FF#$jdv2s6!-P{`t9o^c|1~o>m>ra6xc&Cp8UcnAi?@b(b*`DS?Bossh7-ya z|BNk|oEQ{l{{AZ$Y1X%xL4o1IW4;5*4jc?AzrXeWVTfQ5U^t;x@z1z|g@du7#=7Bw zmpQ|W_Wex?`=egh?dR7kb4_4psAQ48C<5FaNMJHrPaEZ@u;)sl4H3 zS2cr5xR<32-$&U4>wORWtSQrT3A@M1WB>18e94FF%pc?}nV19^7+4q>7#SQG7>HrU zJ)Am0#k}vaiSPQFJ3J>(c|T;dddk5;q;6a`QLMd(awCZHK8-xUz(|qplxue&Efru1 z1}s9vq$eb8M5jR{JxELnwX=|$f*v&bGqN!}`CF{Y;b1t2fq{X+)78&qol`;+02ll3 A_W%F@ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_t.png b/interface/resources/meshes/keyboard/key_t.png new file mode 100644 index 0000000000000000000000000000000000000000..c2082b8f51223e7033afe73b82a3cca584bdd803 GIT binary patch literal 2038 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|WaEPZ!6Kid%2)ehj>oAaUT~rDBFV3ONVZS~!nz_6QgF z2^e)6G+HnwHs>VFIFK`8S;gDRZJ`Taxlelj^R#tK!Fsd(YVj(~yVmVzykLL7m%)L7 zL4bjQg@J*QEat#!XJDkr@sw+KpdjcVAx?J4!y}Bmh=XgxrTsz4TuDaRV3DI|&z`;L ze?P}8t%JcqJ2Z6a^PQFlX3t?_q9oZg>@`+iP5yz-AS7F z)h;vETd5l(QXP+(xd$&5exmmy=mMPGbn$@bg2 z(@!ssxADEqUt?Tw>JERyx|5%4tmex7E%~qXf`54fr9n?r%rz9OT4lt-!tiT_2A8t~ z!-D=(n@m95jIB|UOiT>5Q{Kz|n)0o*NRVt0+sNjS9vW&Y z)w}N>UpzlQ|H5_7tnLgSE*Dtb$uZ+&a$tC|_+v#)b#*r1hj2f}i?y%6{`&Rn*XHxA zu7M`=&ren()fB$XSQ~mSuz2Ek|wU2!9 z@^!#`Lv|*H1MWqV8U&ITq4YsQTzdnavVoA_AqsF7EqJsS fOps^dVc5Uw-;Y%q?4KAI7#KWV{an^LB{Ts5EkS@@ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_u.png b/interface/resources/meshes/keyboard/key_u.png new file mode 100644 index 0000000000000000000000000000000000000000..657527f6c0068fc41a5ecbdcdff232defe858b02 GIT binary patch literal 2232 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|Q5tr;B4q#jQ7YFBabNU}#7zp3D@|WW^BP@PeVdLHj_6 zLJZ?FrgNeUq6%6YtPE5yG;U(npLx1fF@ih$_iwpBA|{)r+^pX^$#r?=ENg~+^Xp}p z7#JBG7#IW?7+4s{Vh*f!7Rnq?v33SV3W5$C;^c=sEW%(knI=)FT>x*g@i@lc-moMj zwDeaTP56&enYJ zXeZZiv2PbN)nunNQ0hd=eE6g5+wZ;23`_^gL#}UaW?*7?d;4u!Xs9J02ZKub?z?Vx z|2=-ZxVeYHVUh8d3hDKHGAS$!Lc9)3ejiBOyzD;*gNp7Ym0F{jE_?SdFgEPAIdA{z z)aLx zMowE>!pG9EUd`vg?W(oW5`WV-M`|2=$+ISG_4Vu5KYY&GpS=FMbpLThHa~OoFNYNs zOkD%Fh&DX_+i)^PXxq)VWrxqlKmUEOZ1>)i+;WHXU(_+IV|cLtP{$uxxqUjPi>&5a z-Muqm_JJi8tOfrL{pBscG}DP;LG9s(1|LuUKK?(Bk%i&k@d_Ki%6IuM*%%uls`kds z%GRlzr$60WorAHV9Fo+j}&r zaP@4)%{O&={?$jl_52AE-PiH7sPgH{GLLh=e^>ALXYn^bhDCuv;QGg(r!OBajPCRQ z)!zBvGCzkeTs@BsWN)FP+P&Fd4hP7weKGiGUmy4WW&jJrx+NN2+qZAuq!V4kWt@{` zBDGgGrr_|;8x5~x<~r=&!#B%6bN~JIVXMDZ)qdQ*qeZ*Z;PU(LzjN0Mp-}q>EzN&u+p|hAEW8%^h0lIP2?GNI NgQu&X%Q~loCIIFy+yVdq literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_under.png b/interface/resources/meshes/keyboard/key_under.png new file mode 100644 index 0000000000000000000000000000000000000000..3694dd31090a560a802191acaf243c676263769a GIT binary patch literal 1579 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|V<>PZ!6Kid%2)9^`FM5O8%g72C2lkW)91Np|hD2Z{Vb zX<7>Rk4!Ikc;*aq!`~P+1_1^J76t}J1_uVRm;$Fc?jy zNfc^lU>x8iAiyBZzyZoB4Gatl3}i9M(f;9XE|b`$sM*E;{!9?OSp9vY0K5ZvO*tS%9LEcio5p7Y4%FAXkBh+d_C?fqvQybAv^rq0v zIkniDUMSM^Vk%OpMqAP}Ct)VD8k#0zMq_?^aQwS}u;=VQ`^P=cJ@?$t^W6J=o^zj@ z?eDkB#E4=9046@Gy*B_LFfLdK{hKlCiafps0Rqqf$N>5yPj74Igw&*R-{@#obV-4bDD`5pRzO4>vV6z1%mT$V-IheS*4sFH~dGM$Z` z=kKZsoWKW*WBKXl1Krv~4J%(;igyQ6adUXHwQ}?1@M4K>f>gtq28PhJQGJkQ>dcNg z;^yw|?%~m^vw^Fkqop*Jw!RP3hvh-)FmX^RO{^_V_(=~DHpkMmZ-c0$Q%!YJDjC%L zd=p32@eVJ=SNCtnjJb&fL^!n~WWI0Q=_)7UlMb{gqE>4_U{QQOoq4!)iR7=t7f~;1 zp^)^hr>AG{a!y2x+OhwJq&x8@DZE%f4jmt(Jo!BZxx3n=5aswZ`<#9cD<$?ijadBnuA@*cA;QKsVE0Yh(llAghr-LI>QrqwyKkbW>=K=wDqcmxjE+K zb5pq!Z^|*RP$C?iV82W69g0JN4R1 zGVs#b8Q&QkI*l*-F%EGwzb57jc+DA2v6X_GH}~`}WGknnYW0Gkv9P3sc28SBjM+)p z5%!dp9o8lts1?ov8n->k=6#q-u@7$;-mvi2#BYkpXaQb5U<-{k%-Q>P)!}YLqbgj2 zvw?=UC)r?rnIBsqVZasvuK)+)z%IL1DH@TfBrLL1x#bl;i@6gX5f;{zcA^^(Aqf3z z<=#QTxS)qxirT=U`Qpo3g+d|dp-OPerM5GFn6rF##p?|ZIyoT(VQcbLm-l@bALv_g_Xf{R zDQxQ`Gic3mK#(!M+N0R09b*qC!~Z{k@%(=NjTSz}6sq5#2nqPP7Zfg+cQgY4_^kBv JzPy6I|8IdU_TK;i literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_w.png b/interface/resources/meshes/keyboard/key_w.png new file mode 100644 index 0000000000000000000000000000000000000000..15de9b25a8cfe59c3350c498126de9eafdf30158 GIT binary patch literal 3466 zcmc(hSyYqP8iscwQ)O-|qEbVl1=BJWgh0RqtcuhE4$z1L1PR8&V60F;f&39HLP4-1 zpalY_)`AJsA`BvsKv7W!LyZW5LKIAx!jQy<=}-Q%I9;pfT%8NJI2U{E@8BScaf%GQ}THFt-}qa$k7dyEbR27_V% zU=D!Jnx^%X>tX)usSV-)y&>@%#~B;2?k^kG?fl;^u5WOEV;jVE{^ty%uJe*LV#D#p z1R_y8WJ$DDTRpX>^(j}WnBc47gK%2*sgMvVbTx!WpPfzT2rH(IyuG~xMTlJPxO)0I zpFhsw@%A`E&d4#ZhY!;}($f+S5C{aVRL1M{yT6yi0f`U<#l^kquz!(Nl%12q$(fs* ztHPEOiRf2g^6QwfO#Z&c#zvKytOQbHkxm|6#cDO>OKNLFcw(vn+OvQQAauhMl@t;va?({;fBX| zNo1Yl-?qSsmo80J8=%M&+Ni)>r|9hC38h0N*1?ajdlP$&BL~Vuf80;TD`D8fRIhf` zi^>VQa6tE)!vi~aEiq+s8vL!VRqmS$ll}&lpkaT^nb}KN*V~Cf?ybk%kq`LEdDT8G zUnISm*GG|?A8|ZeE5EPWb9mPO1tO=xA9oaC&0WUczNO%cwL+E_7H;erz?3P5{SH$d2>kryvVGd{FOl*$Dso3PF4`gnKuZo7$b_dnQYX`{uOtP@{n zZw(v2uJ|bu>Fo4|PMD%Ukkh4)^wCT@tnenk4H4k?48)v2K7(2B^SGS1{2 z_v~SJWUc2iMuTzs1!DEAzVL02e`laVE}P#%ONv%tG#DcSL{%E-T~w0Ie%>v6YDHfz z=!WoAh$KS%c?_# zVo_9Zx0rYUNNc1|Iwc z1~_s}!JxLVJ|<}#F4J#u{dDvPH+n_8n22s8r1P1>ipeOxKk_7}@CvB;uGZ;d(jrAr zDAn)i=AP^yxfdf-RrJ1zT64;JY(|)1Qr>az_0q}5#Fk}@OP7U1a`S@+I(F9@L$R*G z>fW7K&p)vY8Tm$fmy>)YM>98qG&+v)7ET%^p$= z_u3#9z*Ga~&(21>RxE_I@X+U6uwz;^Ypec z8h41|7_@4BvR@Y)3E4ZK$(Tp{=6pRgzDY%FuH!RF=^|&t13>zVEFtOg*>S9Q*-Xtk< z@r_={zqO4RK1}$OX^U{5`79q@fb)3m>@I>3)oO_9t4<7Kpl#iZc_)$l z3TyS%7mSLXj*QD5xW(4weBTXaf!CD@k);{RasrGD1)JrS4Wv!CoOuKq_VsF}cDGG~iD=&Ru% z99r|H9C#%|v7H zw78^%Iw3NcALCopO%3c|0J2eyj6E;)n3M8ftqgm&8R(-^*z*RB!RNUB=XSG3N&~>) ziH1}y*TcUufCd04;0b^cSXa4Gyv_Un94t4I$^VLU8w{2k#LfQ`FxS~>ow;;I^?4h} XH&uO{;pSNe002Jw{q|MuC8zugEve^% literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_x.png b/interface/resources/meshes/keyboard/key_x.png new file mode 100644 index 0000000000000000000000000000000000000000..d81a423f3b3651de72b207bed25a6dc7d38976f4 GIT binary patch literal 2997 zcmdT`Yg7{07Cs;)7AAVrn~VZ!jP_8nOv_71Dm6)inW?F$J;2ZmwU9JX8EbrGjWylG zu?&0pno+aI#Yd%HDst3B$-1P4T8f5>PZ%X+maa8x&97Pa=CAwXti9LT>)ZSL_Bm(m zowb*`%Xk@T834d|_cvbq0Dyw|1D`iZD9>^LfF+xJfbAC(!9I291Ox1##~fy0cOO4= zl(CO-h@KeN#2^5$JaD(yj{OO5hu@9I^*u*7#3@?W^UOZ3*kdU9ZV;^7S_1qaY+&8s z&vWCgD1^Dia=4o{j)^~*sC?jATw*HS@uX?MSWAkGfzyqv)oNHneOv0IB>)O|0AK-t z&y)J!-7mrXsnjCj@GoHer|OG_1Lz`Jw?O2}JO77{KkOG~S|l9%m(NCm(ob7R(Ek49 z6&S=)syU2Dot@KZqaOZBvqxP%r)x7i5j}#;}F|hKUFs{$~7d_{qjaRd_-DE_-PL-IelmsJ!j5+ z&hWw<9B_O3`x!N~2uzrNr&6gLp~gH6CI{$?rIA6oDa{Yg;;?U-Tdj6Jmx+2WdhF8% zS?7q^hh-SObnS>H38Oa!zwJTM1peuv5vS=4shDxKY<%AU7a8Lf+MkEyMbhE7A3j-DLHMEYqNkqZq z3jDdg*sZVb4WPTPMak_(=th}p?=u6Ox>h3L%8{xAPkoNKTqRqi4r{#tR@~g&%q6xF zxetR!ICU?K_WPUHY?VFndD~)!v_{IzQ9<7sRqT*yvMhlYIl$M3Z-}?Z9&Xn+^_f6H z9NE{)(d7MO?I+*HXM!0nOK#_I)F|`istY@1%V)zM`_N*?#ZP=%Kp|B*?WUy)!b9QS z`Z)C<=FvH07>Gh2s0{CD3fWYK+b>x|A+cMcU7Ex4>myd%Cwz5tJYo2QI}`}bY8*8} zO;$Eny7$9Fu>?>^Y#(}3ze1DM7435AnP7x-Y-$(iNl#m#W@3j_Om3UF9j+w~Sy4!A ze|q_V=os7f%~xIX8l3H_kyAqLQ}Xp`rra(9NFF#Fd^5=#7Nrt5_HcXq`<+P_}jygXOjhR(Czmz z@d6%?U%Og%D!F95^sNpL3wie zWF*s$LLy{3Te<$rGPQ<=(m5IMI9W6__s+cy!me^53c}+U5u23xiRsA#0)j#!{4+#; zrSar@Q^2fjJcz6b9JDKvmL>}bW)u>k=!NGIB7H%e@ig)3GabHx?`d$`34vVRmcP&2)NI!R`nE%J5V#$a3SbIyv{gCr>KB^oO z3Vzi3IRd;bDJ$!ZO^{-$-m#kRO$FEji4DKGe`aRpwaF6Y^yy-0Q^j614~IpI>6;>Z zE?pejb$+L<$^{CJR|S`|bQ|sp%F0eBCr@9O#^>@9VB!x|a+T|6Pm_!@p*Qj%4ckK< z`E|6?f1oa)4#YtrywsI4%{Fw-eau9^g(>>@w;aeDzZ(kf@zDzx%VsZEM$ z9)xr&P0%Q92wh3ehwLeq*0s1dAi$HV#{>;H-DKv;`;3Y(M~(<3vei zTVT>_WxN_bYGDco-2t&xj2fB+dE6)U>fE|5SnacSHfgLXm>1tFN8B5mm#jM;l>3G& zyBe|Tk|C*<41EmUywq zlo2%RZv)1bG>+sw4pYT$<(`lQ3aJKBM~yPo`K=K$(Wq40k?J5~p&D)ZhH_=r6)a$P z4uqKE|1ACD@zw0Qui7YuDe#RcgC%mK%7Pb zwBJ~+No#cVdDXJ`Y2WCWJr4K^C^^t%P6wpyaJy8B~?w_E3n zEz1}|`7kU1-5EG6fB+HzNC05qi`>P+$1D~*L>D=O7YYCC5&qw-75}GOT!TutwgJo@ W?s*+|hRFf|fZb%OSB1yHwBG^ucD9EA literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_y.png b/interface/resources/meshes/keyboard/key_y.png new file mode 100644 index 0000000000000000000000000000000000000000..cb85af5b322234afe8e41e3d2c24f2ff8aafc1a4 GIT binary patch literal 3072 zcmc(hYfzJC7RS%~a*LpbtO!D_NtG%Q0YzL{MG{)kQWWG;i9`YXxi%TfTaW_bp&0+{<)hr_548ox=?Gk%0b0jfw=ZhIZ8Hnm`rlI|Ga-u+V$24`{EF>Rx7*(bO3Vz zLjdHX!ao)VWJbl$9H-Afy5AK4Xyt!z*vB>QcWj2Z;r|=~8#R%MJSgv;Mb00sdM@LZ z(H0o;ml`Y8k}OqI`sG9p#|;y1STH+ZC^Rt1l&E`pb_v&y_8+S3O04n!_lP?{5t;E2}l8S7vOwa;_<&vg-cyU2a znPw_irjg)66jAQJ%Gu%ATG4KN_r_{whf71SY*{*pM|}mJe#eQ#kgK}Vu{FkWo*_&; zxlcW%)u;btZmP;*-pgF{Ub|((r$Q&h>6I4-}0 zKG2MJ?iu`m5WcRp*sZd#tH~dmv@(Qe{+sOPkC<8fEp3>#f9Vp+PW8)|%j+c(pC48p z#NQ70n!C15o7lrw&Nkm?PCsZkW3>wR0LnMg3sq-Aj^A^H+qenB52R-K&tmrCf==U@ zMfg#mukpV|m%ni77tWROI0pz2NMX>b?^4_3@&KjXJG^c02zr@O6Vk_wjt-YNTBZll zq!mM~b*YQ88C^#jCQgSL!IMORcVoFWOM)+kZH4Dre^ljq>=`G&!`osu(W`tXO?Fs4 zqHYUacG*oPXe}g~*+a3vv|XAK^;yl}-^K2%5!?d`ELBq;1_6|DvQIY2ywsx3L32;1 z(|Lr*>J%eC&=&XH@Uu)mku}R6ii2|)s-%N+yB~cvF)us>Cqc2_q-|20g7L$q!S*hc_c_Q$@WbN zA19LyQIOPZ&^B{9Q#hA2fRPQsheH}0u&}#{>`t6*ghI(35h2&7D9O=`+hF*LM1e8) zJH3r~($B>EydnG;h?BnCs=}+)yFv|aM>&JKN$JiK$;Zp|F*- zeu*gWy$;S-h8y0UWjdrzDDRO*tz58>by7>*4s9#Sy&F@CXg^P<&j%OpN&m4Q-1Ng9 znKM?XJeD|ZhQe0X@!YtBxL1!g5_?fvF_ci3;@vp48gDd(i^U_FxD&T70mAQoawI)d zOI#)H>pb}(_+n^dtx=Th$BqGA{v?*#saG;GRa!)U zh!LRbOJ>m>t15go3Z;H3YR`wqvz-G4OlQ3STPs#v{}GwsZGr;E@(bwOBMok{h6@C! z^76BIp@|moNZ=~>Ys9XfK)X=BS4k^j+}9=TmSssm?sou@2lML%>F!1fwr%S zL}FS5FN@~^1c>ZoO>G}=S8Z+yayz#%R! z?(C%Lhq7cHWg+v{;a7bZLf!?z*+Os!ykKC~&wYe=$+@v`0Ip zYHH=xV`=TJImELqwy&Cb`kjfqjAJ+Ldv^>6P7=SFv}2n?v1fk~magaz$>OQ{Y6-KM zA2#5jU%5J&4$7en$sQ((LWE{wqNZbzO*RKtCw4wh`}*D#AH?jIiXjYpS{Y&vuItsW zI>$^9e&x7YLKIDZ0pc#QA>`>)tS-}|@hYFQJTL7CCV=8Dvr zz7O+A<`H|Rl6fvR7bcHEo3p3i^t3ka&$-zQ_VaHsu85bM9HlfUW82yek55m#31zod zC9<9Gi}H0-uQ}&ev|>jNoWG>=0GP~87Xu1>015y>@BjY)@MD<&_zG1z{J~~vCO-2w qn=(VQG(+6{-=^umGZkFGEJC6Cjbp}9M~DRg@bdK8c;k!EqrU>>4D2BQ literal 0 HcmV?d00001 diff --git a/interface/resources/meshes/keyboard/key_z.png b/interface/resources/meshes/keyboard/key_z.png new file mode 100644 index 0000000000000000000000000000000000000000..462531351db199b49c68afdcebcd304daa202f80 GIT binary patch literal 2115 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4iHr><-C@GfkCpwHKHUqKdq!Zu_%?nF(p4K zRlzeiF+DXXH8G{K@MNkD0|Wb0PZ!6Kid%2)2Ik&&kU8+MM2o@ML-Gbs7Mo%7EXUpj znoVtvXBMOd7@u%7U@<+49oBH=goWM_rG_e zywG!@8EgFizeophSQsu{Eu9OZH*LQE{`bEA=-s#9+S%E?QCwNf?$Fosw}D~8VgaYn zP>^v2{(G2|^e>xSEtTC{Q~9m@momeId6n~KFEjkc%;Zp3bBmvY!SdCvJc+g)TaVT4 z@YZWQ*|bq!fT1Ym4g0!JTphpd|Mr3moLf2X_BK@@6)6)MJ$~?X%^;?l$uW_gU zu{sBa6Bj={kgxri{ieO9g7wH=PDK-6;k|cm@G~)Z8iDjKd$r39Y)0PCHRd2+E_qd8 z;ZlDzIdaFEt)RHpmIC0WM(Hb;0R>yawm^zQ~d-x#qM zjf@*uAt|-seT{_7>#cY5?%%uj?!opC%Twmo$e1`R+rz{(umz(4Lp07Dg*~5Ph~mnS z7z)vIEJOi?+8cH#K6E{Tf|$zz`!6`;u=vBoS#-wo>-L1 z;Fyx1l&avFo0y&&l$w}QS$HzlhJk@)pQnpsNX4x;cQ*DeH4t&Q_+NbyOS-@%#?%0& zt$|Fb7Z?>&^bShCzUm)-D}n99y7Ma!9Ma#hXq>!o+MoxQ`rV88VJ??j1R>nUT zkl}%qVoT;MF3*ihRz59^45G_!gmcbMjuaDQW4NOn?w;2#3%_SV@XfcF4&_PBa$s)N1&2ea4+>XQH^Z`sSFsBm?SD4dD zaO->q^dLkFQ8X?*fXn7?%e~9U5Rl$(b0<-e#hHQO%1uT7@`%eOwfAQ;GdxJ!=}^=j zayRj&0goFa!-@?K^WJY*_u8hDk)iz5+lkyR?SIYs7P}fUJdoRIRa8BtVAA)>d8%`i zmzta}?zvdNz%bqIhKX-~e%sWmiStFGr={LDQ)Xg_S@`~Up$vEDhD}y)lJ9>njlC+t z@L@Bn8ILokb)swe4R()y}pNo?{(JaZG%Q-e|yQz{EjrrIztFdX-EaSW-r_2!}>BLf4+VS@wvpDQx=g*aXI^eXRV zWnkc7U|?iWU|?VYnJ>V=z{KD{nIN^yK{!J`ijj>WA&vQO32T210|Nttr>mdKI;Vst E0L)M=YXATM literal 0 HcmV?d00001 diff --git a/interface/resources/qml/controlsUit/Keyboard.qml b/interface/resources/qml/controlsUit/Keyboard.qml index 9d4fd33022..c38631ff79 100644 --- a/interface/resources/qml/controlsUit/Keyboard.qml +++ b/interface/resources/qml/controlsUit/Keyboard.qml @@ -36,13 +36,29 @@ Rectangle { readonly property int raisedHeight: keyboardHeight + (showMirrorText ? keyboardRowHeight : 0) - height: enabled && raised ? raisedHeight : 0 - visible: enabled && raised + height: 0 + visible: false property bool shiftMode: false property bool numericShiftMode: false + + onPasswordChanged: { + var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard"); + if (use3DKeyboard) { + KeyboardScriptingInterface.password = password; + } + } + onRaisedChanged: { + var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard"); + if (!use3DKeyboard) { + keyboardBase.height = raised ? raisedHeight : 0; + keyboardBase.visible = raised; + } else { + KeyboardScriptingInterface.raised = raised; + KeyboardScriptingInterface.password = raised ? password : false; + } mirroredText = ""; } diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index 6314921286..08aa903ae9 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -127,6 +127,10 @@ TabletModalWindow { } } + Component.onDestruction: { + loginKeyboard.raised = false; + } + Keyboard { id: loginKeyboard raised: root.keyboardEnabled && root.keyboardRaised diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index 39590748cf..9635681c34 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -19,7 +19,7 @@ Rectangle { HifiControls.Keyboard { id: keyboard z: 1000 - raised: parent.keyboardEnabled && parent.keyboardRaised + raised: parent.keyboardEnabled && parent.keyboardRaised && HMD.active numeric: parent.punctuationMode anchors { left: parent.left @@ -204,7 +204,8 @@ Rectangle { property bool isInManageState: false - Component.onCompleted: { + Component.onDestruction: { + keyboard.raised = false; } AvatarAppStyle { @@ -235,6 +236,8 @@ Rectangle { avatarIconVisible: mainPageVisible settingsButtonVisible: mainPageVisible onSettingsClicked: { + displayNameInput.focus = false; + root.keyboardRaised = false; settings.open(currentAvatarSettings, currentAvatar.avatarScale); } } @@ -344,6 +347,10 @@ Rectangle { emitSendToScript({'method' : 'changeDisplayName', 'displayName' : text}) focus = false; } + + onFocusChanged: { + root.keyboardRaised = focus; + } } ShadowImage { diff --git a/interface/resources/qml/hifi/avatarapp/Settings.qml b/interface/resources/qml/hifi/avatarapp/Settings.qml index bad1394133..cd892c17b1 100644 --- a/interface/resources/qml/hifi/avatarapp/Settings.qml +++ b/interface/resources/qml/hifi/avatarapp/Settings.qml @@ -14,6 +14,22 @@ Rectangle { signal scaleChanged(real scale); + property bool keyboardEnabled: true + property bool keyboardRaised: false + property bool punctuationMode: false + + HifiControlsUit.Keyboard { + id: keyboard + z: 1000 + raised: parent.keyboardEnabled && parent.keyboardRaised + numeric: parent.punctuationMode + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + } + property alias onSaveClicked: dialogButtons.onYesClicked property alias onCancelClicked: dialogButtons.onNoClicked @@ -314,6 +330,10 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right placeholderText: 'user\\file\\dir' + + onFocusChanged: { + keyboardRaised = (avatarAnimationUrlInputText.focus || avatarCollisionSoundUrlInputText.focus); + } } } @@ -340,6 +360,10 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right placeholderText: 'https://hifi-public.s3.amazonaws.com/sounds/Collisions-' + + onFocusChanged: { + keyboardRaised = (avatarAnimationUrlInputText.focus || avatarCollisionSoundUrlInputText.focus); + } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index ed4ba66b2b..874c1c53b7 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -40,6 +40,10 @@ Rectangle { source: "images/wallet-bg.jpg"; } + Component.onDestruction: { + keyboard.raised = false; + } + Connections { target: Commerce; diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml index 6cd220307d..b0f17ff841 100644 --- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml @@ -88,6 +88,10 @@ Rectangle { checkMenu.restart(); } + Component.onDestruction: { + keyboard.raised = false; + } + function updateRunningScripts() { function simplify(path) { // trim URI querystring/fragment diff --git a/interface/resources/qml/hifi/tablet/Edit.qml b/interface/resources/qml/hifi/tablet/Edit.qml index 4acced86ce..099c53cda2 100644 --- a/interface/resources/qml/hifi/tablet/Edit.qml +++ b/interface/resources/qml/hifi/tablet/Edit.qml @@ -49,5 +49,11 @@ StackView { if (currentItem && currentItem.fromScript) currentItem.fromScript(message); } + + Component.onDestruction: { + if (KeyboardScriptingInterface.raised) { + KeyboardScriptingInterface.raised = false; + } + } } diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 0f26ba20aa..b8972378ad 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -56,10 +56,13 @@ StackView { Qt.callLater(function() { addressBarDialog.keyboardEnabled = HMD.active; addressLine.forceActiveFocus(); + addressBarDialog.raised = true; }) } + Component.onDestruction: { root.parentChanged.disconnect(center); + keyboard.raised = false; } function center() { @@ -218,6 +221,11 @@ StackView { leftMargin: 8; verticalCenter: addressLineContainer.verticalCenter; } + + onFocusChanged: { + addressBarDialog.raised = focus; + } + onTextChanged: { updateLocationText(text.length > 0); } diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml index 57ca705352..a5d7b23df6 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletPreferencesDialog.qml @@ -242,6 +242,10 @@ Item { keyboardEnabled = HMD.active; } + Component.onDestruction: { + keyboard.raised = false; + } + onKeyboardRaisedChanged: { if (keyboardEnabled && keyboardRaised) { var delta = mouseArea.mouseY - (dialog.height - footer.height - keyboard.raisedHeight -hifi.dimensions.controlLineHeight); diff --git a/interface/resources/sounds/keyboard_key.mp3 b/interface/resources/sounds/keyboard_key.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e2cec81032389f719f90dd60b0ae4ca10719e725 GIT binary patch literal 9089 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 literal 0 HcmV?d00001 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bd126048dd..ccefb3c772 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -187,6 +187,7 @@ #include "scripting/SelectionScriptingInterface.h" #include "scripting/WalletScriptingInterface.h" #include "scripting/TTSScriptingInterface.h" +#include "scripting/KeyboardScriptingInterface.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif @@ -205,6 +206,7 @@ #include "ui/UpdateDialog.h" #include "ui/overlays/Overlays.h" #include "ui/DomainConnectionModel.h" +#include "ui/Keyboard.h" #include "Util.h" #include "InterfaceParentFinder.h" #include "ui/OctreeStatsProvider.h" @@ -953,6 +955,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); return previousSessionCrashed; } @@ -2327,6 +2331,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Preload Tablet sounds DependencyManager::get()->preloadSounds(); + DependencyManager::get()->createKeyboard(); _pendingIdleEvent = false; _pendingRenderEvent = false; @@ -2436,11 +2441,17 @@ QString Application::getUserAgent() { } void Application::toggleTabletUI(bool shouldOpen) const { - auto tabletScriptingInterface = DependencyManager::get(); auto hmd = DependencyManager::get(); if (!(shouldOpen && hmd->getShouldShowTablet())) { auto HMD = DependencyManager::get(); HMD->toggleShouldShowTablet(); + + if (!HMD->getShouldShowTablet()) { + DependencyManager::get()->setRaised(false); + _window->activateWindow(); + auto tablet = DependencyManager::get()->getTablet(SYSTEM_TABLET); + tablet->unfocus(); + } } } @@ -2633,6 +2644,7 @@ void Application::cleanupBeforeQuit() { // it accesses the PickManager to delete its associated Pick DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete"; } @@ -3113,6 +3125,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Vec3", new Vec3()); surfaceContext->setContextProperty("Uuid", new ScriptUUID()); surfaceContext->setContextProperty("Assets", DependencyManager::get().data()); + surfaceContext->setContextProperty("Keyboard", DependencyManager::get().data()); surfaceContext->setContextProperty("AvatarList", DependencyManager::get().data()); surfaceContext->setContextProperty("Users", DependencyManager::get().data()); @@ -6848,6 +6861,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("LODManager", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Keyboard", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Paths", DependencyManager::get().data()); scriptEngine->registerGlobalObject("HMD", DependencyManager::get().data()); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 16e8af5683..2ca997a1fc 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -356,6 +356,8 @@ Menu::Menu() { qApp->setHmdTabletBecomesToolbarSetting(action->isChecked()); }); + addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::Use3DKeyboard, 0, false); + // Developer > Render >>> MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 1e9955a760..f1d56825b5 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -210,6 +210,7 @@ namespace MenuOption { const QString TurnWithHead = "Turn using Head"; const QString UseAudioForMouth = "Use Audio for Mouth"; const QString UseCamera = "Use Camera"; + const QString Use3DKeyboard = "Use 3D Keyboard"; const QString VelocityFilter = "Velocity Filter"; const QString VisibleToEveryone = "Everyone"; const QString VisibleToFriends = "Friends"; diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index 26b5aacac5..6e979d2d91 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -31,6 +31,9 @@ #include +static const float WEB_TOUCH_Y_OFFSET = 0.105f; // how far forward (or back with a negative number) to slide stylus in hand +static const glm::vec3 TIP_OFFSET = glm::vec3(0.0f, StylusPick::WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, 0.0f); + unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) { switch (type) { case PickQuery::PickType::Ray: @@ -137,7 +140,12 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties maxDistance = propMap["maxDistance"].toFloat(); } - return DependencyManager::get()->addPick(PickQuery::Stylus, std::make_shared(side, filter, maxDistance, enabled)); + glm::vec3 tipOffset = TIP_OFFSET; + if (propMap["tipOffset"].isValid()) { + tipOffset = vec3FromVariant(propMap["tipOffset"]); + } + + return DependencyManager::get()->addPick(PickQuery::Stylus, std::make_shared(side, filter, maxDistance, enabled, tipOffset)); } // NOTE: Laser pointer still uses scaleWithAvatar. Until scaleWithAvatar is also deprecated for pointers, scaleWithAvatar should not be removed from the pick API. @@ -416,4 +424,4 @@ void PickScriptingInterface::setParentTransform(std::shared_ptr pick, pick->parentTransform = std::make_shared(pickID); } } -} \ No newline at end of file +} diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index a44d14b4a6..f0edb4d9ef 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -16,6 +16,11 @@ #include "LaserPointer.h" #include "StylusPointer.h" #include "ParabolaPointer.h" +#include "StylusPick.h" + +static const glm::quat X_ROT_NEG_90{ 0.70710678f, -0.70710678f, 0.0f, 0.0f }; +static const glm::vec3 DEFAULT_POSITION_OFFSET{0.0f, 0.0f, -StylusPick::WEB_STYLUS_LENGTH / 2.0f}; +static const glm::vec3 DEFAULT_MODEL_DIMENSIONS{0.01f, 0.01f, StylusPick::WEB_STYLUS_LENGTH}; void PointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const { DependencyManager::get()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); @@ -50,6 +55,8 @@ unsigned int PointerScriptingInterface::createPointer(const PickQuery::PickType& * @typedef {object} Pointers.StylusPointerProperties * @property {boolean} [hover=false] If this pointer should generate hover events. * @property {boolean} [enabled=false] + * @property {Vec3} [tipOffset] The specified offset of the from the joint index. + * @property {object} [model] Data to replace the default model url, positionOffset and rotationOffset. */ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) const { QVariantMap propertyMap = properties.toMap(); @@ -64,7 +71,28 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) enabled = propertyMap["enabled"].toBool(); } - return DependencyManager::get()->addPointer(std::make_shared(properties, StylusPointer::buildStylusOverlay(propertyMap), hover, enabled)); + glm::vec3 modelPositionOffset = DEFAULT_POSITION_OFFSET; + glm::quat modelRotationOffset = X_ROT_NEG_90; + glm::vec3 modelDimensions = DEFAULT_MODEL_DIMENSIONS; + + if (propertyMap["model"].isValid()) { + QVariantMap modelData = propertyMap["model"].toMap(); + + if (modelData["positionOffset"].isValid()) { + modelPositionOffset = vec3FromVariant(modelData["positionOffset"]); + } + + if (modelData["rotationOffset"].isValid()) { + modelRotationOffset = quatFromVariant(modelData["rotationOffset"]); + } + + if (modelData["dimensions"].isValid()) { + modelDimensions = vec3FromVariant(modelData["dimensions"]); + } + } + + return DependencyManager::get()->addPointer(std::make_shared(properties, StylusPointer::buildStylusOverlay(propertyMap), hover, enabled, modelPositionOffset, + modelRotationOffset, modelDimensions)); } /**jsdoc diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index ad12db4df2..b6adba4a12 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -10,6 +10,7 @@ #include "Application.h" #include "EntityScriptingInterface.h" #include "ui/overlays/Overlays.h" +#include "ui/Keyboard.h" #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" @@ -40,9 +41,12 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) { bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); + auto keyboard = DependencyManager::get(); + QVector ignoreItems = keyboard->getKeysID(); + ignoreItems.append(getIgnoreItemsAs()); RayToOverlayIntersectionResult overlayRes = qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking, - getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); + getIncludeItemsAs(), ignoreItems, !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (overlayRes.intersects) { return std::make_shared(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); } else { @@ -118,4 +122,4 @@ glm::vec2 RayPick::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm:: glm::vec2 RayPick::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized) { auto props = DependencyManager::get()->getEntityProperties(entityID); return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint(), unNormalized); -} \ No newline at end of file +} diff --git a/interface/src/raypick/StylusPick.cpp b/interface/src/raypick/StylusPick.cpp index c495ddd194..0416563272 100644 --- a/interface/src/raypick/StylusPick.cpp +++ b/interface/src/raypick/StylusPick.cpp @@ -21,11 +21,7 @@ #include using namespace bilateral; - -// TODO: make these configurable per pick -static const float WEB_STYLUS_LENGTH = 0.2f; -static const float WEB_TOUCH_Y_OFFSET = 0.105f; // how far forward (or back with a negative number) to slide stylus in hand -static const glm::vec3 TIP_OFFSET = glm::vec3(0.0f, WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, 0.0f); +float StylusPick::WEB_STYLUS_LENGTH = 0.2f; struct SideData { QString avatarJoint; @@ -64,8 +60,8 @@ bool StylusPickResult::checkOrFilterAgainstMaxDistance(float maxDistance) { return distance < maxDistance; } -StylusPick::StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled) : - Pick(StylusTip(side), filter, maxDistance, enabled) +StylusPick::StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled, const glm::vec3& tipOffset) : + Pick(StylusTip(side), filter, maxDistance, enabled), _tipOffset(tipOffset) { } @@ -90,7 +86,7 @@ static StylusTip getFingerWorldLocation(Side side) { } // controllerWorldLocation is where the controller would be, in-world, with an added offset -static StylusTip getControllerWorldLocation(Side side) { +static StylusTip getControllerWorldLocation(Side side, const glm::vec3& tipOffset) { static const std::array INPUTS{ { UserInputMapper::makeStandardInput(SIDES[0].channel), UserInputMapper::makeStandardInput(SIDES[1].channel) } }; const auto sideIndex = index(side); @@ -114,7 +110,7 @@ static StylusTip getControllerWorldLocation(Side side) { // add to the real position so the grab-point is out in front of the hand, a bit result.position += result.orientation * (sideData.grabPointSphereOffset * sensorScaleFactor); // move the stylus forward a bit - result.position += result.orientation * (TIP_OFFSET * sensorScaleFactor); + result.position += result.orientation * (tipOffset * sensorScaleFactor); auto worldControllerPos = avatarPosition + avatarOrientation * pose.translation; // compute tip velocity from hand controller motion, it is more accurate than computing it from previous positions. @@ -131,7 +127,7 @@ StylusTip StylusPick::getMathematicalPick() const { if (qApp->getPreferAvatarFingerOverStylus()) { result = getFingerWorldLocation(_mathPick.side); } else { - result = getControllerWorldLocation(_mathPick.side); + result = getControllerWorldLocation(_mathPick.side, _tipOffset); } return result; } @@ -236,4 +232,4 @@ Transform StylusPick::getResultTransform() const { Transform transform; transform.setTranslation(stylusResult->intersection); return transform; -} \ No newline at end of file +} diff --git a/interface/src/raypick/StylusPick.h b/interface/src/raypick/StylusPick.h index cd01df20e9..3e0ee452e9 100644 --- a/interface/src/raypick/StylusPick.h +++ b/interface/src/raypick/StylusPick.h @@ -58,7 +58,7 @@ public: class StylusPick : public Pick { using Side = bilateral::Side; public: - StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled); + StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled, const glm::vec3& tipOffset); StylusTip getMathematicalPick() const override; PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override; @@ -71,6 +71,11 @@ public: bool isLeftHand() const override { return _mathPick.side == Side::Left; } bool isRightHand() const override { return _mathPick.side == Side::Right; } bool isMouse() const override { return false; } + + static float WEB_STYLUS_LENGTH; + +private: + glm::vec3 _tipOffset; }; -#endif // hifi_StylusPick_h \ No newline at end of file +#endif // hifi_StylusPick_h diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 4ba3813c4a..caa3151cc5 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -17,9 +17,6 @@ #include "PickScriptingInterface.h" #include -// TODO: make these configurable per pointer -static const float WEB_STYLUS_LENGTH = 0.2f; - static const float TABLET_MIN_HOVER_DISTANCE = -0.1f; static const float TABLET_MAX_HOVER_DISTANCE = 0.1f; static const float TABLET_MIN_TOUCH_DISTANCE = -0.1f; @@ -28,9 +25,15 @@ static const float TABLET_MAX_TOUCH_DISTANCE = 0.005f; static const float HOVER_HYSTERESIS = 0.01f; static const float TOUCH_HYSTERESIS = 0.001f; -StylusPointer::StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled) : +static const QString DEFAULT_STYLUS_MODEL_URL = PathUtils::resourcesUrl() + "/meshes/tablet-stylus-fat.fbx"; + +StylusPointer::StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled, + const glm::vec3& modelPositionOffset, const glm::quat& modelRotationOffset, const glm::vec3& modelDimensions) : Pointer(DependencyManager::get()->createStylusPick(props), enabled, hover), - _stylusOverlay(stylusOverlay) + _stylusOverlay(stylusOverlay), + _modelPositionOffset(modelPositionOffset), + _modelDimensions(modelDimensions), + _modelRotationOffset(modelRotationOffset) { } @@ -42,9 +45,19 @@ StylusPointer::~StylusPointer() { OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) { QVariantMap overlayProperties; - // TODO: make these configurable per pointer + // TODO: make these configurable per pointe + QString modelUrl = DEFAULT_STYLUS_MODEL_URL; + + if (properties["model"].isValid()) { + QVariantMap modelData = properties["model"].toMap(); + + if (modelData["url"].isValid()) { + modelUrl = modelData["url"].toString(); + } + } + overlayProperties["name"] = "stylus"; - overlayProperties["url"] = PathUtils::resourcesUrl() + "/meshes/tablet-stylus-fat.fbx"; + overlayProperties["url"] = modelUrl; overlayProperties["loadPriority"] = 10.0f; overlayProperties["solid"] = true; overlayProperties["visible"] = false; @@ -72,13 +85,12 @@ void StylusPointer::updateVisuals(const PickResultPointer& pickResult) { void StylusPointer::show(const StylusTip& tip) { if (!_stylusOverlay.isNull()) { QVariantMap props; - static const glm::quat X_ROT_NEG_90{ 0.70710678f, -0.70710678f, 0.0f, 0.0f }; - auto modelOrientation = tip.orientation * X_ROT_NEG_90; + auto modelOrientation = tip.orientation * _modelRotationOffset; auto sensorToWorldScale = DependencyManager::get()->getMyAvatar()->getSensorToWorldScale(); - auto modelPositionOffset = modelOrientation * (vec3(0.0f, 0.0f, -WEB_STYLUS_LENGTH / 2.0f) * sensorToWorldScale); + auto modelPositionOffset = modelOrientation * (_modelPositionOffset * sensorToWorldScale); props["position"] = vec3toVariant(tip.position + modelPositionOffset); props["rotation"] = quatToVariant(modelOrientation); - props["dimensions"] = vec3toVariant(sensorToWorldScale * vec3(0.01f, 0.01f, WEB_STYLUS_LENGTH)); + props["dimensions"] = vec3toVariant(sensorToWorldScale * _modelDimensions); props["visible"] = true; qApp->getOverlays().editOverlay(_stylusOverlay, props); } diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index ff60fd78e5..64e2a38bed 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -21,7 +21,8 @@ class StylusPointer : public Pointer { using Ptr = std::shared_ptr; public: - StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled); + StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled, + const glm::vec3& modelPositionOffset, const glm::quat& modelRotationOffset, const glm::vec3& modelDimensions); ~StylusPointer(); void updateVisuals(const PickResultPointer& pickResult) override; @@ -81,6 +82,10 @@ private: bool _showing { true }; + glm::vec3 _modelPositionOffset; + glm::vec3 _modelDimensions; + glm::quat _modelRotationOffset; + }; #endif // hifi_StylusPointer_h diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index ea24d6c793..f2f8d3b8d4 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -119,8 +119,11 @@ void HMDScriptingInterface::toggleShouldShowTablet() { } void HMDScriptingInterface::setShouldShowTablet(bool value) { - _showTablet = value; - _tabletContextualMode = false; + if (_showTablet != value) { + _showTablet = value; + _tabletContextualMode = false; + emit showTabletChanged(value); + } } QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 2c0a3fe45f..6cc695762b 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -355,6 +355,8 @@ signals: */ bool shouldShowHandControllersChanged(); + void showTabletChanged(bool showTablet); + public: HMDScriptingInterface(); static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine); diff --git a/interface/src/scripting/KeyboardScriptingInterface.cpp b/interface/src/scripting/KeyboardScriptingInterface.cpp new file mode 100644 index 0000000000..b26e1ec378 --- /dev/null +++ b/interface/src/scripting/KeyboardScriptingInterface.cpp @@ -0,0 +1,34 @@ +// +// KeyboardScriptingInterface.cpp +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// Copyright 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 "KeyboardScriptingInterface.h" +#include "ui/Keyboard.h" + +bool KeyboardScriptingInterface::isRaised() { + return DependencyManager::get()->isRaised(); +} + +void KeyboardScriptingInterface::setRaised(bool raised) { + DependencyManager::get()->setRaised(raised); +} + + +bool KeyboardScriptingInterface::isPassword() { + return DependencyManager::get()->isPassword(); +} + +void KeyboardScriptingInterface::setPassword(bool password) { + DependencyManager::get()->setPassword(password); +} + +void KeyboardScriptingInterface::loadKeyboardFile(const QString& keyboardFile) { + DependencyManager::get()->loadKeyboardFile(keyboardFile); +} diff --git a/interface/src/scripting/KeyboardScriptingInterface.h b/interface/src/scripting/KeyboardScriptingInterface.h new file mode 100644 index 0000000000..1ab91ea7c3 --- /dev/null +++ b/interface/src/scripting/KeyboardScriptingInterface.h @@ -0,0 +1,43 @@ +// +// KeyboardScriptingInterface.h +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// Copyright 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 +// + +#ifndef hifi_KeyboardScriptingInterface_h +#define hifi_KeyboardScriptingInterface_h + +#include + +#include "DependencyManager.h" + +/**jsdoc + * The Keyboard API provides facilities to use 3D Physical keyboard. + * @namespace Keyboard + * + * @hifi-interface + * @hifi-client-entity + * + * @property {bool} raised - true If the keyboard is visible false otherwise + * @property {bool} password - true Will show * instead of characters in the text display false otherwise + */ +class KeyboardScriptingInterface : public QObject, public Dependency { + Q_OBJECT + Q_PROPERTY(bool raised READ isRaised WRITE setRaised) + Q_PROPERTY(bool password READ isPassword WRITE setPassword) + +public: + Q_INVOKABLE void loadKeyboardFile(const QString& string); +private: + bool isRaised(); + void setRaised(bool raised); + + bool isPassword(); + void setPassword(bool password); +}; +#endif diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp new file mode 100644 index 0000000000..677d384a17 --- /dev/null +++ b/interface/src/ui/Keyboard.cpp @@ -0,0 +1,799 @@ +// +// Keyboard.cpp +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// Copyright 2018 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 "Keyboard.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ui/overlays/Overlays.h" +#include "ui/overlays/Overlay.h" +#include "ui/overlays/ModelOverlay.h" +#include "ui/overlays/Cube3DOverlay.h" +#include "ui/overlays/Text3DOverlay.h" +#include "avatar/AvatarManager.h" +#include "avatar/MyAvatar.h" +#include "avatar/AvatarManager.h" +#include "raypick/PickScriptingInterface.h" +#include "scripting/HMDScriptingInterface.h" +#include "scripting/WindowScriptingInterface.h" +#include "DependencyManager.h" + +#include "raypick/StylusPointer.h" +#include "GLMHelpers.h" +#include "Application.h" + +static const int LEFT_HAND_CONTROLLER_INDEX = 0; +static const int RIGHT_HAND_CONTROLLER_INDEX = 1; + +static const float MALLET_LENGTH = 0.4f; +static const float MALLET_TOUCH_Y_OFFSET = 0.105f; +static const float MALLET_Y_OFFSET = 0.35f; + +static const glm::quat MALLET_ROTATION_OFFSET{0.70710678f, 0.0f, -0.70710678f, 0.0f}; +static const glm::vec3 MALLET_MODEL_DIMENSIONS{0.05f, MALLET_LENGTH, 0.05f}; +static const glm::vec3 MALLET_POSITION_OFFSET{0.0f, -MALLET_Y_OFFSET / 2.0f, 0.0f}; +static const glm::vec3 MALLET_TIP_OFFSET{0.0f, MALLET_LENGTH - MALLET_TOUCH_Y_OFFSET, 0.0f}; + + +static const glm::vec3 Z_AXIS {0.0f, 0.0f, 1.0f}; +static const glm::vec3 KEYBOARD_TABLET_OFFSET{0.28f, -0.3f, -0.05f}; +static const glm::vec3 KEYBOARD_TABLET_DEGREES_OFFSET{-45.0f, 0.0f, 0.0f}; +static const glm::vec3 KEYBOARD_TABLET_LANDSCAPE_OFFSET{-0.2f, -0.27f, -0.05f}; +static const glm::vec3 KEYBOARD_TABLET_LANDSCAPE_DEGREES_OFFSET{-45.0f, 0.0f, -90.0f}; +static const glm::vec3 KEYBOARD_AVATAR_OFFSET{-0.6f, 0.3f, -0.7f}; +static const glm::vec3 KEYBOARD_AVATAR_DEGREES_OFFSET{0.0f, 180.0f, 0.0f}; + +static const QString SOUND_FILE = PathUtils::resourcesUrl() + "sounds/keyboard_key.mp3"; +static const QString MALLET_MODEL_URL = PathUtils::resourcesUrl() + "meshes/drumstick.fbx"; + +static const float PULSE_STRENGTH = 0.6f; +static const float PULSE_DURATION = 3.0f; + +static const int KEY_PRESS_TIMEOUT_MS = 100; +static const int LAYER_SWITCH_TIMEOUT_MS = 200; + +static const QString CHARACTER_STRING = "character"; +static const QString CAPS_STRING = "caps"; +static const QString CLOSE_STRING = "close"; +static const QString LAYER_STRING = "layer"; +static const QString BACKSPACE_STRING = "backspace"; +static const QString SPACE_STRING = "space"; +static const QString ENTER_STRING = "enter"; + +std::pair calculateKeyboardPositionAndOrientation() { + auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto hmd = DependencyManager::get(); + + std::pair keyboardLocation = std::make_pair(glm::vec3(), glm::quat()); + float sensorToWorldScale = myAvatar->getSensorToWorldScale(); + QUuid tabletID = hmd->getCurrentTabletFrameID(); + if (!tabletID.isNull() && hmd->getShouldShowTablet()) { + Overlays& overlays = qApp->getOverlays(); + auto tabletOverlay = std::dynamic_pointer_cast(overlays.getOverlay(tabletID)); + if (tabletOverlay) { + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + bool landscapeMode = tablet->getLandscape(); + glm::vec3 keyboardOffset = landscapeMode ? KEYBOARD_TABLET_LANDSCAPE_OFFSET : KEYBOARD_TABLET_OFFSET; + glm::vec3 keyboardDegreesOffset = landscapeMode ? KEYBOARD_TABLET_LANDSCAPE_DEGREES_OFFSET : KEYBOARD_TABLET_DEGREES_OFFSET; + glm::vec3 tabletWorldPosition = tabletOverlay->getWorldPosition(); + glm::quat tabletWorldOrientation = tabletOverlay->getWorldOrientation(); + glm::vec3 scaledKeyboardTabletOffset = keyboardOffset * sensorToWorldScale; + + keyboardLocation.first = tabletWorldPosition + (tabletWorldOrientation * scaledKeyboardTabletOffset); + keyboardLocation.second = tabletWorldOrientation * glm::quat(glm::radians(keyboardDegreesOffset)); + } + + } else { + glm::vec3 avatarWorldPosition = myAvatar->getWorldPosition(); + glm::quat avatarWorldOrientation = myAvatar->getWorldOrientation(); + glm::vec3 scaledKeyboardAvatarOffset = KEYBOARD_AVATAR_OFFSET * sensorToWorldScale; + + keyboardLocation.first = avatarWorldPosition + (avatarWorldOrientation * scaledKeyboardAvatarOffset); + keyboardLocation.second = avatarWorldOrientation * glm::quat(glm::radians(KEYBOARD_AVATAR_DEGREES_OFFSET)); + } + + return keyboardLocation; +} + +void Key::saveDimensionsAndLocalPosition() { + Overlays& overlays = qApp->getOverlays(); + auto model3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_keyID)); + + if (model3DOverlay) { + _originalLocalPosition = model3DOverlay->getLocalPosition(); + _originalDimensions = model3DOverlay->getDimensions(); + _currentLocalPosition = _originalLocalPosition; + } +} + +void Key::scaleKey(float sensorToWorldScale) { + Overlays& overlays = qApp->getOverlays(); + auto model3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_keyID)); + + if (model3DOverlay) { + glm::vec3 scaledLocalPosition = _originalLocalPosition * sensorToWorldScale; + glm::vec3 scaledDimensions = _originalDimensions * sensorToWorldScale; + _currentLocalPosition = scaledLocalPosition; + + QVariantMap properties { + { "dimensions", vec3toVariant(scaledDimensions) }, + { "localPosition", vec3toVariant(scaledLocalPosition) } + }; + + overlays.editOverlay(_keyID, properties); + } +} + +void Key::startTimer(int time) { + if (_timer) { + _timer->start(time); + _timer->setSingleShot(true); + } +} + +bool Key::timerFinished() { + if (_timer) { + return (_timer->remainingTime() <= 0); + } + return false; +} + +QString Key::getKeyString(bool toUpper) const { + return toUpper ? _keyString.toUpper() : _keyString; +} + +int Key::getScanCode(bool toUpper) const { + QString character = toUpper ? _keyString.toUpper() : _keyString; + auto utf8Key = character.toUtf8(); + return (int)utf8Key[0]; +} + +Key::Type Key::getKeyTypeFromString(const QString& keyTypeString) { + if (keyTypeString == SPACE_STRING) { + return Type::SPACE; + } else if (keyTypeString == BACKSPACE_STRING) { + return Type::BACKSPACE; + } else if (keyTypeString == LAYER_STRING) { + return Type::LAYER; + } else if (keyTypeString == CAPS_STRING) { + return Type::CAPS; + } else if (keyTypeString == CLOSE_STRING) { + return Type::CLOSE; + } else if (keyTypeString == ENTER_STRING) { + return Type::ENTER; + } + + return Type::CHARACTER; +} + +Keyboard::Keyboard() { + auto pointerManager = DependencyManager::get(); + auto windowScriptingInterface = DependencyManager::get(); + auto myAvatar = DependencyManager::get()->getMyAvatar(); + connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Keyboard::handleTriggerBegin, Qt::QueuedConnection); + connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Keyboard::handleTriggerContinue, Qt::QueuedConnection); + connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Keyboard::handleTriggerEnd, Qt::QueuedConnection); + connect(myAvatar.get(), &MyAvatar::sensorToWorldScaleChanged, this, &Keyboard::scaleKeyboard, Qt::QueuedConnection); + connect(windowScriptingInterface.data(), &WindowScriptingInterface::domainChanged, [&]() { setRaised(false); }); +} + + +void Keyboard::createKeyboard() { + auto pointerManager = DependencyManager::get(); + + QVariantMap modelProperties { + { "url", MALLET_MODEL_URL } + }; + + QVariantMap leftStylusProperties { + { "hand", LEFT_HAND_CONTROLLER_INDEX }, + { "filter", PickScriptingInterface::PICK_OVERLAYS() }, + { "model", modelProperties }, + { "tipOffset", vec3toVariant(MALLET_TIP_OFFSET) } + }; + + QVariantMap rightStylusProperties { + { "hand", RIGHT_HAND_CONTROLLER_INDEX }, + { "filter", PickScriptingInterface::PICK_OVERLAYS() }, + { "model", modelProperties }, + { "tipOffset", vec3toVariant(MALLET_TIP_OFFSET) } + }; + + _leftHandStylus = pointerManager->addPointer(std::make_shared(leftStylusProperties, StylusPointer::buildStylusOverlay(leftStylusProperties), true, true, + MALLET_POSITION_OFFSET, MALLET_ROTATION_OFFSET, MALLET_MODEL_DIMENSIONS)); + _rightHandStylus = pointerManager->addPointer(std::make_shared(rightStylusProperties, StylusPointer::buildStylusOverlay(rightStylusProperties), true, true, + MALLET_POSITION_OFFSET, MALLET_ROTATION_OFFSET, MALLET_MODEL_DIMENSIONS)); + + pointerManager->disablePointer(_rightHandStylus); + pointerManager->disablePointer(_leftHandStylus); + + QString keyboardSvg = PathUtils::resourcesUrl() + "config/keyboard.json"; + loadKeyboardFile(keyboardSvg); + + _keySound = DependencyManager::get()->getSound(SOUND_FILE); +} + +bool Keyboard::isRaised() const { + return resultWithReadLock([&] { return _raised; }); +} + +void Keyboard::setRaised(bool raised) { + + bool isRaised; + withReadLock([&] { isRaised = _raised; }); + if (isRaised != raised) { + raiseKeyboardAnchor(raised); + raiseKeyboard(raised); + raised ? enableStylus() : disableStylus(); + withWriteLock([&] { + _raised = raised; + _layerIndex = 0; + _capsEnabled = false; + _typedCharacters.clear(); + }); + + updateTextDisplay(); + } +} + +void Keyboard::updateTextDisplay() { + Overlays& overlays = qApp->getOverlays(); + + auto myAvatar = DependencyManager::get()->getMyAvatar(); + float sensorToWorldScale = myAvatar->getSensorToWorldScale(); + float textWidth = (float) overlays.textSize(_textDisplay.overlayID, _typedCharacters).width(); + + glm::vec3 scaledDimensions = _textDisplay.dimensions; + scaledDimensions *= sensorToWorldScale; + float leftMargin = (scaledDimensions.x / 2); + scaledDimensions.x += textWidth; + + + QVariantMap textDisplayProperties { + { "dimensions", vec3toVariant(scaledDimensions) }, + { "leftMargin", leftMargin }, + { "text", _typedCharacters }, + { "lineHeight", (_textDisplay.lineHeight * sensorToWorldScale) } + }; + + overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); +} + +void Keyboard::raiseKeyboardAnchor(bool raise) const { + Overlays& overlays = qApp->getOverlays(); + OverlayID anchorOverlayID = _anchor.overlayID; + auto anchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(anchorOverlayID)); + if (anchorOverlay) { + std::pair keyboardLocation = calculateKeyboardPositionAndOrientation(); + anchorOverlay->setWorldPosition(keyboardLocation.first); + anchorOverlay->setWorldOrientation(keyboardLocation.second); + anchorOverlay->setVisible(raise); + + QVariantMap textDisplayProperties { + { "visible", raise } + }; + + overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); + } +} + +void Keyboard::scaleKeyboard(float sensorToWorldScale) { + Overlays& overlays = qApp->getOverlays(); + + glm::vec3 scaledDimensions = _anchor.originalDimensions * sensorToWorldScale; + auto volume3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(_anchor.overlayID)); + + if (volume3DOverlay) { + volume3DOverlay->setDimensions(scaledDimensions); + } + + for (auto& keyboardLayer: _keyboardLayers) { + for (auto& key: keyboardLayer) { + key.scaleKey(sensorToWorldScale); + } + } + + + + glm::vec3 scaledLocalPosition = _textDisplay.localPosition * sensorToWorldScale; + glm::vec3 textDisplayScaledDimensions = _textDisplay.dimensions * sensorToWorldScale; + + QVariantMap textDisplayProperties { + { "localPosition", vec3toVariant(scaledLocalPosition) }, + { "dimensions", vec3toVariant(textDisplayScaledDimensions) }, + { "lineHeight", (_textDisplay.lineHeight * sensorToWorldScale) } + }; + + overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties); +} + +void Keyboard::startLayerSwitchTimer() { + if (_layerSwitchTimer) { + _layerSwitchTimer->start(LAYER_SWITCH_TIMEOUT_MS); + _layerSwitchTimer->setSingleShot(true); + } +} + +bool Keyboard::isLayerSwitchTimerFinished() { + if (_layerSwitchTimer) { + return (_layerSwitchTimer->remainingTime() <= 0); + } + return false; +} + +void Keyboard::raiseKeyboard(bool raise) const { + if (_keyboardLayers.empty()) { + return; + } + Overlays& overlays = qApp->getOverlays(); + for (const auto& key: _keyboardLayers[_layerIndex]) { + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(key.getID())); + if (base3DOverlay) { + base3DOverlay->setVisible(raise); + } + } +} + +bool Keyboard::isPassword() const { + return resultWithReadLock([&] { return _password; }); +} + +void Keyboard::setPassword(bool password) { + if (_password != password) { + withWriteLock([&] { + _password = password; + _typedCharacters.clear(); + }); + } + + updateTextDisplay(); +} + +void Keyboard::switchToLayer(int layerIndex) { + if (layerIndex >= 0 && layerIndex < (int)_keyboardLayers.size()) { + Overlays& overlays = qApp->getOverlays(); + + OverlayID currentAnchorOverlayID = _anchor.overlayID; + + glm::vec3 currentOverlayPosition; + glm::quat currentOverlayOrientation; + + auto currentAnchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(currentAnchorOverlayID)); + if (currentAnchorOverlay) { + currentOverlayPosition = currentAnchorOverlay->getWorldPosition(); + currentOverlayOrientation = currentAnchorOverlay->getWorldOrientation(); + } + + raiseKeyboardAnchor(false); + raiseKeyboard(false); + + setLayerIndex(layerIndex); + + raiseKeyboardAnchor(true); + raiseKeyboard(true); + + OverlayID newAnchorOverlayID = _anchor.overlayID; + auto newAnchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(newAnchorOverlayID)); + if (newAnchorOverlay) { + newAnchorOverlay->setWorldPosition(currentOverlayPosition); + newAnchorOverlay->setWorldOrientation(currentOverlayOrientation); + } + + startLayerSwitchTimer(); + } +} + +void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + auto buttonType = event.getButton(); + + for (auto index = _keyboardLayers[_layerIndex].begin(); index != _keyboardLayers[_layerIndex].end(); index++) { + Key& key = *index; + if (key.getID() == overlayID && (pointerID == _leftHandStylus || pointerID == _rightHandStylus) && + buttonType == PointerEvent::PrimaryButton) { + + + if (key.timerFinished()) { + + auto handIndex = (pointerID == _leftHandStylus) ? controller::Hand::LEFT : controller::Hand::RIGHT; + auto userInputMapper = DependencyManager::get(); + userInputMapper->triggerHapticPulse(PULSE_STRENGTH, PULSE_DURATION, handIndex); + + Overlays& overlays = qApp->getOverlays(); + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + + glm::vec3 keyWorldPosition; + if (base3DOverlay) { + keyWorldPosition = base3DOverlay->getWorldPosition(); + } + + AudioInjectorOptions audioOptions; + audioOptions.localOnly = true; + audioOptions.position = keyWorldPosition; + audioOptions.volume = 0.4f; + + AudioInjector::playSound(_keySound->getByteArray(), audioOptions); + + int scanCode = key.getScanCode(_capsEnabled); + QString keyString = key.getKeyString(_capsEnabled); + + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + + switch (key.getKeyType()) { + case Key::Type::CLOSE: + setRaised(false); + tablet->unfocus(); + return; + + case Key::Type::CAPS: + _capsEnabled = !_capsEnabled; + switchToLayer(key.getSwitchToLayerIndex()); + return; + case Key::Type::LAYER: + _capsEnabled = false; + switchToLayer(key.getSwitchToLayerIndex()); + return; + case Key::Type::BACKSPACE: + scanCode = Qt::Key_Backspace; + keyString = "\x08"; + _typedCharacters = _typedCharacters.left(_typedCharacters.length() -1); + updateTextDisplay(); + break; + case Key::Type::ENTER: + scanCode = Qt::Key_Return; + keyString = "\x0d"; + _typedCharacters.clear(); + updateTextDisplay(); + break; + case Key::Type::CHARACTER: + if (keyString != " ") { + _typedCharacters.push_back((_password ? "*" : keyString)); + } else { + _typedCharacters.clear(); + } + updateTextDisplay(); + break; + + default: + break; + } + + QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString); + QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString); + QCoreApplication::postEvent(QCoreApplication::instance(), pressEvent); + QCoreApplication::postEvent(QCoreApplication::instance(), releaseEvent); + + key.startTimer(KEY_PRESS_TIMEOUT_MS); + } + + break; + } + } +} + +void Keyboard::handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + + for (auto index = _keyboardLayers[_layerIndex].begin(); index != _keyboardLayers[_layerIndex].end(); index++) { + Key& key = *index; + + if (key.getID() == overlayID && (pointerID == _leftHandStylus || pointerID == _rightHandStylus)) { + Overlays& overlays = qApp->getOverlays(); + + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + + if (base3DOverlay) { + base3DOverlay->setLocalPosition(key.getCurrentLocalPosition()); + } + + key.setIsPressed(false); + if (key.timerFinished()) { + key.startTimer(KEY_PRESS_TIMEOUT_MS); + } + break; + } + } + +} + +void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + + for (auto index = _keyboardLayers[_layerIndex].begin(); index != _keyboardLayers[_layerIndex].end(); index++) { + Key& key = *index; + + if (key.getID() == overlayID && (pointerID == _leftHandStylus || pointerID == _rightHandStylus)) { + Overlays& overlays = qApp->getOverlays(); + + if (!key.isPressed()) { + auto pointerManager = DependencyManager::get(); + auto pickResult = pointerManager->getPrevPickResult(pointerID); + + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + + if (base3DOverlay) { + auto pickResultVariant = pickResult->toVariantMap(); + auto stylusTipVariant = pickResultVariant["stylusTip"]; + auto stylusTipPositionVariant = stylusTipVariant.toMap()["position"]; + glm::vec3 stylusTipPosition = vec3FromVariant(stylusTipPositionVariant); + + glm::quat overlayOrientation = base3DOverlay->getWorldOrientation(); + glm::vec3 overlayPosition = base3DOverlay->getWorldPosition(); + + glm::mat4 overlayWorldMat = createMatFromQuatAndPos(overlayOrientation, overlayPosition); + glm::mat4 overlayWorldToLocalMat = glm::inverse(overlayWorldMat); + + glm::vec3 stylusTipInOverlaySpace = transformPoint(overlayWorldToLocalMat, stylusTipPosition); + + static const float PENATRATION_THRESHOLD = 0.025f; + if (stylusTipInOverlaySpace.z < PENATRATION_THRESHOLD) { + static const float Z_OFFSET = 0.002f; + glm::vec3 overlayYAxis = overlayOrientation * Z_AXIS; + glm::vec3 overlayYOffset = overlayYAxis * Z_OFFSET; + glm::vec3 localPosition = key.getCurrentLocalPosition() - overlayYOffset; + base3DOverlay->setLocalPosition(localPosition); + key.setIsPressed(true); + } + } + } + break; + } + } +} + +void Keyboard::disableStylus() { + auto pointerManager = DependencyManager::get(); + pointerManager->setRenderState(_leftHandStylus, "events off"); + pointerManager->disablePointer(_leftHandStylus); + pointerManager->setRenderState(_rightHandStylus, "events off"); + pointerManager->disablePointer(_rightHandStylus); +} + +void Keyboard::setLayerIndex(int layerIndex) { + if (layerIndex >= 0 && layerIndex < (int)_keyboardLayers.size()) { + _layerIndex = layerIndex; + } else { + _layerIndex = 0; + } +} + +void Keyboard::loadKeyboardFile(const QString& keyboardFile) { + if (keyboardFile.isEmpty()) { + return; + } + + auto request = DependencyManager::get()->createResourceRequest(this, keyboardFile); + + if (!request) { + qCWarning(interfaceapp) << "Could not create resource for Keyboard file" << keyboardFile; + } + + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() != ResourceRequest::Success) { + qCWarning(interfaceapp) << "Keyboard file failed to download"; + return; + } + + clearKeyboardKeys(); + Overlays& overlays = qApp->getOverlays(); + auto requestData = request->getData(); + + QVector includeItems; + + QJsonParseError parseError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(requestData, &parseError); + + if (parseError.error != QJsonParseError::NoError) { + qCWarning(interfaceapp) << "Failed to parse keyboard json file - Error: " << parseError.errorString(); + return; + } + QJsonObject jsonObject = jsonDoc.object(); + QJsonArray layer = jsonObject["Layer1"].toArray(); + QJsonObject anchorObject = jsonObject["anchor"].toObject(); + bool useResourcePath = jsonObject["useResourcesPath"].toBool(); + QString resourcePath = PathUtils::resourcesUrl(); + + + if (anchorObject.isEmpty()) { + qCWarning(interfaceapp) << "No Anchor specified. Not creating keyboard"; + return; + } + + QVariantMap anchorProperties { + { "name", "KeyboardAnchor"}, + { "isSolid", true }, + { "visible", false }, + { "grabbable", true }, + { "ignoreRayIntersection", false }, + { "dimensions", anchorObject["dimensions"].toVariant() }, + { "position", anchorObject["position"].toVariant() }, + { "orientation", anchorObject["rotation"].toVariant() } + }; + + glm::vec3 dimensions = vec3FromVariant(anchorObject["dimensions"].toVariant()); + + Anchor anchor; + anchor.overlayID = overlays.addOverlay("cube", anchorProperties); + anchor.originalDimensions = dimensions; + _anchor = anchor; + + const QJsonArray& keyboardLayers = jsonObject["layers"].toArray(); + int keyboardLayerCount = keyboardLayers.size(); + _keyboardLayers.reserve(keyboardLayerCount); + + + for (int keyboardLayerIndex = 0; keyboardLayerIndex < keyboardLayerCount; keyboardLayerIndex++) { + const QJsonValue& keyboardLayer = keyboardLayers[keyboardLayerIndex].toArray(); + + std::vector keyboardLayerKeys; + foreach (const QJsonValue& keyboardKeyValue, keyboardLayer.toArray()) { + + QVariantMap textureMap; + if (!keyboardKeyValue["texture"].isNull()) { + textureMap = keyboardKeyValue["texture"].toObject().toVariantMap(); + + if (useResourcePath) { + for (auto iter = textureMap.begin(); iter != textureMap.end(); iter++) { + QString modifiedPath = resourcePath + iter.value().toString(); + textureMap[iter.key()] = modifiedPath; + } + } + } + + QString modelUrl = keyboardKeyValue["modelURL"].toString(); + QString url = (useResourcePath ? (resourcePath + modelUrl) : modelUrl); + + QVariantMap properties { + { "dimensions", keyboardKeyValue["dimensions"].toVariant() }, + { "position", keyboardKeyValue["position"].toVariant() }, + { "visible", false }, + { "isSolid", true }, + { "emissive", true }, + { "parentID", _anchor.overlayID }, + { "url", url }, + { "textures", textureMap }, + { "grabbable", false }, + { "localOrientation", keyboardKeyValue["localOrientation"].toVariant() } + }; + + OverlayID overlayID = overlays.addOverlay("model", properties); + + QString keyType = keyboardKeyValue["type"].toString(); + QString keyString = keyboardKeyValue["key"].toString(); + + Key key; + if (!keyType.isNull()) { + Key::Type type= Key::getKeyTypeFromString(keyType); + key.setKeyType(type); + + if (type == Key::Type::LAYER || type == Key::Type::CAPS) { + int switchToLayer = keyboardKeyValue["switchToLayer"].toInt(); + key.setSwitchToLayerIndex(switchToLayer); + } + } + key.setID(overlayID); + key.setKeyString(keyString); + key.saveDimensionsAndLocalPosition(); + + includeItems.append(key.getID()); + _itemsToIgnore.append(key.getID()); + keyboardLayerKeys.push_back(key); + } + + _keyboardLayers.push_back(keyboardLayerKeys); + + } + + TextDisplay textDisplay; + QJsonObject displayTextObject = jsonObject["textDisplay"].toObject(); + + QVariantMap displayTextProperties { + { "dimensions", displayTextObject["dimensions"].toVariant() }, + { "localPosition", displayTextObject["localPosition"].toVariant() }, + { "localOrientation", displayTextObject["localOrientation"].toVariant() }, + { "leftMargin", displayTextObject["leftMargin"].toVariant() }, + { "rightMargin", displayTextObject["rightMargin"].toVariant() }, + { "topMargin", displayTextObject["topMargin"].toVariant() }, + { "bottomMargin", displayTextObject["bottomMargin"].toVariant() }, + { "lineHeight", displayTextObject["lineHeight"].toVariant() }, + { "visible", false }, + { "emissive", true }, + { "grabbable", false }, + { "text", ""}, + { "parentID", _anchor.overlayID } + }; + + textDisplay.overlayID = overlays.addOverlay("text3d", displayTextProperties); + textDisplay.localPosition = vec3FromVariant(displayTextObject["localPosition"].toVariant()); + textDisplay.dimensions = vec3FromVariant(displayTextObject["dimensions"].toVariant()); + textDisplay.lineHeight = (float) displayTextObject["lineHeight"].toDouble(); + + _textDisplay = textDisplay; + + _ignoreItemsLock.withWriteLock([&] { + _itemsToIgnore.push_back(_textDisplay.overlayID); + _itemsToIgnore.push_back(_anchor.overlayID); + }); + _layerIndex = 0; + auto pointerManager = DependencyManager::get(); + pointerManager->setIncludeItems(_leftHandStylus, includeItems); + pointerManager->setIncludeItems(_rightHandStylus, includeItems); + }); + + request->send(); +} + +QVector Keyboard::getKeysID() { + return _ignoreItemsLock.resultWithReadLock>([&] { + return _itemsToIgnore; + }); +} + +void Keyboard::clearKeyboardKeys() { + Overlays& overlays = qApp->getOverlays(); + + for (const auto& keyboardLayer: _keyboardLayers) { + for (const Key& key: keyboardLayer) { + overlays.deleteOverlay(key.getID()); + } + } + + overlays.deleteOverlay(_anchor.overlayID); + overlays.deleteOverlay(_textDisplay.overlayID); + + _keyboardLayers.clear(); + + _ignoreItemsLock.withWriteLock([&] { + _itemsToIgnore.clear(); + }); +} + +void Keyboard::enableStylus() { + auto pointerManager = DependencyManager::get(); + pointerManager->setRenderState(_leftHandStylus, "events on"); + pointerManager->enablePointer(_leftHandStylus); + pointerManager->setRenderState(_rightHandStylus, "events on"); + pointerManager->enablePointer(_rightHandStylus); + +} diff --git a/interface/src/ui/Keyboard.h b/interface/src/ui/Keyboard.h new file mode 100644 index 0000000000..662a51c2da --- /dev/null +++ b/interface/src/ui/Keyboard.h @@ -0,0 +1,152 @@ +// +// Keyboard.h +// interface/src/scripting +// +// Created by Dante Ruiz on 2018-08-27. +// Copyright 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 +// + +#ifndef hifi_Keyboard_h +#define hifi_Keyboard_h + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ui/overlays/Overlay.h" + +class PointerEvent; + + +class Key { +public: + Key() = default; + ~Key() = default; + + enum Type { + CHARACTER, + CAPS, + CLOSE, + LAYER, + BACKSPACE, + SPACE, + ENTER + }; + + + static Key::Type getKeyTypeFromString(const QString& keyTypeString); + + OverlayID getID() const { return _keyID; } + void setID(OverlayID overlayID) { _keyID = overlayID; } + + void startTimer(int time); + bool timerFinished(); + + void setKeyString(QString keyString) { _keyString = keyString; } + QString getKeyString(bool toUpper) const; + int getScanCode(bool toUpper) const; + + bool isPressed() const { return _pressed; } + void setIsPressed(bool pressed) { _pressed = pressed; } + + void setSwitchToLayerIndex(int layerIndex) { _switchToLayer = layerIndex; } + int getSwitchToLayerIndex() const { return _switchToLayer; } + + Type getKeyType() const { return _type; } + void setKeyType(Type type) { _type = type; } + + glm::vec3 getCurrentLocalPosition() const { return _currentLocalPosition; } + + void saveDimensionsAndLocalPosition(); + + void scaleKey(float sensorToWorldScale); +private: + Type _type { Type::CHARACTER }; + + int _switchToLayer { 0 }; + bool _pressed { false }; + + OverlayID _keyID; + QString _keyString; + + glm::vec3 _originalLocalPosition; + glm::vec3 _originalDimensions; + glm::vec3 _currentLocalPosition; + + std::shared_ptr _timer { std::make_shared() }; +}; + +class Keyboard : public Dependency, public QObject, public ReadWriteLockable { +public: + Keyboard(); + void createKeyboard(); + bool isRaised() const; + void setRaised(bool raised); + + bool isPassword() const; + void setPassword(bool password); + + void loadKeyboardFile(const QString& keyboardFile); + QVector getKeysID(); + +public slots: + void handleTriggerBegin(const OverlayID& overlayID, const PointerEvent& event); + void handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& event); + void handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event); + void scaleKeyboard(float sensorToWorldScale); + +private: + struct Anchor { + OverlayID overlayID; + glm::vec3 originalDimensions; + }; + + struct TextDisplay { + float lineHeight; + OverlayID overlayID; + glm::vec3 localPosition; + glm::vec3 dimensions; + }; + + void raiseKeyboard(bool raise) const; + void raiseKeyboardAnchor(bool raise) const; + + void setLayerIndex(int layerIndex); + void enableStylus(); + void disableStylus(); + void clearKeyboardKeys(); + void switchToLayer(int layerIndex); + void updateTextDisplay(); + + void startLayerSwitchTimer(); + bool isLayerSwitchTimerFinished(); + + bool _raised { false }; + bool _password { false }; + bool _capsEnabled { false }; + int _layerIndex { 0 }; + unsigned int _leftHandStylus { 0 }; + unsigned int _rightHandStylus { 0 }; + SharedSoundPointer _keySound { nullptr }; + std::shared_ptr _layerSwitchTimer { std::make_shared() }; + + QString _typedCharacters; + TextDisplay _textDisplay; + Anchor _anchor; + + mutable ReadWriteLockable _ignoreItemsLock; + QVector _itemsToIgnore; + std::vector> _keyboardLayers; +}; + +#endif diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 190d9c3895..fd3ff69691 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -766,4 +766,4 @@ render::ItemKey ModelOverlay::getKey() { builder.withMetaCullGroup(); } return builder.build(); -} \ No newline at end of file +} diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index f4c98bb9e0..35228c6247 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -35,6 +35,7 @@ #include "RectangleOverlay.h" #include "Text3DOverlay.h" #include "Web3DOverlay.h" +#include "ui/Keyboard.h" #include #include @@ -868,8 +869,13 @@ void Overlays::mousePressPointerEvent(const OverlayID& overlayID, const PointerE QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit mousePressOnOverlay(overlayID, event); + + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit mousePressOnOverlay(overlayID, event); + } } bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { @@ -902,8 +908,12 @@ void Overlays::hoverEnterPointerEvent(const OverlayID& overlayID, const PointerE QMetaObject::invokeMethod(thisOverlay.get(), "hoverEnterOverlay", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit hoverEnterOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit hoverEnterOverlay(overlayID, event); + } } void Overlays::hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event) { @@ -917,8 +927,12 @@ void Overlays::hoverOverPointerEvent(const OverlayID& overlayID, const PointerEv QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit hoverOverOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit hoverOverOverlay(overlayID, event); + } } void Overlays::hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event) { @@ -932,8 +946,12 @@ void Overlays::hoverLeavePointerEvent(const OverlayID& overlayID, const PointerE QMetaObject::invokeMethod(thisOverlay.get(), "hoverLeaveOverlay", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit hoverLeaveOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit hoverLeaveOverlay(overlayID, event); + } } bool Overlays::mouseReleaseEvent(QMouseEvent* event) { @@ -962,8 +980,12 @@ void Overlays::mouseReleasePointerEvent(const OverlayID& overlayID, const Pointe QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit mouseReleaseOnOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit mouseReleaseOnOverlay(overlayID, event); + } } bool Overlays::mouseMoveEvent(QMouseEvent* event) { @@ -1014,8 +1036,13 @@ void Overlays::mouseMovePointerEvent(const OverlayID& overlayID, const PointerEv QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); } - // emit to scripts - emit mouseMoveOnOverlay(overlayID, event); + auto keyboard = DependencyManager::get(); + + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeysID().contains(overlayID)) { + // emit to scripts + emit mouseMoveOnOverlay(overlayID, event); + } } QVector Overlays::findOverlays(const glm::vec3& center, float radius) { @@ -1035,7 +1062,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { OverlayID thisID = i.key(); auto overlay = std::dynamic_pointer_cast(i.value()); // FIXME: this ignores overlays with ignorePickIntersection == true, which seems wrong - if (overlay && overlay->getVisible() && !overlay->getIgnorePickIntersection() && overlay->isLoaded()) { + if (overlay && overlay->getVisible() && overlay->isLoaded()) { // get AABox in frame of overlay glm::vec3 dimensions = overlay->getDimensions(); glm::vec3 low = dimensions * -0.5f; diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index f13d25f22c..e7a0c5934e 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -41,6 +41,7 @@ #include "scripting/AssetMappingsScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" +#include "scripting/KeyboardScriptingInterface.h" #include #include #include @@ -273,6 +274,7 @@ void Web3DOverlay::setupQmlSurface(bool isTablet) { _webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("WalletScriptingInterface", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("ResourceRequestObserver", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("KeyboardScriptingInterface", DependencyManager::get().data()); // Override min fps for tablet UI, for silky smooth scrolling setMaxFPS(90); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6f285a9f0c..7da7a45e83 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1180,9 +1180,9 @@ int Model::getLastFreeJointIndex(int jointIndex) const { void Model::setTextures(const QVariantMap& textures) { if (isLoaded()) { - _pendingTextures.clear(); _needsFixupInScene = true; _renderGeometry->setTextures(textures); + _pendingTextures.clear(); } else { _pendingTextures = textures; } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 1081f8c4e7..52d359ad0d 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -888,6 +888,12 @@ void TabletProxy::desktopWindowClosed() { gotoHomeScreen(); } +void TabletProxy::unfocus() { + if (_qmlOffscreenSurface) { + _qmlOffscreenSurface->lowerKeyboard(); + } +} + QQuickItem* TabletProxy::getQmlTablet() const { if (!_qmlTabletRoot) { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 2d37402d01..9821ad1263 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -232,6 +232,7 @@ public: const QString getName() const { return _name; } bool getToolbarMode() const { return _toolbarMode; } void setToolbarMode(bool toolbarMode); + void unfocus(); /**jsdoc * @function TabletProxy#gotoMenuScreen diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 2b17f447a0..6adfa88fb2 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -175,6 +175,23 @@ Script.include("/~/system/libraries/utils.js"); return this.exitModule(); } } + + var stopRunning = false; + + if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0)) { + var stopRunning = false; + controllerData.nearbyOverlayIDs[this.hand].forEach(function(overlayID) { + var overlayName = Overlays.getProperty(overlayID, "name"); + if (overlayName === "KeyboardAnchor") { + stopRunning = true; + } + }); + + if (stopRunning) { + return this.exitModule(); + } + } + this.sendPickData(controllerData); return this.isReady(controllerData); }; diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 763a0a0a27..9bddeb236a 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -46,6 +46,10 @@ Script.include("/~/system/libraries/utils.js"); return this.getOtherModule().thisHandIsParent(props); }; + this.isGrabbedThingVisible = function() { + return Overlays.getProperty(this.grabbedThingID, "visible"); + }; + this.thisHandIsParent = function(props) { if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== MyAvatar.SELF_ID) { return false; @@ -198,7 +202,7 @@ Script.include("/~/system/libraries/utils.js"); }; this.run = function (controllerData) { - if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { + if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) || !this.isGrabbedThingVisible()) { this.endNearParentingGrabOverlay(); this.robbed = false; return makeRunningValues(false, [], []); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 8bfd776971..74c1e4baf0 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -726,9 +726,14 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { filterText = ""; } + var wasIsOpen = ui.isOpen; ui.isOpen = (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen; ui.buttonActive(ui.isOpen); + if (wasIsOpen !== ui.isOpen && Keyboard.raised) { + Keyboard.raised = false; + } + if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { ContextOverlay.isInMarketplaceInspectionMode = true; } else { From f664421fe07ddb4603cccbf36ba862fc44141d59 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 31 Oct 2018 17:44:20 -0700 Subject: [PATCH 124/125] addressing code review feedback --- interface/src/Menu.cpp | 2 +- .../src/raypick/PointerScriptingInterface.cpp | 10 +- interface/src/raypick/RayPick.cpp | 6 +- interface/src/raypick/StylusPick.cpp | 4 +- interface/src/raypick/StylusPick.h | 3 - interface/src/raypick/StylusPointer.cpp | 4 +- interface/src/ui/Keyboard.cpp | 262 +++++++++--------- interface/src/ui/Keyboard.h | 4 +- interface/src/ui/overlays/Overlays.cpp | 7 +- libraries/shared/src/RegisteredMetaTypes.h | 10 +- 10 files changed, 160 insertions(+), 152 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2ca997a1fc..1fc1e0c033 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -356,7 +356,7 @@ Menu::Menu() { qApp->setHmdTabletBecomesToolbarSetting(action->isChecked()); }); - addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::Use3DKeyboard, 0, false); + addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::Use3DKeyboard, 0, true); // Developer > Render >>> MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index f0edb4d9ef..1893132917 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -56,8 +56,16 @@ unsigned int PointerScriptingInterface::createPointer(const PickQuery::PickType& * @property {boolean} [hover=false] If this pointer should generate hover events. * @property {boolean} [enabled=false] * @property {Vec3} [tipOffset] The specified offset of the from the joint index. - * @property {object} [model] Data to replace the default model url, positionOffset and rotationOffset. + * @property {Pointers.StylusPointerProperties.model} [model] Data to replace the default model url, positionOffset and rotationOffset. {@link Pointers.StylusPointerProperties.model} */ + /**jsdoc + * properties defining stylus pick model that can be included to {@link Pointers.StylusPointerProperties} + * @typedef {object} Pointers.StylusPointerProperties.model + * @property {string} [url] url to the model + * @property {Vec3} [dimensions] the dimensions of the model + * @property {Vec3} [positionOffset] the position offset of the model from the parent joint index + * @property {Vec3} [rotationOffset] the rotation offset of the model from the parent joint index + */ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) const { QVariantMap propertyMap = properties.toMap(); diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index b6adba4a12..a48d858504 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -10,7 +10,6 @@ #include "Application.h" #include "EntityScriptingInterface.h" #include "ui/overlays/Overlays.h" -#include "ui/Keyboard.h" #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" @@ -41,12 +40,9 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) { bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); - auto keyboard = DependencyManager::get(); - QVector ignoreItems = keyboard->getKeysID(); - ignoreItems.append(getIgnoreItemsAs()); RayToOverlayIntersectionResult overlayRes = qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking, - getIncludeItemsAs(), ignoreItems, !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); + getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (overlayRes.intersects) { return std::make_shared(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); } else { diff --git a/interface/src/raypick/StylusPick.cpp b/interface/src/raypick/StylusPick.cpp index 0416563272..0a76180be8 100644 --- a/interface/src/raypick/StylusPick.cpp +++ b/interface/src/raypick/StylusPick.cpp @@ -61,7 +61,7 @@ bool StylusPickResult::checkOrFilterAgainstMaxDistance(float maxDistance) { } StylusPick::StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled, const glm::vec3& tipOffset) : - Pick(StylusTip(side), filter, maxDistance, enabled), _tipOffset(tipOffset) + Pick(StylusTip(side, tipOffset), filter, maxDistance, enabled) { } @@ -127,7 +127,7 @@ StylusTip StylusPick::getMathematicalPick() const { if (qApp->getPreferAvatarFingerOverStylus()) { result = getFingerWorldLocation(_mathPick.side); } else { - result = getControllerWorldLocation(_mathPick.side, _tipOffset); + result = getControllerWorldLocation(_mathPick.side, _mathPick.tipOffset); } return result; } diff --git a/interface/src/raypick/StylusPick.h b/interface/src/raypick/StylusPick.h index 3e0ee452e9..14821c0ce5 100644 --- a/interface/src/raypick/StylusPick.h +++ b/interface/src/raypick/StylusPick.h @@ -73,9 +73,6 @@ public: bool isMouse() const override { return false; } static float WEB_STYLUS_LENGTH; - -private: - glm::vec3 _tipOffset; }; #endif // hifi_StylusPick_h diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index caa3151cc5..5595c54b71 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -45,7 +45,7 @@ StylusPointer::~StylusPointer() { OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) { QVariantMap overlayProperties; - // TODO: make these configurable per pointe + QString modelUrl = DEFAULT_STYLUS_MODEL_URL; if (properties["model"].isValid()) { @@ -55,7 +55,7 @@ OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) { modelUrl = modelData["url"].toString(); } } - + // TODO: make these configurable per pointer overlayProperties["name"] = "stylus"; overlayProperties["url"] = modelUrl; overlayProperties["loadPriority"] = 10.0f; diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index 677d384a17..c00ea007f0 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -316,8 +316,8 @@ void Keyboard::scaleKeyboard(float sensorToWorldScale) { } for (auto& keyboardLayer: _keyboardLayers) { - for (auto& key: keyboardLayer) { - key.scaleKey(sensorToWorldScale); + for (auto iter = keyboardLayer.begin(); iter != keyboardLayer.end(); iter++) { + iter.value().scaleKey(sensorToWorldScale); } } @@ -354,8 +354,9 @@ void Keyboard::raiseKeyboard(bool raise) const { return; } Overlays& overlays = qApp->getOverlays(); - for (const auto& key: _keyboardLayers[_layerIndex]) { - auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(key.getID())); + const auto& keyboardLayer = _keyboardLayers[_layerIndex]; + for (auto iter = keyboardLayer.begin(); iter != keyboardLayer.end(); iter++) { + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(iter.key())); if (base3DOverlay) { base3DOverlay->setVisible(raise); } @@ -419,87 +420,90 @@ void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent auto pointerID = event.getID(); auto buttonType = event.getButton(); - for (auto index = _keyboardLayers[_layerIndex].begin(); index != _keyboardLayers[_layerIndex].end(); index++) { - Key& key = *index; - if (key.getID() == overlayID && (pointerID == _leftHandStylus || pointerID == _rightHandStylus) && - buttonType == PointerEvent::PrimaryButton) { + if ((pointerID != _leftHandStylus && pointerID != _rightHandStylus) || buttonType != PointerEvent::PrimaryButton) { + return; + } + auto& keyboardLayer = _keyboardLayers[_layerIndex]; + auto search = keyboardLayer.find(overlayID); - if (key.timerFinished()) { + if (search == keyboardLayer.end()) { + return; + } - auto handIndex = (pointerID == _leftHandStylus) ? controller::Hand::LEFT : controller::Hand::RIGHT; - auto userInputMapper = DependencyManager::get(); - userInputMapper->triggerHapticPulse(PULSE_STRENGTH, PULSE_DURATION, handIndex); + Key& key = search.value(); - Overlays& overlays = qApp->getOverlays(); - auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + if (key.timerFinished()) { - glm::vec3 keyWorldPosition; - if (base3DOverlay) { - keyWorldPosition = base3DOverlay->getWorldPosition(); - } + auto handIndex = (pointerID == _leftHandStylus) ? controller::Hand::LEFT : controller::Hand::RIGHT; + auto userInputMapper = DependencyManager::get(); + userInputMapper->triggerHapticPulse(PULSE_STRENGTH, PULSE_DURATION, handIndex); - AudioInjectorOptions audioOptions; - audioOptions.localOnly = true; - audioOptions.position = keyWorldPosition; - audioOptions.volume = 0.4f; + Overlays& overlays = qApp->getOverlays(); + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); - AudioInjector::playSound(_keySound->getByteArray(), audioOptions); - - int scanCode = key.getScanCode(_capsEnabled); - QString keyString = key.getKeyString(_capsEnabled); - - auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); - - switch (key.getKeyType()) { - case Key::Type::CLOSE: - setRaised(false); - tablet->unfocus(); - return; - - case Key::Type::CAPS: - _capsEnabled = !_capsEnabled; - switchToLayer(key.getSwitchToLayerIndex()); - return; - case Key::Type::LAYER: - _capsEnabled = false; - switchToLayer(key.getSwitchToLayerIndex()); - return; - case Key::Type::BACKSPACE: - scanCode = Qt::Key_Backspace; - keyString = "\x08"; - _typedCharacters = _typedCharacters.left(_typedCharacters.length() -1); - updateTextDisplay(); - break; - case Key::Type::ENTER: - scanCode = Qt::Key_Return; - keyString = "\x0d"; - _typedCharacters.clear(); - updateTextDisplay(); - break; - case Key::Type::CHARACTER: - if (keyString != " ") { - _typedCharacters.push_back((_password ? "*" : keyString)); - } else { - _typedCharacters.clear(); - } - updateTextDisplay(); - break; - - default: - break; - } - - QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString); - QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString); - QCoreApplication::postEvent(QCoreApplication::instance(), pressEvent); - QCoreApplication::postEvent(QCoreApplication::instance(), releaseEvent); - - key.startTimer(KEY_PRESS_TIMEOUT_MS); - } - - break; + glm::vec3 keyWorldPosition; + if (base3DOverlay) { + keyWorldPosition = base3DOverlay->getWorldPosition(); } + + AudioInjectorOptions audioOptions; + audioOptions.localOnly = true; + audioOptions.position = keyWorldPosition; + audioOptions.volume = 0.4f; + + AudioInjector::playSound(_keySound->getByteArray(), audioOptions); + + int scanCode = key.getScanCode(_capsEnabled); + QString keyString = key.getKeyString(_capsEnabled); + + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + + switch (key.getKeyType()) { + case Key::Type::CLOSE: + setRaised(false); + tablet->unfocus(); + return; + + case Key::Type::CAPS: + _capsEnabled = !_capsEnabled; + switchToLayer(key.getSwitchToLayerIndex()); + return; + case Key::Type::LAYER: + _capsEnabled = false; + switchToLayer(key.getSwitchToLayerIndex()); + return; + case Key::Type::BACKSPACE: + scanCode = Qt::Key_Backspace; + keyString = "\x08"; + _typedCharacters = _typedCharacters.left(_typedCharacters.length() -1); + updateTextDisplay(); + break; + case Key::Type::ENTER: + scanCode = Qt::Key_Return; + keyString = "\x0d"; + _typedCharacters.clear(); + updateTextDisplay(); + break; + case Key::Type::CHARACTER: + if (keyString != " ") { + _typedCharacters.push_back((_password ? "*" : keyString)); + } else { + _typedCharacters.clear(); + } + updateTextDisplay(); + break; + + default: + break; + } + + QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString); + QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString); + QCoreApplication::postEvent(QCoreApplication::instance(), pressEvent); + QCoreApplication::postEvent(QCoreApplication::instance(), releaseEvent); + + key.startTimer(KEY_PRESS_TIMEOUT_MS); } } @@ -509,27 +513,29 @@ void Keyboard::handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& } auto pointerID = event.getID(); - - for (auto index = _keyboardLayers[_layerIndex].begin(); index != _keyboardLayers[_layerIndex].end(); index++) { - Key& key = *index; - - if (key.getID() == overlayID && (pointerID == _leftHandStylus || pointerID == _rightHandStylus)) { - Overlays& overlays = qApp->getOverlays(); - - auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); - - if (base3DOverlay) { - base3DOverlay->setLocalPosition(key.getCurrentLocalPosition()); - } - - key.setIsPressed(false); - if (key.timerFinished()) { - key.startTimer(KEY_PRESS_TIMEOUT_MS); - } - break; - } + if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) { + return; } + auto& keyboardLayer = _keyboardLayers[_layerIndex]; + auto search = keyboardLayer.find(overlayID); + + if (search == keyboardLayer.end()) { + return; + } + + Key& key = search.value();; + Overlays& overlays = qApp->getOverlays(); + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + + if (base3DOverlay) { + base3DOverlay->setLocalPosition(key.getCurrentLocalPosition()); + } + + key.setIsPressed(false); + if (key.timerFinished()) { + key.startTimer(KEY_PRESS_TIMEOUT_MS); + } } void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event) { @@ -539,44 +545,40 @@ void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEv auto pointerID = event.getID(); - for (auto index = _keyboardLayers[_layerIndex].begin(); index != _keyboardLayers[_layerIndex].end(); index++) { - Key& key = *index; + if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) { + return; + } - if (key.getID() == overlayID && (pointerID == _leftHandStylus || pointerID == _rightHandStylus)) { - Overlays& overlays = qApp->getOverlays(); + auto& keyboardLayer = _keyboardLayers[_layerIndex]; + auto search = keyboardLayer.find(overlayID); - if (!key.isPressed()) { - auto pointerManager = DependencyManager::get(); - auto pickResult = pointerManager->getPrevPickResult(pointerID); + if (search == keyboardLayer.end()) { + return; + } - auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); + Key& key = search.value(); + Overlays& overlays = qApp->getOverlays(); - if (base3DOverlay) { - auto pickResultVariant = pickResult->toVariantMap(); - auto stylusTipVariant = pickResultVariant["stylusTip"]; - auto stylusTipPositionVariant = stylusTipVariant.toMap()["position"]; - glm::vec3 stylusTipPosition = vec3FromVariant(stylusTipPositionVariant); + if (!key.isPressed()) { + auto base3DOverlay = std::dynamic_pointer_cast(overlays.getOverlay(overlayID)); - glm::quat overlayOrientation = base3DOverlay->getWorldOrientation(); - glm::vec3 overlayPosition = base3DOverlay->getWorldPosition(); + if (base3DOverlay) { + auto pointerManager = DependencyManager::get(); + auto pickResult = pointerManager->getPrevPickResult(pointerID); + auto pickResultVariant = pickResult->toVariantMap(); - glm::mat4 overlayWorldMat = createMatFromQuatAndPos(overlayOrientation, overlayPosition); - glm::mat4 overlayWorldToLocalMat = glm::inverse(overlayWorldMat); + float distance = pickResultVariant["distance"].toFloat(); - glm::vec3 stylusTipInOverlaySpace = transformPoint(overlayWorldToLocalMat, stylusTipPosition); - - static const float PENATRATION_THRESHOLD = 0.025f; - if (stylusTipInOverlaySpace.z < PENATRATION_THRESHOLD) { - static const float Z_OFFSET = 0.002f; - glm::vec3 overlayYAxis = overlayOrientation * Z_AXIS; - glm::vec3 overlayYOffset = overlayYAxis * Z_OFFSET; - glm::vec3 localPosition = key.getCurrentLocalPosition() - overlayYOffset; - base3DOverlay->setLocalPosition(localPosition); - key.setIsPressed(true); - } - } + static const float PENATRATION_THRESHOLD = 0.025f; + if (distance < PENATRATION_THRESHOLD) { + static const float Z_OFFSET = 0.002f; + glm::quat overlayOrientation = base3DOverlay->getWorldOrientation(); + glm::vec3 overlayYAxis = overlayOrientation * Z_AXIS; + glm::vec3 overlayYOffset = overlayYAxis * Z_OFFSET; + glm::vec3 localPosition = key.getCurrentLocalPosition() - overlayYOffset; + base3DOverlay->setLocalPosition(localPosition); + key.setIsPressed(true); } - break; } } } @@ -666,7 +668,7 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { for (int keyboardLayerIndex = 0; keyboardLayerIndex < keyboardLayerCount; keyboardLayerIndex++) { const QJsonValue& keyboardLayer = keyboardLayers[keyboardLayerIndex].toArray(); - std::vector keyboardLayerKeys; + QHash keyboardLayerKeys; foreach (const QJsonValue& keyboardKeyValue, keyboardLayer.toArray()) { QVariantMap textureMap; @@ -718,7 +720,7 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { includeItems.append(key.getID()); _itemsToIgnore.append(key.getID()); - keyboardLayerKeys.push_back(key); + keyboardLayerKeys.insert(overlayID, key); } _keyboardLayers.push_back(keyboardLayerKeys); @@ -774,8 +776,8 @@ void Keyboard::clearKeyboardKeys() { Overlays& overlays = qApp->getOverlays(); for (const auto& keyboardLayer: _keyboardLayers) { - for (const Key& key: keyboardLayer) { - overlays.deleteOverlay(key.getID()); + for (auto iter = keyboardLayer.begin(); iter != keyboardLayer.end(); iter++) { + overlays.deleteOverlay(iter.key()); } } diff --git a/interface/src/ui/Keyboard.h b/interface/src/ui/Keyboard.h index 662a51c2da..2a29a12961 100644 --- a/interface/src/ui/Keyboard.h +++ b/interface/src/ui/Keyboard.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -43,7 +44,6 @@ public: ENTER }; - static Key::Type getKeyTypeFromString(const QString& keyTypeString); OverlayID getID() const { return _keyID; } @@ -146,7 +146,7 @@ private: mutable ReadWriteLockable _ignoreItemsLock; QVector _itemsToIgnore; - std::vector> _keyboardLayers; + std::vector> _keyboardLayers; }; #endif diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 35228c6247..7593e12e07 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -583,7 +583,7 @@ ParabolaToOverlayIntersectionResult Overlays::findParabolaIntersectionVector(con bool visibleOnly, bool collidableOnly) { float bestDistance = std::numeric_limits::max(); bool bestIsFront = false; - + const QVector keyboardKeysToDiscard = DependencyManager::get()->getKeysID(); QMutexLocker locker(&_mutex); ParabolaToOverlayIntersectionResult result; QMapIterator i(_overlaysWorld); @@ -593,7 +593,8 @@ ParabolaToOverlayIntersectionResult Overlays::findParabolaIntersectionVector(con auto thisOverlay = std::dynamic_pointer_cast(i.value()); if ((overlaysToDiscard.size() > 0 && overlaysToDiscard.contains(thisID)) || - (overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID))) { + (overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID)) || + (keyboardKeysToDiscard.size() > 0 && keyboardKeysToDiscard.contains(thisID))) { continue; } @@ -1061,7 +1062,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { i.next(); OverlayID thisID = i.key(); auto overlay = std::dynamic_pointer_cast(i.value()); - // FIXME: this ignores overlays with ignorePickIntersection == true, which seems wrong + if (overlay && overlay->getVisible() && overlay->isLoaded()) { // get AABox in frame of overlay glm::vec3 dimensions = overlay->getDimensions(); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 64a874f63d..18c1339223 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -269,6 +269,7 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay); * * @typedef {object} StylusTip * @property {number} side - The hand the tip is attached to: 0 for left, 1 for right. + * @property {Vec3} tipOffset - the position offset of the stylus tip. * @property {Vec3} position - The position of the stylus tip. * @property {Quat} orientation - The orientation of the stylus tip. * @property {Vec3} velocity - The velocity of the stylus tip. @@ -276,12 +277,14 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay); class StylusTip : public MathPick { public: StylusTip() : position(NAN), velocity(NAN) {} - StylusTip(const bilateral::Side& side, const glm::vec3& position = Vectors::ZERO, const glm::quat& orientation = Quaternions::IDENTITY, const glm::vec3& velocity = Vectors::ZERO) : - side(side), position(position), orientation(orientation), velocity(velocity) {} + StylusTip(const bilateral::Side& side, const glm::vec3& tipOffset = Vectors::ZERO ,const glm::vec3& position = Vectors::ZERO, + const glm::quat& orientation = Quaternions::IDENTITY, const glm::vec3& velocity = Vectors::ZERO) : + side(side), tipOffset(tipOffset), position(position), orientation(orientation), velocity(velocity) {} StylusTip(const QVariantMap& pickVariant) : side(bilateral::Side(pickVariant["side"].toInt())), position(vec3FromVariant(pickVariant["position"])), orientation(quatFromVariant(pickVariant["orientation"])), velocity(vec3FromVariant(pickVariant["velocity"])) {} bilateral::Side side { bilateral::Side::Invalid }; + glm::vec3 tipOffset; glm::vec3 position; glm::quat orientation; glm::vec3 velocity; @@ -289,12 +292,13 @@ public: operator bool() const override { return side != bilateral::Side::Invalid; } bool operator==(const StylusTip& other) const { - return (side == other.side && position == other.position && orientation == other.orientation && velocity == other.velocity); + return (side == other.side && tipOffset == other.tipOffset && position == other.position && orientation == other.orientation && velocity == other.velocity); } QVariantMap toVariantMap() const override { QVariantMap stylusTip; stylusTip["side"] = (int)side; + stylusTip["tipOffset"] = vec3toVariant(tipOffset); stylusTip["position"] = vec3toVariant(position); stylusTip["orientation"] = quatToVariant(orientation); stylusTip["velocity"] = vec3toVariant(velocity); From 358aa547b17a6d6b578743d79c6cf571b5f389c3 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 5 Nov 2018 16:30:30 -0800 Subject: [PATCH 125/125] feedback changes --- interface/resources/config/keyboard.json | 4 +- .../resources/qml/controls/TabletWebView.qml | 4 + .../qml/dialogs/TabletLoginDialog.qml | 16 +++ .../qml/hifi/commerce/wallet/Wallet.qml | 2 +- interface/src/Application.cpp | 2 + .../src/raypick/PointerScriptingInterface.cpp | 4 +- interface/src/ui/Keyboard.cpp | 97 +++++++++++++++++-- interface/src/ui/Keyboard.h | 3 + interface/src/ui/PreferencesDialog.cpp | 1 - libraries/shared/src/RegisteredMetaTypes.h | 4 +- 10 files changed, 121 insertions(+), 16 deletions(-) diff --git a/interface/resources/config/keyboard.json b/interface/resources/config/keyboard.json index ba113da1f5..186a9c1084 100644 --- a/interface/resources/config/keyboard.json +++ b/interface/resources/config/keyboard.json @@ -31,8 +31,8 @@ "localOrientation": { "w": 0.000, "x": 0.000, - "y": 0.976, - "z": 0.216 + "y": 0.906, + "z": 0.423 }, "leftMargin": 0.0, "rightMargin": 0.0, diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 0c5ca37e00..94f4c7978c 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -195,6 +195,10 @@ Item { keyboardEnabled = HMD.active; } + Component.onDestruction: { + keyboardRaised = false; + } + Keys.onPressed: { switch(event.key) { case Qt.Key_L: diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index 08aa903ae9..dad2bb91aa 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -95,6 +95,18 @@ TabletModalWindow { } } + Timer { + id: keyboardTimer + repeat: false + interval: 200 + + onTriggered: { + if (MenuInterface.isOptionChecked("Use 3D Keyboard")) { + KeyboardScriptingInterface.raised = true; + } + } + } + TabletModalFrame { id: mfRoot @@ -131,6 +143,10 @@ TabletModalWindow { loginKeyboard.raised = false; } + Component.onCompleted: { + keyboardTimer.start(); + } + Keyboard { id: loginKeyboard raised: root.keyboardEnabled && root.keyboardRaised diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 874c1c53b7..81fec4ace3 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -41,7 +41,7 @@ Rectangle { } Component.onDestruction: { - keyboard.raised = false; + KeyboardScriptingInterface.raised = false; } Connections { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ccefb3c772..afb2bde7f3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2644,6 +2644,7 @@ void Application::cleanupBeforeQuit() { // it accesses the PickManager to delete its associated Pick DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete"; @@ -2929,6 +2930,7 @@ void Application::initializeRenderEngine() { // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. DependencyManager::get()->initializeShapePipelines(); + DependencyManager::get()->registerKeyboardHighlighting(); }); } diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index 1893132917..0009536479 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -56,14 +56,14 @@ unsigned int PointerScriptingInterface::createPointer(const PickQuery::PickType& * @property {boolean} [hover=false] If this pointer should generate hover events. * @property {boolean} [enabled=false] * @property {Vec3} [tipOffset] The specified offset of the from the joint index. - * @property {Pointers.StylusPointerProperties.model} [model] Data to replace the default model url, positionOffset and rotationOffset. {@link Pointers.StylusPointerProperties.model} + * @property {Pointers.StylusPointerProperties.model} [model] Data to replace the default model url, positionOffset and rotationOffset. */ /**jsdoc * properties defining stylus pick model that can be included to {@link Pointers.StylusPointerProperties} * @typedef {object} Pointers.StylusPointerProperties.model * @property {string} [url] url to the model * @property {Vec3} [dimensions] the dimensions of the model - * @property {Vec3} [positionOffset] the position offset of the model from the parent joint index + * @property {Vec3} [positionOffset] the position offset of the model from the stylus tip. * @property {Vec3} [rotationOffset] the rotation offset of the model from the parent joint index */ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) const { diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index c00ea007f0..773253f85c 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -44,6 +44,7 @@ #include "raypick/PickScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" #include "scripting/WindowScriptingInterface.h" +#include "scripting/SelectionScriptingInterface.h" #include "DependencyManager.h" #include "raypick/StylusPointer.h" @@ -53,12 +54,12 @@ static const int LEFT_HAND_CONTROLLER_INDEX = 0; static const int RIGHT_HAND_CONTROLLER_INDEX = 1; -static const float MALLET_LENGTH = 0.4f; -static const float MALLET_TOUCH_Y_OFFSET = 0.105f; -static const float MALLET_Y_OFFSET = 0.35f; +static const float MALLET_LENGTH = 0.2f; +static const float MALLET_TOUCH_Y_OFFSET = 0.052f; +static const float MALLET_Y_OFFSET = 0.180f; static const glm::quat MALLET_ROTATION_OFFSET{0.70710678f, 0.0f, -0.70710678f, 0.0f}; -static const glm::vec3 MALLET_MODEL_DIMENSIONS{0.05f, MALLET_LENGTH, 0.05f}; +static const glm::vec3 MALLET_MODEL_DIMENSIONS{0.03f, MALLET_LENGTH, 0.03f}; static const glm::vec3 MALLET_POSITION_OFFSET{0.0f, -MALLET_Y_OFFSET / 2.0f, 0.0f}; static const glm::vec3 MALLET_TIP_OFFSET{0.0f, MALLET_LENGTH - MALLET_TOUCH_Y_OFFSET, 0.0f}; @@ -88,6 +89,28 @@ static const QString BACKSPACE_STRING = "backspace"; static const QString SPACE_STRING = "space"; static const QString ENTER_STRING = "enter"; +static const QString KEY_HOVER_HIGHLIGHT = "keyHoverHiglight"; +static const QString KEY_PRESSED_HIGHLIGHT = "keyPressesHighlight"; +static const QVariantMap KEY_HOVERING_STYLE { + { "isOutlineSmooth", true }, + { "outlineWidth", 3 }, + { "outlineUnoccludedColor", QVariantMap {{"red", 13}, {"green", 152}, {"blue", 186}}}, + { "outlineUnoccludedAlpha", 1.0 }, + { "outlineOccludedAlpha", 0.0 }, + { "fillUnoccludedAlpha", 0.0 }, + { "fillOccludedAlpha", 0.0 } +}; + +static const QVariantMap KEY_PRESSING_STYLE { + { "isOutlineSmooth", true }, + { "outlineWidth", 3 }, + { "fillUnoccludedColor", QVariantMap {{"red", 50}, {"green", 50}, {"blue", 50}}}, + { "outlineUnoccludedAlpha", 0.0 }, + { "outlineOccludedAlpha", 0.0 }, + { "fillUnoccludedAlpha", 0.6 }, + { "fillOccludedAlpha", 0.0 } +}; + std::pair calculateKeyboardPositionAndOrientation() { auto myAvatar = DependencyManager::get()->getMyAvatar(); auto hmd = DependencyManager::get(); @@ -201,10 +224,20 @@ Keyboard::Keyboard() { connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Keyboard::handleTriggerBegin, Qt::QueuedConnection); connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Keyboard::handleTriggerContinue, Qt::QueuedConnection); connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Keyboard::handleTriggerEnd, Qt::QueuedConnection); + connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Keyboard::handleHoverBegin, Qt::QueuedConnection); + connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Keyboard::handleHoverEnd, Qt::QueuedConnection); connect(myAvatar.get(), &MyAvatar::sensorToWorldScaleChanged, this, &Keyboard::scaleKeyboard, Qt::QueuedConnection); connect(windowScriptingInterface.data(), &WindowScriptingInterface::domainChanged, [&]() { setRaised(false); }); } +void Keyboard::registerKeyboardHighlighting() { + auto selection = DependencyManager::get(); + selection->enableListHighlight(KEY_HOVER_HIGHLIGHT, KEY_HOVERING_STYLE); + selection->enableListToScene(KEY_HOVER_HIGHLIGHT); + selection->enableListHighlight(KEY_PRESSED_HIGHLIGHT, KEY_PRESSING_STYLE); + selection->enableListToScene(KEY_PRESSED_HIGHLIGHT); +} + void Keyboard::createKeyboard() { auto pointerManager = DependencyManager::get(); @@ -450,7 +483,7 @@ void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent AudioInjectorOptions audioOptions; audioOptions.localOnly = true; audioOptions.position = keyWorldPosition; - audioOptions.volume = 0.4f; + audioOptions.volume = 0.1f; AudioInjector::playSound(_keySound->getByteArray(), audioOptions); @@ -504,6 +537,8 @@ void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent QCoreApplication::postEvent(QCoreApplication::instance(), releaseEvent); key.startTimer(KEY_PRESS_TIMEOUT_MS); + auto selection = DependencyManager::get(); + selection->addToSelectedItemsList(KEY_PRESSED_HIGHLIGHT, "overlay", overlayID); } } @@ -536,6 +571,9 @@ void Keyboard::handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& if (key.timerFinished()) { key.startTimer(KEY_PRESS_TIMEOUT_MS); } + + auto selection = DependencyManager::get(); + selection->removeFromSelectedItemsList(KEY_PRESSED_HIGHLIGHT, "overlay", overlayID); } void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event) { @@ -565,9 +603,8 @@ void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEv if (base3DOverlay) { auto pointerManager = DependencyManager::get(); auto pickResult = pointerManager->getPrevPickResult(pointerID); - auto pickResultVariant = pickResult->toVariantMap(); - - float distance = pickResultVariant["distance"].toFloat(); + auto stylusPickResult = std::dynamic_pointer_cast(pickResult); + float distance = stylusPickResult->distance; static const float PENATRATION_THRESHOLD = 0.025f; if (distance < PENATRATION_THRESHOLD) { @@ -583,6 +620,50 @@ void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEv } } +void Keyboard::handleHoverBegin(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + + if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) { + return; + } + + auto& keyboardLayer = _keyboardLayers[_layerIndex]; + auto search = keyboardLayer.find(overlayID); + + if (search == keyboardLayer.end()) { + return; + } + + auto selection = DependencyManager::get(); + selection->addToSelectedItemsList(KEY_HOVER_HIGHLIGHT, "overlay", overlayID); +} + +void Keyboard::handleHoverEnd(const OverlayID& overlayID, const PointerEvent& event) { + if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) { + return; + } + + auto pointerID = event.getID(); + + if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) { + return; + } + + auto& keyboardLayer = _keyboardLayers[_layerIndex]; + auto search = keyboardLayer.find(overlayID); + + if (search == keyboardLayer.end()) { + return; + } + + auto selection = DependencyManager::get(); + selection->removeFromSelectedItemsList(KEY_HOVER_HIGHLIGHT, "overlay", overlayID); +} + void Keyboard::disableStylus() { auto pointerManager = DependencyManager::get(); pointerManager->setRenderState(_leftHandStylus, "events off"); diff --git a/interface/src/ui/Keyboard.h b/interface/src/ui/Keyboard.h index 2a29a12961..18db38b2ae 100644 --- a/interface/src/ui/Keyboard.h +++ b/interface/src/ui/Keyboard.h @@ -90,6 +90,7 @@ class Keyboard : public Dependency, public QObject, public ReadWriteLockable { public: Keyboard(); void createKeyboard(); + void registerKeyboardHighlighting(); bool isRaised() const; void setRaised(bool raised); @@ -103,6 +104,8 @@ public slots: void handleTriggerBegin(const OverlayID& overlayID, const PointerEvent& event); void handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& event); void handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event); + void handleHoverBegin(const OverlayID& overlayID, const PointerEvent& event); + void handleHoverEnd(const OverlayID& overlayID, const PointerEvent& event); void scaleKeyboard(float sensorToWorldScale); private: diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 34d80f50cf..d71ad9dc82 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -147,7 +147,6 @@ void setupPreferences() { preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter)); } */ - // Snapshots static const QString SNAPSHOTS { "Snapshots" }; { diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 18c1339223..ed637fe771 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -280,8 +280,8 @@ public: StylusTip(const bilateral::Side& side, const glm::vec3& tipOffset = Vectors::ZERO ,const glm::vec3& position = Vectors::ZERO, const glm::quat& orientation = Quaternions::IDENTITY, const glm::vec3& velocity = Vectors::ZERO) : side(side), tipOffset(tipOffset), position(position), orientation(orientation), velocity(velocity) {} - StylusTip(const QVariantMap& pickVariant) : side(bilateral::Side(pickVariant["side"].toInt())), position(vec3FromVariant(pickVariant["position"])), - orientation(quatFromVariant(pickVariant["orientation"])), velocity(vec3FromVariant(pickVariant["velocity"])) {} + StylusTip(const QVariantMap& pickVariant) : side(bilateral::Side(pickVariant["side"].toInt())), tipOffset(vec3FromVariant(pickVariant["tipOffset"])), + position(vec3FromVariant(pickVariant["position"])), orientation(quatFromVariant(pickVariant["orientation"])), velocity(vec3FromVariant(pickVariant["velocity"])) {} bilateral::Side side { bilateral::Side::Invalid }; glm::vec3 tipOffset;