diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 2deb51f2e3..17d473f02c 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -1712,28 +1713,44 @@ void DomainServerSettingsManager::sortPermissions() { } void DomainServerSettingsManager::persistToFile() { - sortPermissions(); - - // make sure we have the dir the settings file is supposed to live in - QFileInfo settingsFileInfo(_configMap.getUserConfigFilename()); - - if (!settingsFileInfo.dir().exists()) { - settingsFileInfo.dir().mkpath("."); - } - - QFile settingsFile(_configMap.getUserConfigFilename()); - - if (settingsFile.open(QIODevice::WriteOnly)) { - // take a read lock so we can grab the config and write it to file - QReadLocker locker(&_settingsLock); - settingsFile.write(QJsonDocument::fromVariant(_configMap.getConfig()).toJson()); - } else { - qCritical("Could not write to JSON settings file. Unable to persist settings."); - - // failed to write, reload whatever the current config state is - // with a write lock since we're about to overwrite the config map + QString settingsFilename = _configMap.getUserConfigFilename(); + QDir settingsDir = QFileInfo(settingsFilename).dir(); + if (!settingsDir.exists() && !settingsDir.mkpath(".")) { + // If the path already exists when the `mkpath` method is + // called, it will return true. It will only return false if the + // path doesn't exist after the call returns. + qCritical("Could not create the settings file parent directory. Unable to persist settings."); QWriteLocker locker(&_settingsLock); _configMap.loadConfig(); + return; + } + QSaveFile settingsFile(settingsFilename); + if (!settingsFile.open(QIODevice::WriteOnly)) { + qCritical("Could not open the JSON settings file. Unable to persist settings."); + QWriteLocker locker(&_settingsLock); + _configMap.loadConfig(); + return; + } + + sortPermissions(); + + QVariantMap conf; + { + QReadLocker locker(&_settingsLock); + conf = _configMap.getConfig(); + } + QByteArray json = QJsonDocument::fromVariant(conf).toJson(); + if (settingsFile.write(json) == -1) { + qCritical("Could not write to JSON settings file. Unable to persist settings."); + QWriteLocker locker(&_settingsLock); + _configMap.loadConfig(); + return; + } + if (!settingsFile.commit()) { + qCritical("Could not commit writes to JSON settings file. Unable to persist settings."); + QWriteLocker locker(&_settingsLock); + _configMap.loadConfig(); + return; // defend against future code } } diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 133ef05136..85364d08d2 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -209,7 +209,7 @@ link_hifi_libraries( model-networking model-baker entities avatars trackers audio audio-client animation script-engine physics render-utils entities-renderer avatars-renderer ui qml auto-updater midi - controllers plugins image trackers + controllers plugins image trackers platform ui-plugins display-plugins input-plugins # Platform specific GL libraries ${PLATFORM_GL_BACKEND} @@ -228,6 +228,7 @@ target_bullet() target_opengl() add_crashpad() target_breakpad() +target_json() # perform standard include and linking for found externals foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3dc49e89d6..45807662ef 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -192,7 +192,7 @@ #include "scripting/WalletScriptingInterface.h" #include "scripting/TTSScriptingInterface.h" #include "scripting/KeyboardScriptingInterface.h" -#include "scripting/RefreshRateScriptingInterface.h" +#include "scripting/PerformanceScriptingInterface.h" @@ -3274,7 +3274,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Controller", DependencyManager::get().data()); surfaceContext->setContextProperty("Entities", DependencyManager::get().data()); - surfaceContext->setContextProperty("RefreshRate", new RefreshRateScriptingInterface()); + surfaceContext->setContextProperty("Performance", new PerformanceScriptingInterface()); _fileDownload = new FileScriptingInterface(engine); surfaceContext->setContextProperty("File", _fileDownload); connect(_fileDownload, &FileScriptingInterface::unzipResult, this, &Application::handleUnzip); @@ -3424,7 +3424,7 @@ void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditiona surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance()); - surfaceContext->setContextProperty("RefreshRate", new RefreshRateScriptingInterface()); + surfaceContext->setContextProperty("Performance", new PerformanceScriptingInterface()); surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED surfaceContext->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED @@ -7392,7 +7392,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("LODManager", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Keyboard", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("RefreshRate", new RefreshRateScriptingInterface); + scriptEngine->registerGlobalObject("Performance", new PerformanceScriptingInterface()); scriptEngine->registerGlobalObject("Paths", DependencyManager::get().data()); diff --git a/interface/src/Application.h b/interface/src/Application.h index e64101d2fd..34a5ba1d0c 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -1,4 +1,4 @@ -// +// // Application.h // interface/src // @@ -48,7 +48,6 @@ #include #include #include - #include #include "avatar/MyAvatar.h" diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index b6fffbb4bd..b0b919fb0f 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -351,10 +351,18 @@ float LODManager::getHMDLODTargetFPS() const { } float LODManager::getLODTargetFPS() const { + auto refreshRateFPS = qApp->getRefreshRateManager().getActiveRefreshRate(); + auto lodTargetFPS = getDesktopLODTargetFPS(); if (qApp->isHMDMode()) { - return getHMDLODTargetFPS(); + lodTargetFPS = getHMDLODTargetFPS(); + } + + // if RefreshRate is slower than LOD target then it becomes the true LOD target + if (lodTargetFPS > refreshRateFPS) { + return refreshRateFPS; + } else { + return lodTargetFPS; } - return getDesktopLODTargetFPS(); } void LODManager::setWorldDetailQuality(float quality) { diff --git a/interface/src/RefreshRateManager.cpp b/interface/src/RefreshRateManager.cpp index 0c5bcd405e..f2033b9491 100644 --- a/interface/src/RefreshRateManager.cpp +++ b/interface/src/RefreshRateManager.cpp @@ -13,13 +13,8 @@ #include "RefreshRateManager.h" #include -#include -#include - -#include - static const int VR_TARGET_RATE = 90; static const std::array REFRESH_RATE_PROFILE_TO_STRING = diff --git a/interface/src/RefreshRateManager.h b/interface/src/RefreshRateManager.h index 6ded8c8869..6316de3bfb 100644 --- a/interface/src/RefreshRateManager.h +++ b/interface/src/RefreshRateManager.h @@ -14,6 +14,7 @@ #include #include +#include #include diff --git a/interface/src/scripting/PerformanceScriptingInterface.cpp b/interface/src/scripting/PerformanceScriptingInterface.cpp new file mode 100644 index 0000000000..b1b4e62dca --- /dev/null +++ b/interface/src/scripting/PerformanceScriptingInterface.cpp @@ -0,0 +1,40 @@ +// +// Created by Bradley Austin Davis on 2019/05/14 +// Copyright 2013-2019 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 "PerformanceScriptingInterface.h" + +#include "../Application.h" + +std::once_flag PerformanceScriptingInterface::registry_flag; + +PerformanceScriptingInterface::PerformanceScriptingInterface() { + std::call_once(registry_flag, [] { + qmlRegisterType("PerformanceEnums", 1, 0, "RefreshRate"); + }); +} + +void PerformanceScriptingInterface::setRefreshRateProfile(RefreshRateProfile refreshRateProfile) { + qApp->getRefreshRateManager().setRefreshRateProfile((RefreshRateManager::RefreshRateProfile)refreshRateProfile); +} + +PerformanceScriptingInterface::RefreshRateProfile PerformanceScriptingInterface::getRefreshRateProfile() const { + return (PerformanceScriptingInterface::RefreshRateProfile)qApp->getRefreshRateManager().getRefreshRateProfile(); +} + +int PerformanceScriptingInterface::getActiveRefreshRate() const { + return qApp->getRefreshRateManager().getActiveRefreshRate(); +} + +RefreshRateManager::UXMode PerformanceScriptingInterface::getUXMode() const { + return qApp->getRefreshRateManager().getUXMode(); +} + +RefreshRateManager::RefreshRateRegime PerformanceScriptingInterface::getRefreshRateRegime() const { + return qApp->getRefreshRateManager().getRefreshRateRegime(); +} diff --git a/interface/src/scripting/PerformanceScriptingInterface.h b/interface/src/scripting/PerformanceScriptingInterface.h new file mode 100644 index 0000000000..90b51d173c --- /dev/null +++ b/interface/src/scripting/PerformanceScriptingInterface.h @@ -0,0 +1,48 @@ +// +// Created by Bradley Austin Davis on 2019/05/14 +// Copyright 2013-2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_PerformanceScriptingInterface_h +#define hifi_PerformanceScriptingInterface_h + +#include + +#include + +#include "../RefreshRateManager.h" + + +class PerformanceScriptingInterface : public QObject { + Q_OBJECT +public: + // Must match RefreshRateManager enums + enum RefreshRateProfile { + ECO = RefreshRateManager::RefreshRateProfile::ECO, + INTERACTIVE = RefreshRateManager::RefreshRateProfile::INTERACTIVE, + REALTIME = RefreshRateManager::RefreshRateProfile::REALTIME, + }; + Q_ENUM(RefreshRateProfile) + + + PerformanceScriptingInterface(); + ~PerformanceScriptingInterface() = default; + +public slots: + void setRefreshRateProfile(RefreshRateProfile refreshRateProfile); + RefreshRateProfile getRefreshRateProfile() const; + + int getActiveRefreshRate() const; + RefreshRateManager::UXMode getUXMode() const; + RefreshRateManager::RefreshRateRegime getRefreshRateRegime() const; + + +private: + static std::once_flag registry_flag; +}; + +#endif // header guard diff --git a/interface/src/scripting/RefreshRateScriptingInterface.h b/interface/src/scripting/RefreshRateScriptingInterface.h deleted file mode 100644 index 697141583f..0000000000 --- a/interface/src/scripting/RefreshRateScriptingInterface.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// RefreshRateScriptingInterface.h -// interface/src/scrfipting -// -// Created by Dante Ruiz on 2019-04-15. -// Copyright 2019 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_RefreshRateScriptingInterface_h -#define hifi_RefreshRateScriptingInterface_h - -#include - -#include - -class RefreshRateScriptingInterface : public QObject { - Q_OBJECT -public: - RefreshRateScriptingInterface() = default; - ~RefreshRateScriptingInterface() = default; - -public: - Q_INVOKABLE QString getRefreshRateProfile() { - RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager(); - return QString::fromStdString(RefreshRateManager::refreshRateProfileToString(refreshRateManager.getRefreshRateProfile())); - } - - Q_INVOKABLE QString getRefreshRateRegime() { - RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager(); - return QString::fromStdString(RefreshRateManager::refreshRateRegimeToString(refreshRateManager.getRefreshRateRegime())); - } - - Q_INVOKABLE QString getUXMode() { - RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager(); - return QString::fromStdString(RefreshRateManager::uxModeToString(refreshRateManager.getUXMode())); - } - - Q_INVOKABLE int getActiveRefreshRate() { - return qApp->getRefreshRateManager().getActiveRefreshRate(); - } -}; - -#endif diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 2c1311924f..eb967dde89 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include "AndroidHelper.h" @@ -414,11 +415,11 @@ QString WindowScriptingInterface::protocolSignature() { } int WindowScriptingInterface::getInnerWidth() { - return qApp->getWindow()->geometry().width(); + return qApp->getPrimaryWidget()->geometry().width(); } int WindowScriptingInterface::getInnerHeight() { - return qApp->getWindow()->geometry().height() - qApp->getPrimaryMenu()->geometry().height(); + return qApp->getPrimaryWidget()->geometry().height(); } glm::vec2 WindowScriptingInterface::getDeviceSize() const { @@ -609,3 +610,31 @@ void WindowScriptingInterface::onMessageBoxSelected(int button) { float WindowScriptingInterface::domainLoadingProgress() { return qApp->getOctreePacketProcessor().domainLoadingProgress(); } + +int WindowScriptingInterface::getDisplayPluginCount() { + return (int)PluginManager::getInstance()->getDisplayPlugins().size(); +} + +QString WindowScriptingInterface::getDisplayPluginName(int index) { + return PluginManager::getInstance()->getDisplayPlugins().at(index)->getName(); +} + +bool WindowScriptingInterface::isDisplayPluginHmd(int index) { + return PluginManager::getInstance()->getDisplayPlugins().at(index)->isHmd(); +} + +int WindowScriptingInterface::getActiveDisplayPlugin() { + auto active = qApp->getActiveDisplayPlugin(); + auto size = getDisplayPluginCount(); + for (int i = 0; i < size; ++i) { + if (PluginManager::getInstance()->getDisplayPlugins().at(i) == active) { + return i; + } + } + return -1; +} + +void WindowScriptingInterface::setActiveDisplayPlugin(int index) { + auto name = PluginManager::getInstance()->getDisplayPlugins().at(index)->getName(); + qApp->setActiveDisplayPlugin(name); +} diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 9a314cad6a..dd3f01dd17 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -558,6 +558,44 @@ public slots: */ float domainLoadingProgress(); + /**jsdoc + * Return the number of display plugins currently available + * @function Window.getDisplayPluginCount + * @returns {int} The number of currently available display plugins + */ + int getDisplayPluginCount(); + + /**jsdoc + * Return the human readable name of a display plugin + * @function Window.getDisplayPluginName + * @param {int} index - The index of the display plugin. Must be less than the value returned by {@link Window.getDisplayPluginCount|getDisplayPluginCount}. + * @returns {string} The name of the specified display plugin + */ + QString getDisplayPluginName(int index); + + /**jsdoc + * Return whether a given display plugin is an HMD + * @function Window.isDisplayPluginHmd + * @param {int} index - The index of the display plugin. Must be less than the value returned by {@link Window.getDisplayPluginCount|getDisplayPluginCount}. + * @returns {bool} True if the specified display plugin is a HMD + */ + bool isDisplayPluginHmd(int index); + + /**jsdoc + * Return the currently active display plugin + * @function Window.getActiveDisplayPlugin + * @returns {int} The index of the currently active display plugin + */ + int getActiveDisplayPlugin(); + + /**jsdoc + * Return the currently active display plugin + * @function Window.setActiveDisplayPlugin + * @param {int} index - The index of the display plugin. Must be less than the value returned by {@link Window.getDisplayPluginCount|getDisplayPluginCount}. + */ + void setActiveDisplayPlugin(int index); + + private slots: void onWindowGeometryChanged(const QRect& geometry); void onMessageBoxSelected(int button); diff --git a/interface/src/ui/InteractiveWindow.cpp b/interface/src/ui/InteractiveWindow.cpp index 4e454798ef..657b6b3ac5 100644 --- a/interface/src/ui/InteractiveWindow.cpp +++ b/interface/src/ui/InteractiveWindow.cpp @@ -86,7 +86,6 @@ void interactiveWindowPointerFromScriptValue(const QScriptValue& object, Interac * provided as {@link Desktop|Desktop.ALWAYS_ON_TOP} and {@link Desktop|Desktop.CLOSE_BUTTON_HIDES}. */ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties) { - bool docked = false; InteractiveWindowPresentationMode presentationMode = InteractiveWindowPresentationMode::Native; if (properties.contains(PRESENTATION_MODE_PROPERTY)) { @@ -146,12 +145,12 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap QObject::connect(quickView.get(), &QQuickView::statusChanged, [&, this] (QQuickView::Status status) { if (status == QQuickView::Ready) { QQuickItem* rootItem = _dockWidget->getRootItem(); + _dockWidget->getQuickView()->rootContext()->setContextProperty(EVENT_BRIDGE_PROPERTY, this); QObject::connect(rootItem, SIGNAL(sendToScript(QVariant)), this, SLOT(qmlToScript(const QVariant&)), Qt::QueuedConnection); } }); _dockWidget->setSource(QUrl(sourceUrl)); mainWindow->addDockWidget(dockArea, _dockWidget.get()); - _dockedWindow = docked; } else { auto offscreenUi = DependencyManager::get(); // Build the event bridge and wrapper on the main thread @@ -210,10 +209,10 @@ InteractiveWindow::~InteractiveWindow() { void InteractiveWindow::sendToQml(const QVariant& message) { // Forward messages received from the script on to QML - if (_dockedWindow) { + if (_dockWidget) { QQuickItem* rootItem = _dockWidget->getRootItem(); if (rootItem) { - QMetaObject::invokeMethod(_qmlWindow, "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message)); + QMetaObject::invokeMethod(rootItem, "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message)); } } else { QMetaObject::invokeMethod(_qmlWindow, "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message)); diff --git a/interface/src/ui/InteractiveWindow.h b/interface/src/ui/InteractiveWindow.h index 22127479d2..fd0a3376fb 100644 --- a/interface/src/ui/InteractiveWindow.h +++ b/interface/src/ui/InteractiveWindow.h @@ -288,7 +288,6 @@ protected slots: void qmlToScript(const QVariant& message); private: - bool _dockedWindow { false }; QPointer _qmlWindow; std::shared_ptr _dockWidget { nullptr }; }; diff --git a/libraries/display-plugins/src/display-plugins/RefreshRateController.cpp b/libraries/display-plugins/src/display-plugins/RefreshRateController.cpp index 2d9b553163..369c3cdb26 100644 --- a/libraries/display-plugins/src/display-plugins/RefreshRateController.cpp +++ b/libraries/display-plugins/src/display-plugins/RefreshRateController.cpp @@ -30,7 +30,7 @@ int RefreshRateController::getRefreshRateLimitPeriod() const { return durationNanosecondsToHz(_refreshRateLimitPeriod); } -void RefreshRateController::sleepThreadIfNeeded(QThread* thread, bool isHmd) { +std::chrono::nanoseconds RefreshRateController::sleepThreadIfNeeded(QThread* thread, bool isHmd) { if (!isHmd) { static const std::chrono::nanoseconds EPSILON = std::chrono::milliseconds(1); auto duration = std::chrono::duration_cast(_endTime - _startTime); @@ -39,5 +39,7 @@ void RefreshRateController::sleepThreadIfNeeded(QThread* thread, bool isHmd) { if (sleepDuration.count() > 0) { thread->msleep(std::chrono::duration_cast(sleepDuration).count()); } + return sleepDuration; } + return std::chrono::nanoseconds(0); } diff --git a/libraries/display-plugins/src/display-plugins/RefreshRateController.h b/libraries/display-plugins/src/display-plugins/RefreshRateController.h index 15adee3d3d..3cb563377f 100644 --- a/libraries/display-plugins/src/display-plugins/RefreshRateController.h +++ b/libraries/display-plugins/src/display-plugins/RefreshRateController.h @@ -29,7 +29,7 @@ public: void clockStartTime() { _startTime = std::chrono::high_resolution_clock::now(); } void clockEndTime() { _endTime = std::chrono::high_resolution_clock::now(); } - void sleepThreadIfNeeded(QThread* thread, bool isHmd); + std::chrono::nanoseconds sleepThreadIfNeeded(QThread* thread, bool isHmd); private: std::chrono::time_point _startTime { std::chrono::high_resolution_clock::now() }; std::chrono::time_point _endTime { std::chrono::high_resolution_clock::now() }; diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 9952072668..d6fcfaba36 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -37,6 +37,28 @@ #include "FBXSerializer.h" +#define GLTF_GET_INDICIES(accCount) int index1 = (indices[n + 0] * accCount); int index2 = (indices[n + 1] * accCount); int index3 = (indices[n + 2] * accCount); + +#define GLTF_APPEND_ARRAY_1(newArray, oldArray) GLTF_GET_INDICIES(1) \ +newArray.append(oldArray[index1]); \ +newArray.append(oldArray[index2]); \ +newArray.append(oldArray[index3]); + +#define GLTF_APPEND_ARRAY_2(newArray, oldArray) GLTF_GET_INDICIES(2) \ +newArray.append(oldArray[index1]); newArray.append(oldArray[index1 + 1]); \ +newArray.append(oldArray[index2]); newArray.append(oldArray[index2 + 1]); \ +newArray.append(oldArray[index3]); newArray.append(oldArray[index3 + 1]); + +#define GLTF_APPEND_ARRAY_3(newArray, oldArray) GLTF_GET_INDICIES(3) \ +newArray.append(oldArray[index1]); newArray.append(oldArray[index1 + 1]); newArray.append(oldArray[index1 + 2]); \ +newArray.append(oldArray[index2]); newArray.append(oldArray[index2 + 1]); newArray.append(oldArray[index2 + 2]); \ +newArray.append(oldArray[index3]); newArray.append(oldArray[index3 + 1]); newArray.append(oldArray[index3 + 2]); + +#define GLTF_APPEND_ARRAY_4(newArray, oldArray) GLTF_GET_INDICIES(4) \ +newArray.append(oldArray[index1]); newArray.append(oldArray[index1 + 1]); newArray.append(oldArray[index1 + 2]); newArray.append(oldArray[index1 + 3]); \ +newArray.append(oldArray[index2]); newArray.append(oldArray[index2 + 1]); newArray.append(oldArray[index2 + 2]); newArray.append(oldArray[index2 + 3]); \ +newArray.append(oldArray[index3]); newArray.append(oldArray[index3 + 1]); newArray.append(oldArray[index3 + 2]); newArray.append(oldArray[index3 + 3]); + bool GLTFSerializer::getStringVal(const QJsonObject& object, const QString& fieldname, QString& value, QMap& defined) { bool _defined = (object.contains(fieldname) && object[fieldname].isString()); @@ -261,6 +283,41 @@ bool GLTFSerializer::setAsset(const QJsonObject& object) { return isAssetDefined; } +GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices GLTFSerializer::createAccessorSparseIndices(const QJsonObject& object) { + GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices accessorSparseIndices; + + getIntVal(object, "bufferView", accessorSparseIndices.bufferView, accessorSparseIndices.defined); + getIntVal(object, "byteOffset", accessorSparseIndices.byteOffset, accessorSparseIndices.defined); + getIntVal(object, "componentType", accessorSparseIndices.componentType, accessorSparseIndices.defined); + + return accessorSparseIndices; +} + +GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues GLTFSerializer::createAccessorSparseValues(const QJsonObject& object) { + GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues accessorSparseValues; + + getIntVal(object, "bufferView", accessorSparseValues.bufferView, accessorSparseValues.defined); + getIntVal(object, "byteOffset", accessorSparseValues.byteOffset, accessorSparseValues.defined); + + return accessorSparseValues; +} + +GLTFAccessor::GLTFAccessorSparse GLTFSerializer::createAccessorSparse(const QJsonObject& object) { + GLTFAccessor::GLTFAccessorSparse accessorSparse; + + getIntVal(object, "count", accessorSparse.count, accessorSparse.defined); + QJsonObject sparseIndicesObject; + if (getObjectVal(object, "indices", sparseIndicesObject, accessorSparse.defined)) { + accessorSparse.indices = createAccessorSparseIndices(sparseIndicesObject); + } + QJsonObject sparseValuesObject; + if (getObjectVal(object, "values", sparseValuesObject, accessorSparse.defined)) { + accessorSparse.values = createAccessorSparseValues(sparseValuesObject); + } + + return accessorSparse; +} + bool GLTFSerializer::addAccessor(const QJsonObject& object) { GLTFAccessor accessor; @@ -273,6 +330,12 @@ bool GLTFSerializer::addAccessor(const QJsonObject& object) { if (getStringVal(object, "type", type, accessor.defined)) { accessor.type = getAccessorType(type); } + + QJsonObject sparseObject; + if (getObjectVal(object, "sparse", sparseObject, accessor.defined)) { + accessor.sparse = createAccessorSparse(sparseObject); + } + getDoubleArrayVal(object, "max", accessor.max, accessor.defined); getDoubleArrayVal(object, "min", accessor.min, accessor.defined); @@ -749,32 +812,16 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) { void GLTFSerializer::getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues) { for (auto &skin : _file.skins) { GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices]; - GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView]; - GLTFBuffer& indicesBuffer = _file.buffers[indicesBufferview.buffer]; - int accBoffset = indicesAccessor.defined["byteOffset"] ? indicesAccessor.byteOffset : 0; QVector matrices; - addArrayOfType(indicesBuffer.blob, - indicesBufferview.byteOffset + accBoffset, - indicesAccessor.count, - matrices, - indicesAccessor.type, - indicesAccessor.componentType); + addArrayFromAccessor(indicesAccessor, matrices); inverseBindMatrixValues.push_back(matrices.toStdVector()); } } void GLTFSerializer::generateTargetData(int index, float weight, QVector& returnVector) { GLTFAccessor& accessor = _file.accessors[index]; - GLTFBufferView& bufferview = _file.bufferviews[accessor.bufferView]; - GLTFBuffer& buffer = _file.buffers[bufferview.buffer]; - int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0; QVector storedValues; - addArrayOfType(buffer.blob, - bufferview.byteOffset + accBoffset, - accessor.count, - storedValues, - accessor.type, - accessor.componentType); + addArrayFromAccessor(accessor, storedValues); for (int n = 0; n < storedValues.size(); n = n + 3) { returnVector.push_back(glm::vec3(weight * storedValues[n], weight * storedValues[n + 1], weight * storedValues[n + 2])); } @@ -783,7 +830,7 @@ void GLTFSerializer::generateTargetData(int index, float weight, QVector parents; QVector sortedNodes; parents.fill(-1, numNodes); @@ -813,7 +860,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& nodecount++; } - + // since parent indices must exist in the sorted list before any of their children, sortedNodes might not be initialized in the correct order // therefore we need to re-initialize the order in which nodes will be parsed QVector hasBeenSorted; @@ -868,7 +915,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& joint.translation = extractTranslation(joint.transform); joint.rotation = glmExtractRotation(joint.transform); glm::vec3 scale = extractScale(joint.transform); - joint.postTransform = glm::scale(glm::mat4(), scale); + joint.postTransform = glm::scale(glm::mat4(), scale); joint.name = node.name; joint.isSkeletonJoint = false; @@ -915,12 +962,18 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& } - // Build materials + //Build materials QVector materialIDs; QString unknown = "Default"; int ukcount = 0; foreach(auto material, _file.materials) { - QString mid = (material.defined["name"]) ? material.name : unknown + ukcount++; + if (!material.defined["name"]) { + QString name = unknown + QString::number(ukcount++); + material.name = name; + material.defined.insert("name", true); + } + + QString mid = material.name; materialIDs.push_back(mid); } @@ -929,6 +982,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& hfmModel.materials[matid] = HFMMaterial(); HFMMaterial& hfmMaterial = hfmModel.materials[matid]; hfmMaterial._material = std::make_shared(); + hfmMaterial.name = hfmMaterial.materialID = matid; setHFMMaterial(hfmMaterial, _file.materials[i]); } @@ -939,56 +993,76 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& auto& node = _file.nodes[nodeIndex]; if (node.defined["mesh"]) { - foreach(auto &primitive, _file.meshes[node.mesh].primitives) { - hfmModel.meshes.append(HFMMesh()); - HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; - if (!hfmModel.hasSkeletonJoints) { + + hfmModel.meshes.append(HFMMesh()); + HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; + if (!hfmModel.hasSkeletonJoints) { + HFMCluster cluster; + cluster.jointIndex = nodecount; + cluster.inverseBindMatrix = glm::mat4(); + cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); + mesh.clusters.append(cluster); + } else { // skinned model + for (int j = 0; j < numNodes; j++) { HFMCluster cluster; - cluster.jointIndex = nodecount; - cluster.inverseBindMatrix = glm::mat4(); + cluster.jointIndex = j; + cluster.inverseBindMatrix = jointInverseBindTransforms[j]; cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); mesh.clusters.append(cluster); - } else { // skinned model - for (int j = 0; j < numNodes; j++) { - HFMCluster cluster; - cluster.jointIndex = j; - cluster.inverseBindMatrix = jointInverseBindTransforms[j]; - cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); - mesh.clusters.append(cluster); + } + } + HFMCluster root; + root.jointIndex = 0; + root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex]; + root.inverseBindTransform = Transform(root.inverseBindMatrix); + mesh.clusters.append(root); + + QList meshAttributes; + foreach(auto &primitive, _file.meshes[node.mesh].primitives) { + QList keys = primitive.attributes.values.keys(); + foreach (auto &key, keys) { + if (!meshAttributes.contains(key)) { + meshAttributes.push_back(key); } } - HFMCluster root; - root.jointIndex = 0; - root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex]; - root.inverseBindTransform = Transform(root.inverseBindMatrix); - mesh.clusters.append(root); + } + foreach(auto &primitive, _file.meshes[node.mesh].primitives) { HFMMeshPart part = HFMMeshPart(); int indicesAccessorIdx = primitive.indices; GLTFAccessor& indicesAccessor = _file.accessors[indicesAccessorIdx]; - GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView]; - GLTFBuffer& indicesBuffer = _file.buffers[indicesBufferview.buffer]; - int indicesAccBoffset = indicesAccessor.defined["byteOffset"] ? indicesAccessor.byteOffset : 0; + // Buffers + QVector indices; + QVector vertices; + int verticesStride = 3; + QVector normals; + int normalStride = 3; + QVector tangents; + int tangentStride = 4; + QVector texcoords; + int texCoordStride = 2; + QVector texcoords2; + int texCoord2Stride = 2; + QVector colors; + int colorStride = 3; + QVector joints; + int jointStride = 4; + QVector weights; + int weightStride = 4; - QVector raw_indices; - QVector raw_vertices; - QVector raw_normals; - - bool success = addArrayOfType(indicesBuffer.blob, - indicesBufferview.byteOffset + indicesAccBoffset, - indicesAccessor.count, - part.triangleIndices, - indicesAccessor.type, - indicesAccessor.componentType); + bool success = addArrayFromAccessor(indicesAccessor, indices); if (!success) { qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url; continue; } + // Increment the triangle indices by the current mesh vertex count so each mesh part can all reference the same buffers within the mesh + int prevMeshVerticesCount = mesh.vertices.count(); + QList keys = primitive.attributes.values.keys(); QVector clusterJoints; QVector clusterWeights; @@ -997,147 +1071,391 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& int accessorIdx = primitive.attributes.values[key]; GLTFAccessor& accessor = _file.accessors[accessorIdx]; - GLTFBufferView& bufferview = _file.bufferviews[accessor.bufferView]; - GLTFBuffer& buffer = _file.buffers[bufferview.buffer]; - int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0; if (key == "POSITION") { - QVector vertices; - success = addArrayOfType(buffer.blob, - bufferview.byteOffset + accBoffset, - accessor.count, vertices, - accessor.type, - accessor.componentType); + if (accessor.type != GLTFAccessorType::VEC3) { + qWarning(modelformat) << "Invalid accessor type on glTF POSITION data for model " << _url; + continue; + } + + success = addArrayFromAccessor(accessor, vertices); if (!success) { qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url; continue; } - for (int n = 0; n < vertices.size(); n = n + 3) { - mesh.vertices.push_back(glm::vec3(vertices[n], vertices[n + 1], vertices[n + 2])); - } } else if (key == "NORMAL") { - QVector normals; - success = addArrayOfType(buffer.blob, - bufferview.byteOffset + accBoffset, - accessor.count, - normals, - accessor.type, - accessor.componentType); + if (accessor.type != GLTFAccessorType::VEC3) { + qWarning(modelformat) << "Invalid accessor type on glTF NORMAL data for model " << _url; + continue; + } + + success = addArrayFromAccessor(accessor, normals); if (!success) { qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url; continue; } - for (int n = 0; n < normals.size(); n = n + 3) { - mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2])); - } - } else if (key == "COLOR_0") { - QVector colors; - success = addArrayOfType(buffer.blob, - bufferview.byteOffset + accBoffset, - accessor.count, - colors, - accessor.type, - accessor.componentType); - if (!success) { - qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url; + } else if (key == "TANGENT") { + if (accessor.type == GLTFAccessorType::VEC4) { + tangentStride = 4; + } else if (accessor.type == GLTFAccessorType::VEC3) { + tangentStride = 3; + } else { + qWarning(modelformat) << "Invalid accessor type on glTF TANGENT data for model " << _url; continue; } - int stride = (accessor.type == GLTFAccessorType::VEC4) ? 4 : 3; - for (int n = 0; n < colors.size() - 3; n += stride) { - mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2])); - } - } else if (key == "TANGENT") { - QVector tangents; - success = addArrayOfType(buffer.blob, - bufferview.byteOffset + accBoffset, - accessor.count, - tangents, - accessor.type, - accessor.componentType); + + success = addArrayFromAccessor(accessor, tangents); if (!success) { qWarning(modelformat) << "There was a problem reading glTF TANGENT data for model " << _url; + tangentStride = 0; continue; } - // tangents can be a vec3 or a vec4 which includes a w component (of -1 or 1) - int stride = (accessor.type == GLTFAccessorType::VEC4) ? 4 : 3; - for (int n = 0; n < tangents.size() - 3; n += stride) { - float tanW = stride == 4 ? tangents[n + 3] : 1; - mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tanW * tangents[n + 2])); - } } else if (key == "TEXCOORD_0") { - QVector texcoords; - success = addArrayOfType(buffer.blob, - bufferview.byteOffset + accBoffset, - accessor.count, - texcoords, - accessor.type, - accessor.componentType); + success = addArrayFromAccessor(accessor, texcoords); if (!success) { qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url; continue; } - for (int n = 0; n < texcoords.size(); n = n + 2) { - mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1])); + + if (accessor.type != GLTFAccessorType::VEC2) { + qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_0 data for model " << _url; + continue; } } else if (key == "TEXCOORD_1") { - QVector texcoords; - success = addArrayOfType(buffer.blob, - bufferview.byteOffset + accBoffset, - accessor.count, - texcoords, - accessor.type, - accessor.componentType); + success = addArrayFromAccessor(accessor, texcoords2); if (!success) { qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url; continue; } - for (int n = 0; n < texcoords.size(); n = n + 2) { - mesh.texCoords1.push_back(glm::vec2(texcoords[n], texcoords[n + 1])); + + if (accessor.type != GLTFAccessorType::VEC2) { + qWarning(modelformat) << "Invalid accessor type on glTF TEXCOORD_1 data for model " << _url; + continue; + } + } else if (key == "COLOR_0") { + if (accessor.type == GLTFAccessorType::VEC4) { + colorStride = 4; + } else if (accessor.type == GLTFAccessorType::VEC3) { + colorStride = 3; + } else { + qWarning(modelformat) << "Invalid accessor type on glTF COLOR_0 data for model " << _url; + continue; + } + + success = addArrayFromAccessor(accessor, colors); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url; + continue; } } else if (key == "JOINTS_0") { - QVector joints; - success = addArrayOfType(buffer.blob, - bufferview.byteOffset + accBoffset, - accessor.count, - joints, - accessor.type, - accessor.componentType); + if (accessor.type == GLTFAccessorType::VEC4) { + jointStride = 4; + } else if (accessor.type == GLTFAccessorType::VEC3) { + jointStride = 3; + } else if (accessor.type == GLTFAccessorType::VEC2) { + jointStride = 2; + } else if (accessor.type == GLTFAccessorType::SCALAR) { + jointStride = 1; + } else { + qWarning(modelformat) << "Invalid accessor type on glTF JOINTS_0 data for model " << _url; + continue; + } + + success = addArrayFromAccessor(accessor, joints); if (!success) { qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url; continue; } - for (int n = 0; n < joints.size(); n++) { - clusterJoints.push_back(joints[n]); - } } else if (key == "WEIGHTS_0") { - QVector weights; - success = addArrayOfType(buffer.blob, - bufferview.byteOffset + accBoffset, - accessor.count, - weights, - accessor.type, - accessor.componentType); + if (accessor.type == GLTFAccessorType::VEC4) { + weightStride = 4; + } else if (accessor.type == GLTFAccessorType::VEC3) { + weightStride = 3; + } else if (accessor.type == GLTFAccessorType::VEC2) { + weightStride = 2; + } else if (accessor.type == GLTFAccessorType::SCALAR) { + weightStride = 1; + } else { + qWarning(modelformat) << "Invalid accessor type on glTF WEIGHTS_0 data for model " << _url; + continue; + } + + success = addArrayFromAccessor(accessor, weights); if (!success) { qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url; continue; } - for (int n = 0; n < weights.size(); n++) { - clusterWeights.push_back(weights[n]); + } + } + + // Validation stage + if (indices.count() == 0) { + qWarning(modelformat) << "Missing indices for model " << _url; + continue; + } + if (vertices.count() == 0) { + qWarning(modelformat) << "Missing vertices for model " << _url; + continue; + } + + int partVerticesCount = vertices.size() / 3; + + // generate the normals if they don't exist + if (normals.size() == 0) { + QVector newIndices; + QVector newVertices; + QVector newNormals; + QVector newTexcoords; + QVector newTexcoords2; + QVector newColors; + QVector newJoints; + QVector newWeights; + + for (int n = 0; n < indices.size(); n = n + 3) { + int v1_index = (indices[n + 0] * 3); + int v2_index = (indices[n + 1] * 3); + int v3_index = (indices[n + 2] * 3); + + glm::vec3 v1 = glm::vec3(vertices[v1_index], vertices[v1_index + 1], vertices[v1_index + 2]); + glm::vec3 v2 = glm::vec3(vertices[v2_index], vertices[v2_index + 1], vertices[v2_index + 2]); + glm::vec3 v3 = glm::vec3(vertices[v3_index], vertices[v3_index + 1], vertices[v3_index + 2]); + + newVertices.append(v1.x); + newVertices.append(v1.y); + newVertices.append(v1.z); + newVertices.append(v2.x); + newVertices.append(v2.y); + newVertices.append(v2.z); + newVertices.append(v3.x); + newVertices.append(v3.y); + newVertices.append(v3.z); + + glm::vec3 norm = glm::normalize(glm::cross(v2 - v1, v3 - v1)); + + newNormals.append(norm.x); + newNormals.append(norm.y); + newNormals.append(norm.z); + newNormals.append(norm.x); + newNormals.append(norm.y); + newNormals.append(norm.z); + newNormals.append(norm.x); + newNormals.append(norm.y); + newNormals.append(norm.z); + + if (texcoords.size() == partVerticesCount * texCoordStride) { + GLTF_APPEND_ARRAY_2(newTexcoords, texcoords) + } + + if (texcoords2.size() == partVerticesCount * texCoord2Stride) { + GLTF_APPEND_ARRAY_2(newTexcoords2, texcoords2) + } + + if (colors.size() == partVerticesCount * colorStride) { + if (colorStride == 4) { + GLTF_APPEND_ARRAY_4(newColors, colors) + } else { + GLTF_APPEND_ARRAY_3(newColors, colors) + } + } + + if (joints.size() == partVerticesCount * jointStride) { + if (jointStride == 4) { + GLTF_APPEND_ARRAY_4(newJoints, joints) + } else if (jointStride == 3) { + GLTF_APPEND_ARRAY_3(newJoints, joints) + } else if (jointStride == 2) { + GLTF_APPEND_ARRAY_2(newJoints, joints) + } else { + GLTF_APPEND_ARRAY_1(newJoints, joints) + } + } + + if (weights.size() == partVerticesCount * weightStride) { + if (weightStride == 4) { + GLTF_APPEND_ARRAY_4(newWeights, weights) + } else if (weightStride == 3) { + GLTF_APPEND_ARRAY_3(newWeights, weights) + } else if (weightStride == 2) { + GLTF_APPEND_ARRAY_2(newWeights, weights) + } else { + GLTF_APPEND_ARRAY_1(newWeights, weights) + } + } + newIndices.append(n); + newIndices.append(n + 1); + newIndices.append(n + 2); + } + + vertices = newVertices; + normals = newNormals; + tangents = QVector(); + texcoords = newTexcoords; + texcoords2 = newTexcoords2; + colors = newColors; + joints = newJoints; + weights = newWeights; + indices = newIndices; + + partVerticesCount = vertices.size() / 3; + } + + QVector validatedIndices; + for (int n = 0; n < indices.count(); n++) { + if (indices[n] < partVerticesCount) { + validatedIndices.push_back(indices[n] + prevMeshVerticesCount); + } else { + validatedIndices = QVector(); + break; + } + } + + if (validatedIndices.size() == 0) { + qWarning(modelformat) << "Indices out of range for model " << _url; + continue; + } + + part.triangleIndices.append(validatedIndices); + + for (int n = 0; n < vertices.size(); n = n + verticesStride) { + mesh.vertices.push_back(glm::vec3(vertices[n], vertices[n + 1], vertices[n + 2])); + } + + for (int n = 0; n < normals.size(); n = n + normalStride) { + mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2])); + } + + // TODO: add correct tangent generation + if (tangents.size() == partVerticesCount * tangentStride) { + for (int n = 0; n < tangents.size(); n += tangentStride) { + float tanW = tangentStride == 4 ? tangents[n + 3] : 1; + mesh.tangents.push_back(glm::vec3(tanW * tangents[n], tangents[n + 1], tanW * tangents[n + 2])); + } + } else { + if (meshAttributes.contains("TANGENT")) { + for (int i = 0; i < partVerticesCount; i++) { + mesh.tangents.push_back(glm::vec3(0.0f, 0.0f, 0.0f)); } } } - + + if (texcoords.size() == partVerticesCount * texCoordStride) { + for (int n = 0; n < texcoords.size(); n = n + 2) { + mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1])); + } + } else { + if (meshAttributes.contains("TEXCOORD_0")) { + for (int i = 0; i < partVerticesCount; i++) { + mesh.texCoords.push_back(glm::vec2(0.0f, 0.0f)); + } + } + } + + if (texcoords2.size() == partVerticesCount * texCoord2Stride) { + for (int n = 0; n < texcoords2.size(); n = n + 2) { + mesh.texCoords1.push_back(glm::vec2(texcoords2[n], texcoords2[n + 1])); + } + } else { + if (meshAttributes.contains("TEXCOORD_1")) { + for (int i = 0; i < partVerticesCount; i++) { + mesh.texCoords1.push_back(glm::vec2(0.0f, 0.0f)); + } + } + } + + if (colors.size() == partVerticesCount * colorStride) { + for (int n = 0; n < colors.size(); n += colorStride) { + mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2])); + } + } else { + if (meshAttributes.contains("COLOR_0")) { + for (int i = 0; i < partVerticesCount; i++) { + mesh.colors.push_back(glm::vec3(1.0f, 1.0f, 1.0f)); + } + } + } + + if (joints.size() == partVerticesCount * jointStride) { + for (int n = 0; n < joints.size(); n += jointStride) { + clusterJoints.push_back(joints[n]); + if (jointStride > 1) { + clusterJoints.push_back(joints[n + 1]); + if (jointStride > 2) { + clusterJoints.push_back(joints[n + 2]); + if (jointStride > 3) { + clusterJoints.push_back(joints[n + 3]); + } else { + clusterJoints.push_back(0); + } + } else { + clusterJoints.push_back(0); + clusterJoints.push_back(0); + } + } else { + clusterJoints.push_back(0); + clusterJoints.push_back(0); + clusterJoints.push_back(0); + } + } + } else { + if (meshAttributes.contains("JOINTS_0")) { + for (int i = 0; i < partVerticesCount; i++) { + for (int j = 0; j < 4; j++) { + clusterJoints.push_back(0); + } + } + } + } + + if (weights.size() == partVerticesCount * weightStride) { + for (int n = 0; n < weights.size(); n += weightStride) { + clusterWeights.push_back(weights[n]); + if (weightStride > 1) { + clusterWeights.push_back(weights[n + 1]); + if (weightStride > 2) { + clusterWeights.push_back(weights[n + 2]); + if (weightStride > 3) { + clusterWeights.push_back(weights[n + 3]); + } else { + clusterWeights.push_back(0.0f); + } + } else { + clusterWeights.push_back(0.0f); + clusterWeights.push_back(0.0f); + } + } else { + clusterWeights.push_back(0.0f); + clusterWeights.push_back(0.0f); + clusterWeights.push_back(0.0f); + } + } + } else { + if (meshAttributes.contains("WEIGHTS_0")) { + for (int i = 0; i < partVerticesCount; i++) { + clusterWeights.push_back(1.0f); + for (int j = 1; j < 4; j++) { + clusterWeights.push_back(0.0f); + } + } + } + } + // Build weights (adapted from FBXSerializer.cpp) if (hfmModel.hasSkeletonJoints) { - int numClusterIndices = clusterJoints.size(); + int prevMeshClusterIndexCount = mesh.clusterIndices.count(); + int prevMeshClusterWeightCount = mesh.clusterWeights.count(); const int WEIGHTS_PER_VERTEX = 4; const float ALMOST_HALF = 0.499f; - int numVertices = mesh.vertices.size(); - mesh.clusterIndices.fill(mesh.clusters.size() - 1, numClusterIndices); - mesh.clusterWeights.fill(0, numClusterIndices); + int numVertices = mesh.vertices.size() - prevMeshVerticesCount; + + // Append new cluster indices and weights for this mesh part + for (int i = 0; i < numVertices * WEIGHTS_PER_VERTEX; i++) { + mesh.clusterIndices.push_back(mesh.clusters.size() - 1); + mesh.clusterWeights.push_back(0); + } for (int c = 0; c < clusterJoints.size(); c++) { - mesh.clusterIndices[c] = originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[c]]]; + mesh.clusterIndices[prevMeshClusterIndexCount + c] = + originalToNewNodeIndexMap[_file.skins[node.skin].joints[clusterJoints[c]]]; } // normalize and compress to 16-bits @@ -1151,10 +1469,10 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& if (totalWeight > 0.0f) { float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { - mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF); + mesh.clusterWeights[prevMeshClusterWeightCount + k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF); } } else { - mesh.clusterWeights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF); + mesh.clusterWeights[prevMeshClusterWeightCount + j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF); } } } @@ -1259,6 +1577,20 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mesh.meshIndex = hfmModel.meshes.size(); } + + mesh.meshExtents.reset(); + foreach(const glm::vec3& vertex, mesh.vertices) { + mesh.meshExtents.addPoint(vertex); + hfmModel.meshExtents.addPoint(vertex); + } + + // Add epsilon to mesh extents to compensate for planar meshes + mesh.meshExtents.minimum -= glm::vec3(EPSILON, EPSILON, EPSILON); + mesh.meshExtents.maximum += glm::vec3(EPSILON, EPSILON, EPSILON); + hfmModel.meshExtents.minimum -= glm::vec3(EPSILON, EPSILON, EPSILON); + hfmModel.meshExtents.maximum += glm::vec3(EPSILON, EPSILON, EPSILON); + + mesh.meshIndex = hfmModel.meshes.size(); } nodecount++; } @@ -1412,10 +1744,6 @@ HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) { void GLTFSerializer::setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& material) { - if (material.defined["name"]) { - fbxmat.name = fbxmat.materialID = material.name; - } - if (material.defined["emissiveFactor"] && material.emissiveFactor.size() == 3) { glm::vec3 emissive = glm::vec3(material.emissiveFactor[0], material.emissiveFactor[1], @@ -1552,7 +1880,74 @@ bool GLTFSerializer::addArrayOfType(const hifi::ByteArray& bin, int byteOffset, return false; } -void GLTFSerializer::retriangulate(const QVector& inIndices, const QVector& in_vertices, +template +bool GLTFSerializer::addArrayFromAccessor(GLTFAccessor& accessor, QVector& outarray) { + bool success = true; + + if (accessor.defined["bufferView"]) { + GLTFBufferView& bufferview = _file.bufferviews[accessor.bufferView]; + GLTFBuffer& buffer = _file.buffers[bufferview.buffer]; + + int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0; + + success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, accessor.count, outarray, accessor.type, + accessor.componentType); + } else { + for (int i = 0; i < accessor.count; i++) { + T value; + memset(&value, 0, sizeof(T)); // Make sure the dummy array is initalised to zero. + outarray.push_back(value); + } + } + + if (success) { + if (accessor.defined["sparse"]) { + QVector out_sparse_indices_array; + + GLTFBufferView& sparseIndicesBufferview = _file.bufferviews[accessor.sparse.indices.bufferView]; + GLTFBuffer& sparseIndicesBuffer = _file.buffers[sparseIndicesBufferview.buffer]; + + int accSIBoffset = accessor.sparse.indices.defined["byteOffset"] ? accessor.sparse.indices.byteOffset : 0; + + success = addArrayOfType(sparseIndicesBuffer.blob, sparseIndicesBufferview.byteOffset + accSIBoffset, + accessor.sparse.count, out_sparse_indices_array, GLTFAccessorType::SCALAR, + accessor.sparse.indices.componentType); + if (success) { + QVector out_sparse_values_array; + + GLTFBufferView& sparseValuesBufferview = _file.bufferviews[accessor.sparse.values.bufferView]; + GLTFBuffer& sparseValuesBuffer = _file.buffers[sparseValuesBufferview.buffer]; + + int accSVBoffset = accessor.sparse.values.defined["byteOffset"] ? accessor.sparse.values.byteOffset : 0; + + success = addArrayOfType(sparseValuesBuffer.blob, sparseValuesBufferview.byteOffset + accSVBoffset, + accessor.sparse.count, out_sparse_values_array, accessor.type, accessor.componentType); + + if (success) { + for (int i = 0; i < accessor.sparse.count; i++) { + if ((i * 3) + 2 < out_sparse_values_array.size()) { + if ((out_sparse_indices_array[i] * 3) + 2 < outarray.length()) { + for (int j = 0; j < 3; j++) { + outarray[(out_sparse_indices_array[i] * 3) + j] = out_sparse_values_array[(i * 3) + j]; + } + } else { + success = false; + break; + } + } else { + success = false; + break; + } + } + } + } + } + } + + return success; +} + +void GLTFSerializer::retriangulate(const QVector& inIndices, const QVector& in_vertices, const QVector& in_normals, QVector& outIndices, QVector& out_vertices, QVector& out_normals) { for (int i = 0; i < inIndices.size(); i = i + 3) { @@ -1717,7 +2112,7 @@ void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) { qCDebug(modelformat) << "---------------- Joints ----------------"; - foreach(HFMJoint joint, hfmModel.joints) { + foreach (HFMJoint joint, hfmModel.joints) { qCDebug(modelformat) << "\n"; qCDebug(modelformat) << " shapeInfo.avgPoint =" << joint.shapeInfo.avgPoint; qCDebug(modelformat) << " shapeInfo.debugLines =" << joint.shapeInfo.debugLines; diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index b1c1bc4e44..4d72805863 100755 --- a/libraries/fbx/src/GLTFSerializer.h +++ b/libraries/fbx/src/GLTFSerializer.h @@ -481,6 +481,49 @@ namespace GLTFAccessorComponentType { }; } struct GLTFAccessor { + struct GLTFAccessorSparse { + struct GLTFAccessorSparseIndices { + int bufferView; + int byteOffset{ 0 }; + int componentType; + + QMap defined; + void dump() { + if (defined["bufferView"]) { + qCDebug(modelformat) << "bufferView: " << bufferView; + } + if (defined["byteOffset"]) { + qCDebug(modelformat) << "byteOffset: " << byteOffset; + } + if (defined["componentType"]) { + qCDebug(modelformat) << "componentType: " << componentType; + } + } + }; + struct GLTFAccessorSparseValues { + int bufferView; + int byteOffset{ 0 }; + + QMap defined; + void dump() { + if (defined["bufferView"]) { + qCDebug(modelformat) << "bufferView: " << bufferView; + } + if (defined["byteOffset"]) { + qCDebug(modelformat) << "byteOffset: " << byteOffset; + } + } + }; + + int count; + GLTFAccessorSparseIndices indices; + GLTFAccessorSparseValues values; + + QMap defined; + void dump() { + + } + }; int bufferView; int byteOffset { 0 }; int componentType; //required @@ -489,6 +532,7 @@ struct GLTFAccessor { bool normalized{ false }; QVector max; QVector min; + GLTFAccessorSparse sparse; QMap defined; void dump() { if (defined["bufferView"]) { @@ -521,6 +565,10 @@ struct GLTFAccessor { qCDebug(modelformat) << m; } } + if (defined["sparse"]) { + qCDebug(modelformat) << "sparse: "; + sparse.dump(); + } } }; @@ -763,6 +811,11 @@ private: int& outidx, QMap& defined); bool setAsset(const QJsonObject& object); + + GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseIndices createAccessorSparseIndices(const QJsonObject& object); + GLTFAccessor::GLTFAccessorSparse::GLTFAccessorSparseValues createAccessorSparseValues(const QJsonObject& object); + GLTFAccessor::GLTFAccessorSparse createAccessorSparse(const QJsonObject& object); + bool addAccessor(const QJsonObject& object); bool addAnimation(const QJsonObject& object); bool addBufferView(const QJsonObject& object); @@ -782,11 +835,14 @@ private: template bool readArray(const hifi::ByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType); - + template bool addArrayOfType(const hifi::ByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType, int componentType); + template + bool addArrayFromAccessor(GLTFAccessor& accessor, QVector& outarray); + void retriangulate(const QVector& in_indices, const QVector& in_vertices, const QVector& in_normals, QVector& out_indices, QVector& out_vertices, QVector& out_normals); diff --git a/libraries/platform/CMakeLists.txt b/libraries/platform/CMakeLists.txt new file mode 100644 index 0000000000..2d71babe6f --- /dev/null +++ b/libraries/platform/CMakeLists.txt @@ -0,0 +1,5 @@ +set(TARGET_NAME platform) +setup_hifi_library() + +link_hifi_libraries(shared) +target_json() diff --git a/libraries/platform/src/AndroidPlatform.cpp b/libraries/platform/src/AndroidPlatform.cpp new file mode 100644 index 0000000000..e998f6f938 --- /dev/null +++ b/libraries/platform/src/AndroidPlatform.cpp @@ -0,0 +1,40 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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 "AndroidPlatform.h" +#include "platformJsonKeys.h" + +#include +#include +using namespace platform; + +void AndroidInstance::enumerateCpu() { + json cpu; + cpu["cpuBrand"] = ""; + cpu["cpuModel"] = ""; + cpu["cpuClockSpeed"] = ""; + cpu["cpuNumCores"] = ""; + _cpu.push_back(cpu); +} + +void AndroidInstance::enumerateGpu() { + GPUIdent* ident = GPUIdent::getInstance(); + json gpu = {}; + gpu["gpuName"] = ident->getName().toUtf8().constData(); + gpu["gpuMemory"] = ident->getMemory(); + gpu["gpuDriver"] = ident->getDriver().toUtf8().constData(); + + _gpu.push_back(gpu); + _display = ident->getOutput(); +} + +void AndroidInstance::enumerateMemory() { + json ram = {}; + + _memory.push_back(ram); +} diff --git a/libraries/platform/src/AndroidPlatform.h b/libraries/platform/src/AndroidPlatform.h new file mode 100644 index 0000000000..17efbb45e3 --- /dev/null +++ b/libraries/platform/src/AndroidPlatform.h @@ -0,0 +1,25 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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_AndroidPlatform_h +#define hifi_AndroidPlatform_h + +#include "platformInstance.h" + +namespace platform { + class AndroidInstance : public Instance { + + public: + void enumerateCpu() override; + void enumerateMemory() override; + void enumerateGpu() override; + }; + +} // namespace platform + +#endif //hifi_androidplatform_h diff --git a/libraries/platform/src/LinuxPlatform.cpp b/libraries/platform/src/LinuxPlatform.cpp new file mode 100644 index 0000000000..96c105826f --- /dev/null +++ b/libraries/platform/src/LinuxPlatform.cpp @@ -0,0 +1,42 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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 "LinuxPlatform.h" +#include "platformJsonKeys.h" +#include +#include + +using namespace platform; +void LinuxInstance::enumerateCpu() { + json cpu = {}; + + cpu["cpuBrand"] = ""; + cpu["cpuModel"] = ""; + cpu["cpuClockSpeed"] = ""; + cpu["cpuNumCores"] = ""; + + _cpu.push_back(cpu); +} + +void LinuxInstance::enumerateGpu() { + GPUIdent* ident = GPUIdent::getInstance(); + json gpu = {}; + gpu["gpuName"] = ident->getName().toUtf8().constData(); + gpu["gpuMemory"] = ident->getMemory(); + gpu["gpuDriver"] = ident->getDriver().toUtf8().constData(); + + _gpu.push_back(gpu); + _display = ident->getOutput(); +} + +void LinuxInstance::enumerateMemory() { + json ram = {}; + + + _memory.push_back(ram); +} diff --git a/libraries/platform/src/LinuxPlatform.h b/libraries/platform/src/LinuxPlatform.h new file mode 100644 index 0000000000..1af4ce7444 --- /dev/null +++ b/libraries/platform/src/LinuxPlatform.h @@ -0,0 +1,25 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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_LinuxPlatform_h +#define hifi_LinuxPlatform_h + +#include "platformInstance.h" + +namespace platform { + class LinuxInstance : public Instance { + + public: + void enumerateCpu() override; + void enumerateMemory() override; + void enumerateGpu() override; + }; + +} // namespace platform + +#endif //hifi_linuxPlaform_h diff --git a/libraries/platform/src/MACOSPlatform.cpp b/libraries/platform/src/MACOSPlatform.cpp new file mode 100644 index 0000000000..172cd642aa --- /dev/null +++ b/libraries/platform/src/MACOSPlatform.cpp @@ -0,0 +1,86 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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 "MACOSPlatform.h" +#include "platformJsonKeys.h" +#include +#include +#include + +#ifdef Q_OS_MAC +#include +#include +#endif + +using namespace platform; + +static void getCpuId( uint32_t* p, uint32_t ax ) +{ +#ifdef Q_OS_MAC + __asm __volatile + ( "movl %%ebx, %%esi\n\t" + "cpuid\n\t" + "xchgl %%ebx, %%esi" + : "=a" (p[0]), "=S" (p[1]), + "=c" (p[2]), "=d" (p[3]) + : "0" (ax) + ); +#endif +} + +void MACOSInstance::enumerateCpu() { + json cpu = {}; + uint32_t cpuInfo[4]={0,0,0,0}; + char CPUBrandString[16]; + char CPUModelString[16]; + char CPUClockString[16]; + uint32_t nExIds; + getCpuId(cpuInfo, 0x80000000); + nExIds = cpuInfo[0]; + + for (uint32_t i = 0x80000000; i <= nExIds; ++i) { + getCpuId(cpuInfo, i); + // Interpret CPU brand string + if (i == 0x80000002) { + memcpy(CPUBrandString, cpuInfo, sizeof(cpuInfo)); + } else if (i == 0x80000003) { + memcpy(CPUModelString, cpuInfo, sizeof(cpuInfo)); + } else if (i == 0x80000004) { + memcpy(CPUClockString, cpuInfo, sizeof(cpuInfo)); + } + } + + cpu["cpuBrand"] = CPUBrandString; + cpu["cpuModel"] = CPUModelString; + cpu["cpuClockSpeed"] = CPUClockString; + cpu["cpuNumCores"] = std::thread::hardware_concurrency(); + + _cpu.push_back(cpu); +} + +void MACOSInstance::enumerateGpu() { + GPUIdent* ident = GPUIdent::getInstance(); + json gpu = {}; + gpu["gpuName"] = ident->getName().toUtf8().constData(); + gpu["gpuMemory"] = ident->getMemory(); + gpu["gpuDriver"] = ident->getDriver().toUtf8().constData(); + + _gpu.push_back(gpu); + _display = ident->getOutput(); +} + +void MACOSInstance::enumerateMemory() { + json ram = {}; + +#ifdef Q_OS_MAC + long pages = sysconf(_SC_PHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + ram["totalMemory"] = pages * page_size;; +#endif + _memory.push_back(ram); +} diff --git a/libraries/platform/src/MACOSPlatform.h b/libraries/platform/src/MACOSPlatform.h new file mode 100644 index 0000000000..287e0c0ed7 --- /dev/null +++ b/libraries/platform/src/MACOSPlatform.h @@ -0,0 +1,25 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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_MACOSPlatform_h +#define hifi_MACOSPlatform_h + +#include "platformInstance.h" + +namespace platform { + class MACOSInstance : public Instance { + + public: + void enumerateCpu() override; + void enumerateMemory() override; + void enumerateGpu() override; + }; + +} // namespace platform + +#endif //hifi_winplatform_h diff --git a/libraries/platform/src/WINPlatform.cpp b/libraries/platform/src/WINPlatform.cpp new file mode 100644 index 0000000000..601a9d7290 --- /dev/null +++ b/libraries/platform/src/WINPlatform.cpp @@ -0,0 +1,82 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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 "WINPlatform.h" +#include "platformJsonKeys.h" +#ifdef Q_OS_WINDOWS +#include +#include +#endif + +#include +#include +#include + + +using namespace platform; + +void WINInstance::enumerateCpu() { + json cpu = {}; + +#ifdef Q_OS_WINDOWS + int CPUInfo[4] = { -1 }; + unsigned nExIds; + unsigned int i = 0; + char CPUBrandString[16]; + char CPUModelString[16]; + char CPUClockString[16]; + // Get the information associated with each extended ID. + __cpuid(CPUInfo, 0x80000000); + nExIds = CPUInfo[0]; + + for (i = 0x80000000; i <= nExIds; ++i) { + __cpuid(CPUInfo, i); + // Interpret CPU brand string + if (i == 0x80000002) { + memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo)); + } else if (i == 0x80000003) { + memcpy(CPUModelString, CPUInfo, sizeof(CPUInfo)); + } else if (i == 0x80000004) { + memcpy(CPUClockString, CPUInfo, sizeof(CPUInfo)); + } + } + + cpu["cpuBrand"] = CPUBrandString; + cpu["cpuModel"] = CPUModelString; + cpu["cpuClockSpeed"] = CPUClockString; + cpu["cpuNumCores"] = std::thread::hardware_concurrency(); +#endif + + _cpu.push_back(cpu); +} + +void WINInstance::enumerateGpu() { + + GPUIdent* ident = GPUIdent::getInstance(); + + json gpu = {}; + gpu["gpuName"] = ident->getName().toUtf8().constData(); + gpu["gpuMemory"] = ident->getMemory(); + gpu["gpuDriver"] = ident->getDriver().toUtf8().constData(); + + _gpu.push_back(gpu); + _display = ident->getOutput(); +} + +void WINInstance::enumerateMemory() { + json ram = {}; + +#ifdef Q_OS_WINDOWS + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + GlobalMemoryStatusEx(&statex); + int totalRam = statex.ullTotalPhys / 1024 / 1024; + ram[jsonKeys::totalMemory] = totalRam; +#endif + _memory.push_back(ram); +} diff --git a/libraries/platform/src/WINPlatform.h b/libraries/platform/src/WINPlatform.h new file mode 100644 index 0000000000..4d466a9b7e --- /dev/null +++ b/libraries/platform/src/WINPlatform.h @@ -0,0 +1,25 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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_WinPlatform_h +#define hifi_WinPlatform_h + +#include "platformInstance.h" + +namespace platform { + class WINInstance : public Instance { + + public: + void enumerateCpu() override; + void enumerateMemory() override; + void enumerateGpu() override; + + }; +} // namespace platform + +#endif //hifi_winplatform_h diff --git a/libraries/platform/src/platform.cpp b/libraries/platform/src/platform.cpp new file mode 100644 index 0000000000..27e773d435 --- /dev/null +++ b/libraries/platform/src/platform.cpp @@ -0,0 +1,79 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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 "platform.h" + +#include + +#if defined(Q_OS_WIN) +#include "WINPlatform.h" +#elif defined(Q_OS_MAC) +#include "MACOSPlatform.h" +#elif defined(Q_OS_ANDROID) +#include "AndroidPlatform.h" +#elif defined(Q_OS_LINUX) +#include "LinuxPlatform.h" +#endif + +using namespace platform; + +Instance *_instance; + +void platform::create() { +#if defined(Q_OS_WIN) + _instance =new WINInstance(); +#elif defined(Q_OS_MAC) + _instance = new MACOSInstance(); +#elif defined(Q_OS_ANDROID) + _instance= new AndroidInstance(); +#elif defined(Q_OS_LINUX) + _instance= new LinuxInstance(); +#endif +} + +void platform::destroy() { + if(_instance) + delete _instance; +} + +bool platform::enumeratePlatform() { + return _instance->enumeratePlatform(); +} + +int platform::getNumCPU() { + return _instance->getNumCPU(); +} + +json platform::getCPU(int index) { + return _instance->getCPU(index); +} + +int platform::getNumGPU() { + return _instance->getNumGPU(); +} + +json platform::getGPU(int index) { + return _instance->getGPU(index); +} + +int platform::getNumDisplay() { + return _instance->getNumDisplay(); +} + +json platform::getDisplay(int index) { + return _instance->getDisplay(index); +} + +int platform::getNumMemory() { + return _instance->getNumMemory(); +} + +json platform::getMemory(int index) { + return _instance->getMemory(index); +} diff --git a/libraries/platform/src/platform.h b/libraries/platform/src/platform.h new file mode 100644 index 0000000000..895114ba6d --- /dev/null +++ b/libraries/platform/src/platform.h @@ -0,0 +1,37 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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_Platform_h +#define hifi_Platform_h + +#include "platformInstance.h" +#include +#include + +namespace platform { + using json = nlohmann::json; + +void create(); +void destroy(); +bool enumeratePlatform(); + +int getNumCPU(); +json getCPU(int index); + +int getNumGPU(); +json getGPU(int index); + +int getNumDisplay(); +json getDisplay(int index); + +int getNumMemory(); +json getMemory(int index); + +} // namespace platform + +#endif // hifi_platform_h diff --git a/libraries/platform/src/platformInstance.cpp b/libraries/platform/src/platformInstance.cpp new file mode 100644 index 0000000000..705f5bd358 --- /dev/null +++ b/libraries/platform/src/platformInstance.cpp @@ -0,0 +1,86 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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 "platform.h" + +#include + +#ifdef Q_OS_WIN +#include "WINPlatform.h" +#endif + +#ifdef Q_OS_MACOS +#include "MACOSPlatform.h" +#endif + +#ifdef Q_OS_LINUX +#endif + +using namespace platform; + +bool Instance::enumeratePlatform() { + enumerateCpu(); + enumerateGpu(); + enumerateMemory(); + return true; +} + +json Instance::getCPU(int index) { + assert(index <(int) _cpu.size()); + if (index >= (int)_cpu.size()) + return json(); + + return _cpu.at(index); +} + +//These are ripe for template.. will work on that next +json Instance::getMemory(int index) { + assert(index <(int) _memory.size()); + if(index >= (int)_memory.size()) + return json(); + + return _memory.at(index); +} + +json Instance::getGPU(int index) { + assert(index <(int) _gpu.size()); + + if (index >=(int) _gpu.size()) + return json(); + + return _gpu.at(index); +} + +json Instance::getDisplay(int index) { + assert(index <(int) _display.size()); + + if (index >=(int) _display.size()) + return json(); + + return _display.at(index); +} + +Instance::~Instance() { + if (_cpu.size() > 0) { + _cpu.clear(); + } + + if (_memory.size() > 0) { + _memory.clear(); + } + + + if (_gpu.size() > 0) { + _gpu.clear(); + } + + if (_display.size() > 0) { + _display.clear(); + } +} diff --git a/libraries/platform/src/platformInstance.h b/libraries/platform/src/platformInstance.h new file mode 100644 index 0000000000..4770200f07 --- /dev/null +++ b/libraries/platform/src/platformInstance.h @@ -0,0 +1,49 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 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_PlatformInstance_h +#define hifi_PlatformInstance_h + +#include +#include + +namespace platform { + using json = nlohmann::json; + +class Instance { +public: + bool virtual enumeratePlatform(); + + int getNumCPU() { return (int)_cpu.size(); } + json getCPU(int index); + + int getNumGPU() { return (int)_gpu.size(); } + json getGPU(int index); + + int getNumMemory() { return (int)_memory.size(); } + json getMemory(int index); + + int getNumDisplay() { return (int)_display.size(); } + json getDisplay(int index); + + void virtual enumerateCpu()=0; + void virtual enumerateMemory()=0; + void virtual enumerateGpu()=0; + + virtual ~Instance(); + +protected: + std::vector _cpu; + std::vector _memory; + std::vector _gpu; + std::vector _display; +}; + +} // namespace platform + +#endif // hifi_platformInstance_h diff --git a/libraries/platform/src/platformJsonKeys.h b/libraries/platform/src/platformJsonKeys.h new file mode 100644 index 0000000000..633add2b7e --- /dev/null +++ b/libraries/platform/src/platformJsonKeys.h @@ -0,0 +1,34 @@ +// +// Created by Amer Cerkic 05/02/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once +#ifndef hifi_PlatformJsonKeys_h +#define hifi_PlatformJsonKeys_h + +namespace platform { + namespace jsonKeys{ +#if 0 + static const char* cpuBrand { "cpuBrand"}; + static const char* cpuModel {"cpuModel"}; + static const char* cpuClockSpeed {"clockSpeed"}; + static const char* cpuNumCores { "numCores"}; + static const char* gpuName {"GpuName"}; + static const char* gpuMemory {"gpuMemory"}; + static const char* gpuDriver {"gpuDriver"}; + static const char* totalMemory {"totalMem"}; + static const char* displayDescription { "description"}; + static const char* displayName {"deviceName"}; + static const char* displayCoordsLeft {"coordinatesleft"}; + static const char* displayCoordsRight { "coordinatesright"}; + static const char* displayCoordsTop { "coordinatestop"}; + static const char* displayCoordsBottom { "coordinatesbottom"}; +#endif + } + +} // namespace platform + +#endif diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index a07e6cea04..9dc1d7002d 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -211,7 +211,7 @@ public: virtual void cycleDebugOutput() {} void waitForPresent(); - float getAveragePresentTime() { return _movingAveragePresent.average / (float)USECS_PER_MSEC; } // in msec + float getAveragePresentTime() { return _movingAveragePresent.average / (float)USECS_PER_MSEC; } // in msec std::function getHUDOperator(); diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 093e3249f2..eb3e286843 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -15,3 +15,5 @@ endif() target_zlib() target_nsight() +target_json() + diff --git a/libraries/shared/src/GPUIdent.cpp b/libraries/shared/src/GPUIdent.cpp index 3b7a6cee40..a78ded483b 100644 --- a/libraries/shared/src/GPUIdent.cpp +++ b/libraries/shared/src/GPUIdent.cpp @@ -12,6 +12,7 @@ #ifdef Q_OS_WIN #include +#include //#include //#include @@ -250,6 +251,22 @@ GPUIdent* GPUIdent::ensureQuery(const QString& vendor, const QString& renderer) */ if (!validAdapterList.empty()) { + for (auto outy = adapterToOutputs.begin(); outy != adapterToOutputs.end(); ++outy) { + + AdapterEntry entry = *outy; + for (auto test = entry.second.begin(); test != entry.second.end(); ++test) { + + nlohmann::json output = {}; + output["description"] = entry.first.first.Description; + output["deviceName"]= test->DeviceName; + output["coordinatesleft"] = test->DesktopCoordinates.left; + output["coordinatesright"] = test->DesktopCoordinates.right; + output["coordinatestop"] = test->DesktopCoordinates.top; + output["coordinatesbottom"] = test->DesktopCoordinates.bottom; + _output.push_back(output); + } + } + auto& adapterEntry = adapterToOutputs[validAdapterList.front()]; std::wstring wDescription(adapterEntry.first.first.Description); diff --git a/libraries/shared/src/GPUIdent.h b/libraries/shared/src/GPUIdent.h index f780a4ddbd..3d47ef8a33 100644 --- a/libraries/shared/src/GPUIdent.h +++ b/libraries/shared/src/GPUIdent.h @@ -15,19 +15,25 @@ #define hifi_GPUIdent_h #include - #include +#include +#include +#include class GPUIdent { + public: uint64_t getMemory() { return _dedicatedMemoryMB; } QString getName() { return _name; } QString getDriver() { return _driver; } bool isValid() { return _isValid; } + const std::vector& getOutput() { return _output; } + // E.g., GPUIdent::getInstance()->getMemory(); static GPUIdent* getInstance(const QString& vendor = "", const QString& renderer = "") { return _instance.ensureQuery(vendor, renderer); } private: + std::vector _output; uint64_t _dedicatedMemoryMB { 0 }; QString _name { "" }; QString _driver { "" };