diff --git a/CMakeLists.txt b/CMakeLists.txt index 25fd4731e6..93b784b462 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# If we're running under the gradle build, HIFI_ANDROID will be set here, but +# If we're running under the gradle build, HIFI_ANDROID will be set here, but # ANDROID will not be set until after the `project` statement. This is the *ONLY* # place you need to use `HIFI_ANDROID` instead of `ANDROID` if (WIN32 AND NOT HIFI_ANDROID) @@ -14,8 +14,12 @@ include("cmake/init.cmake") include("cmake/compiler.cmake") if (BUILD_SCRIBE_ONLY) - add_subdirectory(tools/scribe) - return() + add_subdirectory(tools/scribe) + return() +endif() + +if (NOT DEFINED CLIENT_ONLY) + set(CLIENT_ONLY 0) endif() if (NOT DEFINED SERVER_ONLY) @@ -23,80 +27,93 @@ if (NOT DEFINED SERVER_ONLY) endif() if (ANDROID OR UWP) - set(MOBILE 1) + set(MOBILE 1) else() - set(MOBILE 0) + set(MOBILE 0) endif() +set(BUILD_CLIENT_OPTION ON) +set(BUILD_SERVER_OPTION ON) +set(BUILD_TESTS_OPTION ON) +set(BUILD_TOOLS_OPTION ON) +set(BUILD_INSTALLER_OPTION ON) +set(GLES_OPTION OFF) +set(DISABLE_QML_OPTION OFF) +set(DOWNLOAD_SERVERLESS_CONTENT_OPTION OFF) + if (ANDROID OR UWP) - option(BUILD_SERVER "Build server components" OFF) - option(BUILD_TOOLS "Build tools" OFF) - option(BUILD_INSTALLER "Build installer" OFF) -else() - option(BUILD_SERVER "Build server components" ON) - option(BUILD_TOOLS "Build tools" ON) - option(BUILD_INSTALLER "Build installer" ON) + set(BUILD_SERVER_OPTION OFF) + set(BUILD_TOOLS_OPTION OFF) + set(BUILD_INSTALLER OFF) +endif() + +if (CLIENT_ONLY) + set(BUILD_SERVER_OPTION OFF) endif() if (SERVER_ONLY) - option(BUILD_CLIENT "Build client components" OFF) - option(BUILD_TESTS "Build tests" OFF) -else() - option(BUILD_CLIENT "Build client components" ON) - option(BUILD_TESTS "Build tests" ON) + set(BUILD_CLIENT_OPTION OFF) + set(BUILD_TESTS_OPTION OFF) endif() if (ANDROID) - option(USE_GLES "Use OpenGL ES" ON) - set(PLATFORM_QT_COMPONENTS AndroidExtras WebView) + set(GLES_OPTION ON) + set(PLATFORM_QT_COMPONENTS AndroidExtras WebView) else () - option(USE_GLES "Use OpenGL ES" OFF) - set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets) + set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets) endif () if (USE_GLES AND (NOT ANDROID)) - option(DISABLE_QML "Disable QML" ON) -else() - option(DISABLE_QML "Disable QML" OFF) + set(DISABLE_QML_OPTION ON) endif() + +option(BUILD_CLIENT "Build client components" ${BUILD_CLIENT_OPTION}) +option(BUILD_SERVER "Build server components" ${BUILD_SERVER_OPTION}) +option(BUILD_TESTS "Build tests" ${BUILD_TESTS_OPTION}) +option(BUILD_TOOLS "Build tools" ${BUILD_TOOLS_OPTION}) +option(BUILD_INSTALLER "Build installer" ${BUILD_INSTALLER_OPTION}) +option(USE_GLES "Use OpenGL ES" ${GLES_OPTION}) +option(DISABLE_QML "Disable QML" ${DISABLE_QML_OPTION}) option(DISABLE_KTX_CACHE "Disable KTX Cache" OFF) - - +option( + DOWNLOAD_SERVERLESS_CONTENT + "Download and setup default serverless content beside Interface" + ${DOWNLOAD_SERVERLESS_CONTENT_OPTION} +) set(PLATFORM_QT_GL OpenGL) if (USE_GLES) add_definitions(-DUSE_GLES) - set(PLATFORM_GL_BACKEND gpu-gles) + set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gles) else() - set(PLATFORM_GL_BACKEND gpu-gl) + set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gl) endif() - foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS}) list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}") endforeach() - -MESSAGE(STATUS "Build server: " ${BUILD_SERVER}) -MESSAGE(STATUS "Build client: " ${BUILD_CLIENT}) -MESSAGE(STATUS "Build tests: " ${BUILD_TESTS}) -MESSAGE(STATUS "Build tools: " ${BUILD_TOOLS}) -MESSAGE(STATUS "Build installer: " ${BUILD_INSTALLER}) -MESSAGE(STATUS "GL ES: " ${USE_GLES}) +MESSAGE(STATUS "Build server: " ${BUILD_SERVER}) +MESSAGE(STATUS "Build client: " ${BUILD_CLIENT}) +MESSAGE(STATUS "Build tests: " ${BUILD_TESTS}) +MESSAGE(STATUS "Build tools: " ${BUILD_TOOLS}) +MESSAGE(STATUS "Build installer: " ${BUILD_INSTALLER}) +MESSAGE(STATUS "GL ES: " ${USE_GLES}) +MESSAGE(STATUS "DL serverless content: " ${DOWNLOAD_SERVERLESS_CONTENT}) if (DISABLE_QML) -MESSAGE(STATUS "QML disabled!") -add_definitions(-DDISABLE_QML) + MESSAGE(STATUS "QML disabled!") + add_definitions(-DDISABLE_QML) endif() if (DISABLE_KTX_CACHE) -MESSAGE(STATUS "KTX cache disabled!") -add_definitions(-DDISABLE_KTX_CACHE) + MESSAGE(STATUS "KTX cache disabled!") + add_definitions(-DDISABLE_KTX_CACHE) endif() if (UNIX AND DEFINED ENV{HIFI_MEMORY_DEBUGGING}) - MESSAGE(STATUS "Memory debugging is enabled") + MESSAGE(STATUS "Memory debugging is enabled") endif() # @@ -132,8 +149,8 @@ set_packaging_parameters() # FIXME hack to work on the proper Android toolchain if (ANDROID) - add_subdirectory(android/app) - return() + add_subdirectory(android/app) + return() endif() # add subdirectories for all targets @@ -148,32 +165,30 @@ if (BUILD_SERVER) endif() if (BUILD_CLIENT) - add_subdirectory(interface) - set_target_properties(interface PROPERTIES FOLDER "Apps") - if (ANDROID) - add_subdirectory(gvr-interface) - set_target_properties(gvr-interface PROPERTIES FOLDER "Apps") - endif() + add_subdirectory(interface) + set_target_properties(interface PROPERTIES FOLDER "Apps") + + option(USE_SIXENSE "Build Interface with sixense library/plugin" OFF) endif() if (BUILD_CLIENT OR BUILD_SERVER) - add_subdirectory(plugins) + add_subdirectory(plugins) endif() # BUILD_TOOLS option will be handled inside the tools's CMakeLists.txt because 'scribe' tool is required for build anyway add_subdirectory(tools) if (BUILD_TESTS) - add_subdirectory(tests) + add_subdirectory(tests) endif() if (BUILD_INSTALLER) - if (UNIX) - install( - DIRECTORY "${CMAKE_SOURCE_DIR}/scripts" - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/interface - COMPONENT ${CLIENT_COMPONENT} - ) - endif() - generate_installers() + if (UNIX) + install( + DIRECTORY "${CMAKE_SOURCE_DIR}/scripts" + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/interface + COMPONENT ${CLIENT_COMPONENT} + ) + endif() + generate_installers() endif() diff --git a/INSTALL.md b/INSTALL.md index 79d7e96977..e07d28a43d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -17,6 +17,8 @@ To produce an executable installer on Windows, the following are required: - [Nullsoft Scriptable Install System](http://nsis.sourceforge.net/Download) - 3.0b3 - [UAC Plug-in for Nullsoft](http://nsis.sourceforge.net/UAC_plug-in) - 0.2.4c - [nsProcess Plug-in for Nullsoft](http://nsis.sourceforge.net/NsProcess_plugin) - 1.6 +- [Inetc Plug-in for Nullsoft](http://nsis.sourceforge.net/Inetc_plug-in) - 1.0 +- [NSISpcre Plug-in for Nullsoft](http://nsis.sourceforge.net/NSISpcre_plug-in) - 1.0 Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System. diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 87827a27d9..1eb43a45a5 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -46,15 +46,68 @@ static const uint8_t CPU_AFFINITY_COUNT_LOW = 1; static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000; #endif -const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; - static const QStringList BAKEABLE_MODEL_EXTENSIONS = { "fbx" }; static QStringList BAKEABLE_TEXTURE_EXTENSIONS; -static const QStringList BAKEABLE_SCRIPT_EXTENSIONS = {}; +static const QStringList BAKEABLE_SCRIPT_EXTENSIONS = { }; + static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; static const QString BAKED_SCRIPT_SIMPLE_NAME = "asset.js"; +static const ModelBakeVersion CURRENT_MODEL_BAKE_VERSION = (ModelBakeVersion)((BakeVersion)ModelBakeVersion::COUNT - 1); +static const TextureBakeVersion CURRENT_TEXTURE_BAKE_VERSION = (TextureBakeVersion)((BakeVersion)TextureBakeVersion::COUNT - 1); +static const ScriptBakeVersion CURRENT_SCRIPT_BAKE_VERSION = (ScriptBakeVersion)((BakeVersion)ScriptBakeVersion::COUNT - 1); + +BakedAssetType assetTypeForExtension(const QString& extension) { + auto extensionLower = extension.toLower(); + if (BAKEABLE_MODEL_EXTENSIONS.contains(extensionLower)) { + return Model; + } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extensionLower.toLocal8Bit())) { + return Texture; + } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extensionLower)) { + return Script; + } + return Undefined; +} + +BakedAssetType assetTypeForFilename(const QString& filename) { + auto dotIndex = filename.lastIndexOf("."); + if (dotIndex == -1) { + return BakedAssetType::Undefined; + } + + auto extension = filename.mid(dotIndex + 1); + return assetTypeForExtension(extension); +} + +QString bakedFilenameForAssetType(BakedAssetType type) { + switch (type) { + case Model: + return BAKED_MODEL_SIMPLE_NAME; + case Texture: + return BAKED_TEXTURE_SIMPLE_NAME; + case Script: + return BAKED_SCRIPT_SIMPLE_NAME; + default: + return ""; + } +} + +BakeVersion currentBakeVersionForAssetType(BakedAssetType type) { + switch (type) { + case Model: + return (BakeVersion)CURRENT_MODEL_BAKE_VERSION; + case Texture: + return (BakeVersion)CURRENT_TEXTURE_BAKE_VERSION; + case Script: + return (BakeVersion)CURRENT_SCRIPT_BAKE_VERSION; + default: + return 0; + } +} + +const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; + void AssetServer::bakeAsset(const AssetUtils::AssetHash& assetHash, const AssetUtils::AssetPath& assetPath, const QString& filePath) { qDebug() << "Starting bake for: " << assetPath << assetHash; auto it = _pendingBakes.find(assetHash); @@ -167,36 +220,38 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU return false; } - auto dotIndex = path.lastIndexOf("."); - if (dotIndex == -1) { + BakedAssetType type = assetTypeForFilename(path); + + if (type == Undefined) { return false; } - auto extension = path.mid(dotIndex + 1); + QString bakedFilename = bakedFilenameForAssetType(type); + auto bakedPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + assetHash + "/" + bakedFilename; + auto mappingIt = _fileMappings.find(bakedPath); + bool bakedMappingExists = mappingIt != _fileMappings.end(); - QString bakedFilename; + // If the path is mapped to the original file's hash, baking has been disabled for this + // asset + if (bakedMappingExists && mappingIt->second == assetHash) { + return false; + } bool loaded; AssetMeta meta; std::tie(loaded, meta) = readMetaFile(assetHash); - // TODO: Allow failed bakes that happened on old versions to be re-baked - if (loaded && meta.failedLastBake) { + if (type == Texture && !loaded) { return false; } - if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) { - bakedFilename = BAKED_MODEL_SIMPLE_NAME; - } else if (loaded && BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit())) { - bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; - } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extension)) { - bakedFilename = BAKED_SCRIPT_SIMPLE_NAME; - } else { + auto currentVersion = currentBakeVersionForAssetType(type); + + if (loaded && (meta.failedLastBake && meta.bakeVersion >= currentVersion)) { return false; } - auto bakedPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + assetHash + "/" + bakedFilename; - return _fileMappings.find(bakedPath) == _fileMappings.end(); + return !bakedMappingExists || (meta.bakeVersion < currentVersion); } bool interfaceRunning() { @@ -598,15 +653,9 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, NLPacketLi // first, figure out from the mapping extension what type of file this is auto assetPathExtension = assetPath.mid(assetPath.lastIndexOf('.') + 1).toLower(); - QString bakedRootFile; - if (BAKEABLE_MODEL_EXTENSIONS.contains(assetPathExtension)) { - bakedRootFile = BAKED_MODEL_SIMPLE_NAME; - } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(assetPathExtension.toLocal8Bit())) { - bakedRootFile = BAKED_TEXTURE_SIMPLE_NAME; - } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(assetPathExtension)) { - bakedRootFile = BAKED_SCRIPT_SIMPLE_NAME; - } + auto type = assetTypeForFilename(assetPath); + QString bakedRootFile = bakedFilenameForAssetType(type); auto originalAssetHash = it->second; QString redirectedAssetHash; @@ -653,9 +702,19 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, NLPacketLi auto query = QUrlQuery(url.query()); bool isSkybox = query.hasQueryItem("skybox"); if (isSkybox) { - writeMetaFile(originalAssetHash); - if (!bakingDisabled) { - maybeBake(assetPath, originalAssetHash); + bool loaded; + AssetMeta meta; + std::tie(loaded, meta) = readMetaFile(originalAssetHash); + + if (!loaded) { + AssetMeta needsBakingMeta; + needsBakingMeta.bakeVersion = NEEDS_BAKING_BAKE_VERSION; + + writeMetaFile(originalAssetHash, needsBakingMeta); + if (!bakingDisabled) { + maybeBake(assetPath, originalAssetHash); + } + } } } @@ -1275,15 +1334,19 @@ QString getBakeMapping(const AssetUtils::AssetHash& hash, const QString& relativ } void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, QString errors) { - qDebug() << "Failed: " << originalAssetHash << assetPath << errors; + qDebug() << "Failed to bake: " << originalAssetHash << assetPath << "(" << errors << ")"; bool loaded; AssetMeta meta; std::tie(loaded, meta) = readMetaFile(originalAssetHash); + auto type = assetTypeForFilename(assetPath); + auto currentTypeVersion = currentBakeVersionForAssetType(type); + meta.failedLastBake = true; meta.lastBakeErrors = errors; + meta.bakeVersion = currentTypeVersion; writeMetaFile(originalAssetHash, meta); @@ -1373,17 +1436,20 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina qWarning() << "Failed to remove temporary directory:" << bakedTempOutputDir; } - if (!errorCompletingBake) { - // create the meta file to store which version of the baking process we just completed - writeMetaFile(originalAssetHash); - } else { + auto type = assetTypeForFilename(originalAssetPath); + auto currentTypeVersion = currentBakeVersionForAssetType(type); + + AssetMeta meta; + meta.bakeVersion = currentTypeVersion; + meta.failedLastBake = errorCompletingBake; + + if (errorCompletingBake) { qWarning() << "Could not complete bake for" << originalAssetHash; - AssetMeta meta; - meta.failedLastBake = true; meta.lastBakeErrors = errorReason; - writeMetaFile(originalAssetHash, meta); } + writeMetaFile(originalAssetHash, meta); + _pendingBakes.remove(originalAssetHash); } @@ -1447,7 +1513,7 @@ bool AssetServer::writeMetaFile(AssetUtils::AssetHash originalAssetHash, const A // construct the JSON that will be in the meta file QJsonObject metaFileObject; - metaFileObject[BAKE_VERSION_KEY] = meta.bakeVersion; + metaFileObject[BAKE_VERSION_KEY] = (int)meta.bakeVersion; metaFileObject[FAILED_LAST_BAKE_KEY] = meta.failedLastBake; metaFileObject[LAST_BAKE_ERRORS_KEY] = meta.lastBakeErrors; @@ -1479,27 +1545,13 @@ bool AssetServer::setBakingEnabled(const AssetUtils::AssetPathList& paths, bool for (const auto& path : paths) { auto it = _fileMappings.find(path); if (it != _fileMappings.end()) { + auto type = assetTypeForFilename(path); + if (type == Undefined) { + continue; + } + QString bakedFilename = bakedFilenameForAssetType(type); + auto hash = it->second; - - auto dotIndex = path.lastIndexOf("."); - if (dotIndex == -1) { - continue; - } - - auto extension = path.mid(dotIndex + 1); - - QString bakedFilename; - - if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) { - bakedFilename = BAKED_MODEL_SIMPLE_NAME; - } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) { - bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; - } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extension)) { - bakedFilename = BAKED_SCRIPT_SIMPLE_NAME; - } else { - continue; - } - auto bakedMapping = getBakeMapping(hash, bakedFilename); auto it = _fileMappings.find(bakedMapping); diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 00ab27c74d..a55a15e6fc 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -23,8 +23,47 @@ #include "RegisteredMetaTypes.h" +using BakeVersion = int; +static const BakeVersion INITIAL_BAKE_VERSION = 0; +static const BakeVersion NEEDS_BAKING_BAKE_VERSION = -1; + +enum BakedAssetType : int { + Model = 0, + Texture, + Script, + + NUM_ASSET_TYPES, + Undefined +}; + +// ATTENTION! If you change the current version for an asset type, you will also +// need to update the function currentBakeVersionForAssetType() inside of AssetServer.cpp. +enum class ModelBakeVersion : BakeVersion { + Initial = INITIAL_BAKE_VERSION, + + COUNT +}; + +// ATTENTION! See above. +enum class TextureBakeVersion : BakeVersion { + Initial = INITIAL_BAKE_VERSION, + + COUNT +}; + +// ATTENTION! See above. +enum class ScriptBakeVersion : BakeVersion { + Initial = INITIAL_BAKE_VERSION, + FixEmptyScripts, + + COUNT +}; + struct AssetMeta { - int bakeVersion { 0 }; + AssetMeta() { + } + + BakeVersion bakeVersion; bool failedLastBake { false }; QString lastBakeErrors; }; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 3ca924b007..929941c05c 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -42,7 +42,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : ThreadedAssignment(message) { // make sure we hear about node kills so we can tell the other nodes - connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled); + connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &AvatarMixer::handleAvatarKilled); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::AvatarData, this, "queueIncomingPacket"); @@ -423,14 +423,15 @@ void AvatarMixer::throttle(std::chrono::microseconds duration, int frame) { } } -void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { - if (killedNode->getType() == NodeType::Agent - && killedNode->getLinkedData()) { + +void AvatarMixer::handleAvatarKilled(SharedNodePointer avatarNode) { + if (avatarNode->getType() == NodeType::Agent + && avatarNode->getLinkedData()) { auto nodeList = DependencyManager::get(); { // decrement sessionDisplayNames table and possibly remove - QMutexLocker nodeDataLocker(&killedNode->getLinkedData()->getMutex()); - AvatarMixerClientData* nodeData = dynamic_cast(killedNode->getLinkedData()); + QMutexLocker nodeDataLocker(&avatarNode->getLinkedData()->getMutex()); + AvatarMixerClientData* nodeData = dynamic_cast(avatarNode->getLinkedData()); const QString& baseDisplayName = nodeData->getBaseDisplayName(); // No sense guarding against very rare case of a node with no entry, as this will work without the guard and do one less lookup in the common case. if (--_sessionDisplayNames[baseDisplayName].second <= 0) { @@ -447,12 +448,12 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { // we relay avatar kill packets to agents that are not upstream // and downstream avatar mixers, if the node that was just killed was being replicated return (node->getType() == NodeType::Agent && !node->isUpstream()) || - (killedNode->isReplicated() && shouldReplicateTo(*killedNode, *node)); + (avatarNode->isReplicated() && shouldReplicateTo(*avatarNode, *node)); }, [&](const SharedNodePointer& node) { if (node->getType() == NodeType::Agent) { if (!killPacket) { killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason)); - killPacket->write(killedNode->getUUID().toRfc4122()); + killPacket->write(avatarNode->getUUID().toRfc4122()); killPacket->writePrimitive(KillAvatarReason::AvatarDisconnected); } @@ -462,7 +463,7 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { if (!replicatedKillPacket) { replicatedKillPacket = NLPacket::create(PacketType::ReplicatedKillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason)); - replicatedKillPacket->write(killedNode->getUUID().toRfc4122()); + replicatedKillPacket->write(avatarNode->getUUID().toRfc4122()); replicatedKillPacket->writePrimitive(KillAvatarReason::AvatarDisconnected); } @@ -479,7 +480,7 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { return false; } - if (node->getUUID() == killedNode->getUUID()) { + if (node->getUUID() == avatarNode->getUUID()) { return false; } @@ -489,7 +490,7 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { QMetaObject::invokeMethod(node->getLinkedData(), "cleanupKilledNode", Qt::AutoConnection, - Q_ARG(const QUuid&, QUuid(killedNode->getUUID()))); + Q_ARG(const QUuid&, QUuid(avatarNode->getUUID()))); } ); } @@ -605,7 +606,9 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes void AvatarMixer::handleKillAvatarPacket(QSharedPointer message, SharedNodePointer node) { auto start = usecTimestampNow(); - DependencyManager::get()->processKillNode(*message); + handleAvatarKilled(node); + + node->setLinkedData(nullptr); auto end = usecTimestampNow(); _handleKillAvatarPacketElapsedTime += (end - start); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index cb5f536faa..1fbfd7338b 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -39,7 +39,7 @@ public slots: /// runs the avatar mixer void run() override; - void nodeKilled(SharedNodePointer killedNode); + void handleAvatarKilled(SharedNodePointer killedNode); void sendStatsPacket() override; diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index e5cee84f1b..94d21f1c9a 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -442,12 +442,16 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream PrioritizedEntity queuedItem = _sendQueue.top(); EntityItemPointer entity = queuedItem.getEntity(); if (entity) { - // Only send entities that match the jsonFilters, but keep track of everything we've tried to send so we don't try to send it again + const QUuid& entityID = entity->getID(); + // Only send entities that match the jsonFilters, but keep track of everything we've tried to send so we don't try to send it again; + // also send if we previously matched since this represents change to a matched item. bool entityMatchesFilters = entity->matchesJSONFilters(jsonFilters); - if (entityMatchesFilters || entityNodeData->isEntityFlaggedAsExtra(entity->getID())) { + bool entityPreviouslyMatchedFilter = entityNodeData->sentFilteredEntity(entityID); + + if (entityMatchesFilters || entityNodeData->isEntityFlaggedAsExtra(entityID) || entityPreviouslyMatchedFilter) { if (!jsonFilters.isEmpty() && entityMatchesFilters) { // Record explicitly filtered-in entity so that extra entities can be flagged. - entityNodeData->insertSentFilteredEntity(entity->getID()); + entityNodeData->insertSentFilteredEntity(entityID); } OctreeElement::AppendState appendEntityState = entity->appendEntityData(&_packetData, params, _extraEncodeData); @@ -458,6 +462,10 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream params.stopReason = EncodeBitstreamParams::DIDNT_FIT; break; } + + if (entityPreviouslyMatchedFilter && !entityMatchesFilters) { + entityNodeData->removeSentFilteredEntity(entityID); + } ++_numEntities; } if (queuedItem.shouldForceRemove()) { diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 60cb1e349b..1255c18e71 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -476,6 +476,7 @@ void EntityScriptServer::clear() { // do this here (instead of in deleter) to avoid marshalling unload signals back to this thread _entitiesScriptEngine->unloadAllEntityScripts(); _entitiesScriptEngine->stop(); + _entitiesScriptEngine->waitTillDoneRunning(); } _entityViewer.clear(); @@ -565,8 +566,15 @@ void EntityScriptServer::handleOctreePacket(QSharedPointer mess void EntityScriptServer::aboutToFinish() { shutdownScriptEngine(); + auto entityScriptingInterface = DependencyManager::get(); // our entity tree is going to go away so tell that to the EntityScriptingInterface - DependencyManager::get()->setEntityTree(nullptr); + entityScriptingInterface->setEntityTree(nullptr); + + // Should always be true as they are singletons. + if (entityScriptingInterface->getPacketSender() == &_entityEditSender) { + // The packet sender is about to go away. + entityScriptingInterface->setPacketSender(nullptr); + } DependencyManager::get()->cleanup(); diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index 0d66b365a2..f2690e0a7d 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_policy(SET CMP0046 OLD) include(ExternalProject) -set(QUAZIP_CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON) +set(QUAZIP_CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON) if (APPLE) else () diff --git a/cmake/externals/serverless-content/CMakeLists.txt b/cmake/externals/serverless-content/CMakeLists.txt new file mode 100644 index 0000000000..1c66fb213f --- /dev/null +++ b/cmake/externals/serverless-content/CMakeLists.txt @@ -0,0 +1,16 @@ +include(ExternalProject) + +set(EXTERNAL_NAME serverless-content) + +ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC66.zip + URL_MD5 91edfde96e06efc847ca327ab97f4c74 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 +) + +# Hide this external target (for IDE users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 702636dd01..742c5b5b94 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -23,7 +23,7 @@ macro(GENERATE_INSTALLERS) set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) if (PR_BUILD) - set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2") + set(CPACK_NSIS_COMPRESSOR "bzip2") endif () set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME}) @@ -46,9 +46,35 @@ macro(GENERATE_INSTALLERS) set(UNINSTALLER_HEADER_IMAGE "") fix_path_for_nsis(${_UNINSTALLER_HEADER_BAD_PATH} UNINSTALLER_HEADER_IMAGE) - # grab the latest VC redist (2017) and add it to the installer, our NSIS template - # will call it during the install - install(CODE "file(DOWNLOAD https://go.microsoft.com/fwlink/?LinkId=746572 \"\${CMAKE_INSTALL_PREFIX}/vcredist_x64.exe\")") + # we use external libraries that still need the 120 (VS2013) redistributables + # so we include them as well until those external libraries are updated + # to use the redistributables that match what we build our applications for + set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS + "C:/Windows/System32/msvcp120.dll" + "C:/Windows/System32/msvcr120.dll" + ) + + set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) + set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR}) + set(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT ${CLIENT_COMPONENT}) + include(InstallRequiredSystemLibraries) + + if (CLIENT_ONLY OR SERVER_ONLY) + set(CPACK_MONOLITHIC_INSTALL 1) + endif () + + # setup conditional checks for server component selection depending on + # the inclusion of the server component at all + if (CLIENT_ONLY) + set(SERVER_COMPONENT_CONDITIONAL "0 == 1") + set(CLIENT_COMPONENT_CONDITIONAL "1 == 1") + elseif (SERVER_ONLY) + set(SERVER_COMPONENT_CONDITIONAL "1 == 1") + set(CLIENT_COMPONENT_CONDITIONAL "0 == 1") + else () + set(SERVER_COMPONENT_CONDITIONAL "\\\${SectionIsSelected} \\\${${SERVER_COMPONENT}}") + set(CLIENT_COMPONENT_CONDITIONAL "\\\${SectionIsSelected} \\\${${CLIENT_COMPONENT}}") + endif () elseif (APPLE) # produce a drag and drop DMG on OS X set(CPACK_GENERATOR "DragNDrop") @@ -79,8 +105,13 @@ macro(GENERATE_INSTALLERS) set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") - cpack_add_component(${CLIENT_COMPONENT} DISPLAY_NAME "High Fidelity Interface") - cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "High Fidelity Sandbox") + if (BUILD_CLIENT) + cpack_add_component(${CLIENT_COMPONENT} DISPLAY_NAME "High Fidelity Interface") + endif () + + if (BUILD_SERVER) + cpack_add_component(${SERVER_COMPONENT} DISPLAY_NAME "High Fidelity Sandbox") + endif () include(CPack) endmacro() diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index d324776572..29f4617a6f 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -39,7 +39,9 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> \"$\"" + COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND}\ + ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release>\ + --no-compiler-runtime --no-opengl-sw --no-angle -no-system-d3d-compiler \"$\"" ) set(QTAUDIO_PATH "$/audio") diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index e26f81edd9..3ca5cb0e54 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -27,6 +27,11 @@ macro(SET_PACKAGING_PARAMETERS) message(STATUS "The BRANCH environment variable is: $ENV{BRANCH}") message(STATUS "The RELEASE_TYPE variable is: ${RELEASE_TYPE}") + # setup component categories for installer + set(DDE_COMPONENT dde) + set(CLIENT_COMPONENT client) + set(SERVER_COMPONENT server) + if (RELEASE_TYPE STREQUAL "PRODUCTION") set(DEPLOY_PACKAGE TRUE) set(PRODUCTION_BUILD 1) @@ -68,6 +73,11 @@ macro(SET_PACKAGING_PARAMETERS) add_definitions(-DDEV_BUILD) endif () + if (DEPLOY_PACKAGE) + # for deployed packages always grab the serverless content + set(DOWNLOAD_SERVERLESS_CONTENT ON) + endif () + if (APPLE) set(DMG_SUBFOLDER_NAME "${BUILD_ORGANIZATION}") @@ -149,13 +159,10 @@ macro(SET_PACKAGING_PARAMETERS) set(CLIENT_LAUNCH_NOW_REG_KEY "ClientLaunchAfterInstall") set(SERVER_LAUNCH_NOW_REG_KEY "ServerLaunchAfterInstall") set(CUSTOM_INSTALL_REG_KEY "CustomInstall") + set(CLIENT_ID_REG_KEY "ClientGUID") + set(GA_TRACKING_ID $ENV{GA_TRACKING_ID}) endif () - # setup component categories for installer - set(DDE_COMPONENT dde) - set(CLIENT_COMPONENT client) - set(SERVER_COMPONENT server) - # print out some results for testing this new build feature message(STATUS "The BUILD_GLOBAL_SERVICES variable is: ${BUILD_GLOBAL_SERVICES}") message(STATUS "The USE_STABLE_GLOBAL_SERVICES variable is: ${USE_STABLE_GLOBAL_SERVICES}") diff --git a/cmake/templates/CPackProperties.cmake.in b/cmake/templates/CPackProperties.cmake.in index b91d78f628..9c303f7532 100644 --- a/cmake/templates/CPackProperties.cmake.in +++ b/cmake/templates/CPackProperties.cmake.in @@ -41,6 +41,10 @@ set(CONSOLE_STARTUP_REG_KEY "@CONSOLE_STARTUP_REG_KEY@") set(SERVER_LAUNCH_NOW_REG_KEY "@SERVER_LAUNCH_NOW_REG_KEY@") set(CLIENT_LAUNCH_NOW_REG_KEY "@CLIENT_LAUNCH_NOW_REG_KEY@") set(CUSTOM_INSTALL_REG_KEY "@CUSTOM_INSTALL_REG_KEY@") +set(GA_TRACKING_ID "@GA_TRACKING_ID@") +set(CLIENT_ID_REG_KEY "@CLIENT_ID_REG_KEY@") set(INSTALLER_HEADER_IMAGE "@INSTALLER_HEADER_IMAGE@") set(UNINSTALLER_HEADER_IMAGE "@UNINSTALLER_HEADER_IMAGE@") set(ADD_REMOVE_ICON_PATH "@ADD_REMOVE_ICON_PATH@") +set(SERVER_COMPONENT_CONDITIONAL "@SERVER_COMPONENT_CONDITIONAL@") +set(CLIENT_COMPONENT_CONDITIONAL "@CLIENT_COMPONENT_CONDITIONAL@") diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index c1bfebe2c4..7faa67d1b0 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -319,6 +319,78 @@ Function DownloadFile FunctionEnd !endif + +!include NSISpcre.nsh +!insertmacro REMatches + +Var CampaignName + +!macro GetCampaignName RetVar + Call GetCampaignName + Pop ${RetVar} +!macroend + +Function GetCampaignName + Push $0 ; Stash $0 + + ; Parse filename out of the path + ${RECaptureMatches} $0 "([^\\]*\\)*(.*)\.exe" $EXEPATH 0 + ${If} $0 == 2 + Pop $0 ; Discard Path + Pop $0 ; Recover filename + ; Parse campaign out of the filename + ${RECaptureMatches} $0 "HighFidelity-([^-]*-)Beta-.*" $0 0 + ${If} $0 == 1 + Pop $0 ; Recover campaign name + StrCpy $0 $0 -1 0 ; Remove trailing - and copy to _RetVar + ${Else} + StrCpy $0 "" + ${EndIf} + ${Else} + StrCpy $0 "" + ${EndIf} + + Exch $0 ; Restore $0 and push result +FunctionEnd + +!macro CreateGUID RetVar + System::Call 'ole32::CoCreateGuid(g .s)' + Pop ${RetVar} + ; Strip opening and closing braces + StrCpy ${RetVar} ${RetVar} -1 1 +!macroend + +Var GAClientID + +!macro InitGAClientID + ; Generate a new GUID on every run for now + !insertmacro CreateGUID $GAClientID +!macroend + +!macro GoogleAnalytics Category Action Label Value + ${If} "@GA_TRACKING_ID@" != "" + Push $0 + Push $1 + + StrCpy $0 "https://google-analytics.com/collect?v=1&tid=@GA_TRACKING_ID@" + StrCpy $0 "$0&cid=$GAClientID&t=event&ec=${Category}&ea=${Action}" + + ${If} "${Label}" != "" + StrCpy $0 "$0&el=${Label}" + ${EndIf} + ${If} "${Value}" != "" + StrCpy $0 "$0&ev=${Value}" + ${EndIf} + + GetTempFileName $1 + inetc::get /SILENT $0 $1 /END + Delete $1 + + Pop $1 + Pop $0 + ${EndIf} +!macroend + ;-------------------------------- ; Installation types @@ -342,28 +414,38 @@ SectionEnd ;-------------------------------- ;Pages + !define MUI_CUSTOMFUNCTION_ABORT OnUserAbort + + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageWelcomePre !insertmacro MUI_PAGE_WELCOME + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageLicensePre !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" Page custom InstallTypesPage ReadInstallTypes - !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageDirectoryPre !insertmacro MUI_PAGE_DIRECTORY ;Start Menu Folder Page Configuration !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" - - !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageStartMenuPre !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER - !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageComponentsPre @CPACK_NSIS_PAGE_COMPONENTS@ + ; the MUI_PAGE_CUSTOMFUNCTION_PRE shouldn't be defined here + ; which can happen for a component-less (like client only) install + !ifdef MUI_PAGE_CUSTOMFUNCTION_PRE + !undef MUI_PAGE_CUSTOMFUNCTION_PRE + !endif + Page custom PostInstallOptionsPage ReadPostInstallOptions + !define MUI_PAGE_CUSTOMFUNCTION_PRE PageInstallFilesPre !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_UNPAGE_CONFIRM @@ -452,8 +534,40 @@ Var CopyFromProductionCheckbox Var ExpressInstallRadioButton Var CustomInstallRadioButton Var InstallTypeDialog -Var Express Var CustomInstallTemporaryState +Var Express + +!macro MaybeSkipPage + ; Check if Express is set, if so, abort the post install options page + ${If} $Express == "1" + Abort + ${EndIf} +!macroend + +Function OnUserAbort + !insertmacro GoogleAnalytics "Installer" "Abort" "User Abort" "" +FunctionEnd +Function PageWelcomePre + !insertmacro GoogleAnalytics "Installer" "Welcome" "" "" +FunctionEnd +Function PageLicensePre + !insertmacro GoogleAnalytics "Installer" "License" "" "" +FunctionEnd +Function PageDirectoryPre + !insertmacro MaybeSkipPage + !insertmacro GoogleAnalytics "Installer" "Directory" "" "" +FunctionEnd +Function PageStartMenuPre + !insertmacro MaybeSkipPage + !insertmacro GoogleAnalytics "Installer" "StartMenu" "" "" +FunctionEnd +Function PageComponentsPre + !insertmacro MaybeSkipPage + !insertmacro GoogleAnalytics "Installer" "Components" "" "" +FunctionEnd +Function PageInstallFilesPre + !insertmacro GoogleAnalytics "Installer" "Install" "" "" +FunctionEnd !macro SetInstallOption Checkbox OptionName Default ; reads the value for the given install option to the registry @@ -472,6 +586,8 @@ Var CustomInstallTemporaryState !macroend Function InstallTypesPage + !insertmacro GoogleAnalytics "Installer" "Install Types" "" "" + !insertmacro MUI_HEADER_TEXT "Choose Installation Type" "Express or Custom Install" nsDialogs::Create 1018 @@ -502,10 +618,11 @@ Function InstallTypesPage ${If} $CustomInstallTemporaryState == ${BST_UNCHECKED} ${NSD_Check} $ExpressInstallRadioButton + Call ChangeExpressLabel + ${Else} + Call ChangeCustomLabel ${EndIf} - Call ChangeExpressLabel - nsDialogs::Show FunctionEnd @@ -523,14 +640,10 @@ Function ChangeCustomLabel Pop $R1 FunctionEnd -Function AbortFunction - ; Check if Express is set, if so, abort the post install options page - StrCmp $Express "1" 0 end - Abort - end: -FunctionEnd - Function PostInstallOptionsPage + !insertmacro MaybeSkipPage + !insertmacro GoogleAnalytics "Installer" "Post Install Options" "" "" + !insertmacro MUI_HEADER_TEXT "Setup Options" "" nsDialogs::Create 1018 @@ -540,15 +653,10 @@ Function PostInstallOptionsPage Abort ${EndIf} - ; Check if Express is set, if so, abort the post install options page - StrCmp $Express "1" 0 end - Abort - end: - StrCpy $CurrentOffset 0 StrCpy $OffsetUnits u - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @INTERFACE_HF_SHORTCUT_NAME@" Pop $DesktopClientCheckbox IntOp $CurrentOffset $CurrentOffset + 15 @@ -557,7 +665,7 @@ Function PostInstallOptionsPage !insertmacro SetInstallOption $DesktopClientCheckbox @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ ${BST_CHECKED} ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @CONSOLE_HF_SHORTCUT_NAME@" Pop $DesktopServerCheckbox IntOp $CurrentOffset $CurrentOffset + 15 @@ -566,7 +674,7 @@ Function PostInstallOptionsPage !insertmacro SetInstallOption $DesktopServerCheckbox @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ ${BST_UNCHECKED} ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ after install" Pop $LaunchServerNowCheckbox @@ -580,7 +688,7 @@ Function PostInstallOptionsPage IntOp $CurrentOffset $CurrentOffset + 15 ${EndIf} - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @INTERFACE_HF_SHORTCUT_NAME@ after install" Pop $LaunchClientNowCheckbox IntOp $CurrentOffset $CurrentOffset + 30 @@ -593,7 +701,7 @@ Function PostInstallOptionsPage ${EndIf} ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ on startup" Pop $ServerStartupCheckbox IntOp $CurrentOffset $CurrentOffset + 15 @@ -602,7 +710,7 @@ Function PostInstallOptionsPage !insertmacro SetInstallOption $ServerStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED} ${EndIf} - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Perform a clean install (Delete older settings and content)" Pop $CleanInstallCheckbox IntOp $CurrentOffset $CurrentOffset + 15 @@ -610,11 +718,11 @@ Function PostInstallOptionsPage ${If} @PR_BUILD@ == 1 ; a PR build defaults all install options expect LaunchServerNowCheckbox, LaunchClientNowCheckbox and the settings copy to unchecked - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ${NSD_SetState} $DesktopClientCheckbox ${BST_UNCHECKED} ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ${NSD_SetState} $DesktopServerCheckbox ${BST_UNCHECKED} ${NSD_SetState} $ServerStartupCheckbox ${BST_UNCHECKED} ${EndIf} @@ -673,12 +781,12 @@ Function ReadInstallTypes FunctionEnd Function ReadPostInstallOptions - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ; check if the user asked for a desktop shortcut to High Fidelity ${NSD_GetState} $DesktopClientCheckbox $DesktopClientState ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ; check if the user asked for a desktop shortcut to Sandbox ${NSD_GetState} $DesktopServerCheckbox $DesktopServerState @@ -691,24 +799,24 @@ Function ReadPostInstallOptions ${NSD_GetState} $CopyFromProductionCheckbox $CopyFromProductionState ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ; check if we need to launch the server post-install ${NSD_GetState} $LaunchServerNowCheckbox $LaunchServerNowState ${EndIf} - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ; check if we need to launch the client post-install ${NSD_GetState} $LaunchClientNowCheckbox $LaunchClientNowState ${EndIf} - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ; check if the user asked for a clean install ${NSD_GetState} $CleanInstallCheckbox $CleanInstallState ${EndIf} FunctionEnd Function HandlePostInstallOptions - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ; check if the user asked for a desktop shortcut to High Fidelity ${If} $DesktopClientState == ${BST_CHECKED} CreateShortCut "$DESKTOP\@INTERFACE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" @@ -719,7 +827,7 @@ Function HandlePostInstallOptions ${EndIf} - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ ; check if the user asked for a desktop shortcut to Sandbox ${If} $DesktopServerState == ${BST_CHECKED} CreateShortCut "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@" @@ -748,7 +856,7 @@ Function HandlePostInstallOptions ${EndIf} ${EndIf} - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ ; check if the user asked for a clean install ${If} $CleanInstallState == ${BST_CHECKED} SetShellVarContext current @@ -785,7 +893,8 @@ Function HandlePostInstallOptions ${EndIf} ${EndIf} - ${If} $LaunchServerNowState == ${BST_CHECKED} + ${If} @SERVER_COMPONENT_CONDITIONAL@ + ${AndIf} $LaunchServerNowState == ${BST_CHECKED} !insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ YES ; both launches use the explorer trick in case the user has elevated permissions for the installer @@ -799,7 +908,7 @@ Function HandlePostInstallOptions Exec '"$WINDIR\explorer.exe" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"' ${EndIf} - ${Else} + ${ElseIf} @CLIENT_COMPONENT_CONDITIONAL@ !insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ NO ; launch uses the explorer trick in case the user has elevated permissions for the installer @@ -837,9 +946,6 @@ Section "-Core installation" Delete "$INSTDIR\ui_resources_200_percent.pak" Delete "$INSTDIR\vccorlib120.dll" Delete "$INSTDIR\version" - Delete "$INSTDIR\msvcr140.dll" - Delete "$INSTDIR\msvcp140.dll" - Delete "$INSTDIR\vcruntime140.dll" Delete "$INSTDIR\xinput1_3.dll" ; Delete old desktop shortcuts before they were renamed during Sandbox rename @@ -858,11 +964,8 @@ Section "-Core installation" ; Rename the incorrectly cased Raleway font Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml" - ExecWait "$INSTDIR\vcredist_x64.exe /install /q /norestart" - ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" - Delete "$INSTDIR\vcredist_x64.exe" ;Use the entire tree produced by the INSTALL target. Keep the ;list of directories here in sync with the RMDir commands below. @@ -931,7 +1034,7 @@ Section "-Core installation" @CPACK_NSIS_CREATE_ICONS_EXTRA@ ; Conditional handling for Interface specific options - ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} @CLIENT_COMPONENT_CONDITIONAL@ CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@INTERFACE_SHORTCUT_NAME@.lnk" \ "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" @@ -946,7 +1049,7 @@ Section "-Core installation" ${EndIf} ; Conditional handling for server console shortcut - ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${If} @SERVER_COMPONENT_CONDITIONAL@ CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk" \ "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@" ${EndIf} @@ -965,6 +1068,7 @@ Section "-Core installation" ; Handle whichever post install options were set Call HandlePostInstallOptions + !insertmacro GoogleAnalytics "Installer" "Done" "" "" SectionEnd !include nsProcess.nsh @@ -979,7 +1083,7 @@ SectionEnd ${If} $R0 == 0 ; the process is running, ask the user to close it - + ${If} "${displayName}" == "@CONSOLE_DISPLAY_NAME@" MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION \ "${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it in the system tray and click Retry to continue." \ @@ -992,6 +1096,8 @@ SectionEnd /SD IDCANCEL IDRETRY Prompt_${UniqueID} IDCANCEL 0 ${EndIf} + !insertmacro GoogleAnalytics "Installer" "Abort" "${displayName} Running" "" + ; If the user decided to cancel, stop the current installer/uninstaller Abort @@ -1087,8 +1193,8 @@ Function .onSelChange !insertmacro SectionList MaybeSelectionChanged ; if neither component is selected, disable the install button - ${IfNot} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} - ${AndIfNot} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} + ${IfNot} @CLIENT_COMPONENT_CONDITIONAL@ + ${AndIfNot} @SERVER_COMPONENT_CONDITIONAL@ GetDlgItem $0 $HWNDPARENT 1 EnableWindow $0 0 ${Else} @@ -1219,6 +1325,11 @@ Function .onInit Quit !endif + !insertmacro InitGAClientID + !insertmacro GetCampaignName $CampaignName + + !insertmacro GoogleAnalytics "Installer" "Start" "$CampaignName" "" + ; make sure none of the installed applications are still running !insertmacro CheckForRunningApplications "installed" "Installer" ${nsProcess::Unload} diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js index 3fec5a9000..853e87ae23 100644 --- a/domain-server/resources/web/content/js/content.js +++ b/domain-server/resources/web/content/js/content.js @@ -132,6 +132,41 @@ $(document).ready(function(){ var ACTIVE_BACKUP_ROW_CLASS = 'active-backup'; var CORRUPTED_ROW_CLASS = 'danger'; + $('body').on('click', '.' + BACKUP_DOWNLOAD_LINK_CLASS, function(ev) { + ev.preventDefault(); + var backupID = $(this).data('backup-id'); + + showSpinnerAlert("Preparing backup..."); + function checkBackupStatus() { + $.ajax({ + url: "/api/backups/" + backupID, + dataType: 'json', + success: function(data) { + if (data.complete) { + if (data.error == '') { + location.href = "/api/backups/download/" + backupID; + swal.close(); + } else { + showErrorMessage( + "Error", + "There was an error preparing your backup. Please refresh the page and try again." + ); + } + } else { + setTimeout(checkBackupStatus, 500); + } + }, + error: function() { + showErrorMessage( + "Error", + "There was an error preparing your backup." + ); + }, + }); + } + checkBackupStatus(); + }); + function reloadBackupInformation() { // make a GET request to get backup information to populate the table $.ajax({ @@ -164,7 +199,7 @@ $(document).ready(function(){ + ""; } diff --git a/domain-server/resources/web/js/shared.js b/domain-server/resources/web/js/shared.js index 84bba4de56..1647da045f 100644 --- a/domain-server/resources/web/js/shared.js +++ b/domain-server/resources/web/js/shared.js @@ -2,7 +2,7 @@ if (typeof Settings === "undefined") { Settings = {}; } -Object.assign(Settings, { +$.extend(Settings, { DEPRECATED_CLASS: 'deprecated-setting', TRIGGER_CHANGE_CLASS: 'trigger-change', DATA_ROW_CLASS: 'value-row', diff --git a/domain-server/src/DomainContentBackupManager.cpp b/domain-server/src/DomainContentBackupManager.cpp index 85040d8c35..518ed73f9e 100644 --- a/domain-server/src/DomainContentBackupManager.cpp +++ b/domain-server/src/DomainContentBackupManager.cpp @@ -55,15 +55,20 @@ DomainContentBackupManager::DomainContentBackupManager(const QString& backupDire const QVariantList& backupRules, std::chrono::milliseconds persistInterval, bool debugTimestampNow) : + _consolidatedBackupDirectory(PathUtils::generateTemporaryDir()), _backupDirectory(backupDirectory), _persistInterval(persistInterval), _lastCheck(p_high_resolution_clock::now()) { - setObjectName("DomainContentBackupManager"); // Make sure the backup directory exists. QDir(_backupDirectory).mkpath("."); parseBackupRules(backupRules); + + constexpr int CONSOLIDATED_BACKUP_CLEANER_INTERVAL_MSECS = 30 * 1000; + _consolidatedBackupCleanupTimer.setInterval(CONSOLIDATED_BACKUP_CLEANER_INTERVAL_MSECS); + connect(&_consolidatedBackupCleanupTimer, &QTimer::timeout, this, &DomainContentBackupManager::removeOldConsolidatedBackups); + _consolidatedBackupCleanupTimer.start(); } void DomainContentBackupManager::parseBackupRules(const QVariantList& backupRules) { @@ -498,23 +503,87 @@ void DomainContentBackupManager::backup() { } } -void DomainContentBackupManager::consolidateBackup(MiniPromise::Promise promise, QString fileName) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "consolidateBackup", Q_ARG(MiniPromise::Promise, promise), - Q_ARG(QString, fileName)); - return; +void DomainContentBackupManager::removeOldConsolidatedBackups() { + constexpr std::chrono::minutes MAX_TIME_TO_KEEP_CONSOLIDATED_BACKUP { 30 }; + auto now = std::chrono::system_clock::now(); + auto it = _consolidatedBackups.begin(); + while (it != _consolidatedBackups.end()) { + auto& backup = it->second; + auto diff = now - backup.createdAt; + if (diff > MAX_TIME_TO_KEEP_CONSOLIDATED_BACKUP) { + QFile oldBackup(backup.absoluteFilePath); + if (!oldBackup.exists() || oldBackup.remove()) { + qDebug() << "Removed old consolidated backup: " << backup.absoluteFilePath; + it = _consolidatedBackups.erase(it); + } else { + qDebug() << "Failed to remove old consolidated backup: " << backup.absoluteFilePath; + it++; + } + } else { + it++; + } + } +} + +ConsolidatedBackupInfo DomainContentBackupManager::consolidateBackup(QString fileName) { + { + std::lock_guard lock { _consolidatedBackupsMutex }; + auto it = _consolidatedBackups.find(fileName); + + if (it != _consolidatedBackups.end()) { + return it->second; + } + } + QMetaObject::invokeMethod(this, "consolidateBackupInternal", Q_ARG(QString, fileName)); + return { + ConsolidatedBackupInfo::CONSOLIDATING, + "", + "", + std::chrono::system_clock::now() + }; +} + +void DomainContentBackupManager::consolidateBackupInternal(QString fileName) { + auto markFailure = [this, &fileName](QString error) { + qWarning() << "Failed to consolidate backup:" << fileName << error; + { + std::lock_guard lock { _consolidatedBackupsMutex }; + auto& consolidatedBackup = _consolidatedBackups[fileName]; + consolidatedBackup.state = ConsolidatedBackupInfo::COMPLETE_WITH_ERROR; + consolidatedBackup.error = error; + } + }; + + { + std::lock_guard lock { _consolidatedBackupsMutex }; + + auto it = _consolidatedBackups.find(fileName); + if (it != _consolidatedBackups.end()) { + return; + } + + _consolidatedBackups[fileName] = { + ConsolidatedBackupInfo::CONSOLIDATING, + "", + "", + std::chrono::system_clock::now() + }; } QDir backupDir { _backupDirectory }; if (!backupDir.exists()) { - qCritical() << "Backup directory does not exist, bailing consolidation of backup"; - promise->resolve({ { "success", false } }); + markFailure("Backup directory does not exist, bailing consolidation of backup"); return; } auto filePath = backupDir.absoluteFilePath(fileName); + + if (!QFile::exists(filePath)) { + markFailure("Backup does not exist"); + return; + } - auto copyFilePath = QDir::tempPath() + "/" + fileName; + auto copyFilePath = _consolidatedBackupDirectory + "/" + fileName; { QFile copyFile(copyFilePath); @@ -523,8 +592,7 @@ void DomainContentBackupManager::consolidateBackup(MiniPromise::Promise promise, } auto copySuccess = QFile::copy(filePath, copyFilePath); if (!copySuccess) { - qCritical() << "Failed to create copy of backup."; - promise->resolve({ { "success", false } }); + markFailure("Failed to create copy of backup."); return; } @@ -532,7 +600,7 @@ void DomainContentBackupManager::consolidateBackup(MiniPromise::Promise promise, if (!zip.open(QuaZip::mdAdd)) { qCritical() << "Could not open backup archive:" << filePath; qCritical() << " ERROR:" << zip.getZipError(); - promise->resolve({ { "success", false } }); + markFailure("Could not open backup archive"); return; } @@ -544,14 +612,17 @@ void DomainContentBackupManager::consolidateBackup(MiniPromise::Promise promise, if (zip.getZipError() != UNZ_OK) { qCritical() << "Failed to consolidate backup: " << zip.getZipError(); - promise->resolve({ { "success", false } }); + markFailure("Failed to consolidate backup"); return; } - promise->resolve({ - { "success", true }, - { "backupFilePath", copyFilePath } - }); + { + std::lock_guard lock { _consolidatedBackupsMutex }; + auto& consolidatedBackup = _consolidatedBackups[fileName]; + consolidatedBackup.state = ConsolidatedBackupInfo::COMPLETE_WITH_SUCCESS; + consolidatedBackup.absoluteFilePath = copyFilePath; + } + } void DomainContentBackupManager::createManualBackup(MiniPromise::Promise promise, const QString& name) { diff --git a/domain-server/src/DomainContentBackupManager.h b/domain-server/src/DomainContentBackupManager.h index fbc8084ac6..2b07afe0b3 100644 --- a/domain-server/src/DomainContentBackupManager.h +++ b/domain-server/src/DomainContentBackupManager.h @@ -15,9 +15,15 @@ #ifndef hifi_DomainContentBackupManager_h #define hifi_DomainContentBackupManager_h +#include + #include #include #include +#include + +#include +#include #include @@ -38,6 +44,18 @@ struct BackupItemInfo { bool isManualBackup; }; +struct ConsolidatedBackupInfo { + enum State { + CONSOLIDATING, + COMPLETE_WITH_ERROR, + COMPLETE_WITH_SUCCESS + }; + State state; + QString error; + QString absoluteFilePath; + std::chrono::system_clock::time_point createdAt; +}; + class DomainContentBackupManager : public GenericThread { Q_OBJECT public: @@ -61,6 +79,7 @@ public: void addBackupHandler(BackupHandlerPointer handler); void aboutToFinish(); /// call this to inform the persist thread that the owner is about to finish to support final persist void replaceData(QByteArray data); + ConsolidatedBackupInfo consolidateBackup(QString fileName); public slots: void getAllBackupsAndStatus(MiniPromise::Promise promise); @@ -68,7 +87,6 @@ public slots: void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName); void recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup); void deleteBackup(MiniPromise::Promise promise, const QString& backupName); - void consolidateBackup(MiniPromise::Promise promise, QString fileName); signals: void loadCompleted(); @@ -91,11 +109,21 @@ protected: bool recoverFromBackupZip(const QString& backupName, QuaZip& backupZip); +private slots: + void removeOldConsolidatedBackups(); + void consolidateBackupInternal(QString fileName); + private: + QTimer _consolidatedBackupCleanupTimer; + + const QString _consolidatedBackupDirectory; const QString _backupDirectory; std::vector _backupHandlers; std::chrono::milliseconds _persistInterval { 0 }; + std::mutex _consolidatedBackupsMutex; + std::unordered_map _consolidatedBackups; + std::atomic _isRecovering { false }; QString _recoveryFilename { }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index ce672be604..197ac7eac2 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -163,6 +164,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : _iceServerAddr(ICE_SERVER_DEFAULT_HOSTNAME), _iceServerPort(ICE_SERVER_DEFAULT_PORT) { + PathUtils::removeTemporaryApplicationDirs(); + parseCommandLine(); DependencyManager::set(); @@ -727,7 +730,7 @@ void DomainServer::setupNodeListAndAssignments() { packetReceiver.registerListener(PacketType::OctreeDataPersist, this, "processOctreeDataPersistMessage"); packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacementRequest"); - packetReceiver.registerListener(PacketType::OctreeFileReplacementFromUrl, this, "handleOctreeFileReplacementFromURLRequest"); + packetReceiver.registerListener(PacketType::DomainContentReplacementFromUrl, this, "handleDomainContentReplacementFromURLRequest"); // set a custom packetVersionMatch as the verify packet operator for the udt::Socket nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified); @@ -736,7 +739,6 @@ void DomainServer::setupNodeListAndAssignments() { auto assetClient = DependencyManager::set(); assetClient->moveToThread(&_assetClientThread); _assetClientThread.start(); - // add whatever static assignments that have been parsed to the queue addStaticAssignmentsToQueue(); } @@ -1933,6 +1935,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url const QString URI_API_DOMAINS_ID = "/api/domains/"; const QString URI_API_BACKUPS = "/api/backups"; const QString URI_API_BACKUPS_ID = "/api/backups/"; + const QString URI_API_BACKUPS_DOWNLOAD_ID = "/api/backups/download/"; const QString URI_API_BACKUPS_RECOVER = "/api/backups/recover/"; const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; @@ -2133,30 +2136,40 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url }); _contentManager->getAllBackupsAndStatus(deferred); return true; + } else if (url.path().startsWith(URI_API_BACKUPS_DOWNLOAD_ID)) { + auto id = url.path().mid(QString(URI_API_BACKUPS_DOWNLOAD_ID).length()); + auto info = _contentManager->consolidateBackup(id); + + if (info.state == ConsolidatedBackupInfo::COMPLETE_WITH_SUCCESS) { + auto file { std::unique_ptr(new QFile(info.absoluteFilePath)) }; + if (file->open(QIODevice::ReadOnly)) { + constexpr const char* CONTENT_TYPE_ZIP = "application/zip"; + auto downloadedFilename = id; + downloadedFilename.replace(QRegularExpression(".zip$"), ".content.zip"); + auto contentDisposition = "attachment; filename=\"" + downloadedFilename + "\""; + connectionPtr->respond(HTTPConnection::StatusCode200, std::move(file), CONTENT_TYPE_ZIP, { + { "Content-Disposition", contentDisposition.toUtf8() } + }); + } else { + qCritical(domain_server) << "Unable to load consolidated backup at:" << info.absoluteFilePath; + connectionPtr->respond(HTTPConnection::StatusCode500, "Error opening backup"); + } + } else if (info.state == ConsolidatedBackupInfo::COMPLETE_WITH_ERROR) { + connectionPtr->respond(HTTPConnection::StatusCode500, ("Error creating backup: " + info.error).toUtf8()); + } else { + connectionPtr->respond(HTTPConnection::StatusCode400, "Backup unavailable"); + } + return true; } else if (url.path().startsWith(URI_API_BACKUPS_ID)) { auto id = url.path().mid(QString(URI_API_BACKUPS_ID).length()); - auto deferred = makePromise("consolidateBackup"); - deferred->then([connectionPtr, JSON_MIME_TYPE](QString error, QVariantMap result) { - if (!connectionPtr) { - return; - } + auto info = _contentManager->consolidateBackup(id); - QJsonObject rootJSON; - auto success = result["success"].toBool(); - if (success) { - auto path = result["backupFilePath"].toString(); - auto file { std::unique_ptr(new QFile(path)) }; - if (file->open(QIODevice::ReadOnly)) { - connectionPtr->respond(HTTPConnection::StatusCode200, std::move(file)); - } else { - qCritical(domain_server) << "Unable to load consolidated backup at:" << path << result; - connectionPtr->respond(HTTPConnection::StatusCode500, "Error opening backup"); - } - } else { - connectionPtr->respond(HTTPConnection::StatusCode400); - } - }); - _contentManager->consolidateBackup(deferred, id); + QJsonObject rootJSON { + { "complete", info.state == ConsolidatedBackupInfo::COMPLETE_WITH_SUCCESS }, + { "error", info.error } + }; + QJsonDocument docJSON { rootJSON }; + connectionPtr->respond(HTTPConnection::StatusCode200, docJSON.toJson(), JSON_MIME_TYPE.toUtf8()); return true; } else if (url.path() == URI_RESTART) { @@ -2209,7 +2222,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url const QString ASSIGNMENT_INSTANCES_HEADER = "ASSIGNMENT-INSTANCES"; const QString ASSIGNMENT_POOL_HEADER = "ASSIGNMENT-POOL"; - QByteArray assignmentInstancesValue = connection->requestHeaders().value(ASSIGNMENT_INSTANCES_HEADER.toLocal8Bit()); + QByteArray assignmentInstancesValue = connection->requestHeader(ASSIGNMENT_INSTANCES_HEADER.toLocal8Bit()); int numInstances = 1; @@ -2221,7 +2234,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url } QString assignmentPool = emptyPool; - QByteArray assignmentPoolValue = connection->requestHeaders().value(ASSIGNMENT_POOL_HEADER.toLocal8Bit()); + QByteArray assignmentPoolValue = connection->requestHeader(ASSIGNMENT_POOL_HEADER.toLocal8Bit()); if (!assignmentPoolValue.isEmpty()) { // specific pool requested, set that on the created assignment @@ -2619,7 +2632,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl if (!_oauthProviderURL.isEmpty() && (adminUsersVariant.isValid() || adminRolesVariant.isValid())) { - QString cookieString = connection->requestHeaders().value(HTTP_COOKIE_HEADER_KEY); + QString cookieString = connection->requestHeader(HTTP_COOKIE_HEADER_KEY); const QString COOKIE_UUID_REGEX_STRING = HIFI_SESSION_COOKIE_KEY + "=([\\d\\w-]+)($|;)"; QRegExp cookieUUIDRegex(COOKIE_UUID_REGEX_STRING); @@ -2664,7 +2677,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl static const QByteArray REQUESTED_WITH_HEADER = "X-Requested-With"; static const QString XML_REQUESTED_WITH = "XMLHttpRequest"; - if (connection->requestHeaders().value(REQUESTED_WITH_HEADER) == XML_REQUESTED_WITH) { + if (connection->requestHeader(REQUESTED_WITH_HEADER) == XML_REQUESTED_WITH) { // unauthorized XHR requests get a 401 and not a 302, since there isn't an XHR // path to OAuth authorize connection->respond(HTTPConnection::StatusCode401, UNAUTHENTICATED_BODY); @@ -2695,7 +2708,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl const QByteArray BASIC_AUTH_HEADER_KEY = "Authorization"; // check if a username and password have been provided with the request - QString basicAuthString = connection->requestHeaders().value(BASIC_AUTH_HEADER_KEY); + QString basicAuthString = connection->requestHeader(BASIC_AUTH_HEADER_KEY); if (!basicAuthString.isEmpty()) { QStringList splitAuthString = basicAuthString.split(' '); @@ -3429,13 +3442,10 @@ void DomainServer::handleOctreeFileReplacement(QByteArray octreeFile) { } } -void DomainServer::handleOctreeFileReplacementFromURLRequest(QSharedPointer message) { +void DomainServer::handleDomainContentReplacementFromURLRequest(QSharedPointer message) { qInfo() << "Received request to replace content from a url"; auto node = DependencyManager::get()->findNodeWithAddr(message->getSenderSockAddr()); - if (node) { - qDebug() << "Found node: " << node->getCanReplaceContent(); - } - if (node->getCanReplaceContent()) { + if (node && node->getCanReplaceContent()) { // Convert message data into our URL QString url(message->getMessage()); QUrl modelsURL = QUrl(url, QUrl::StrictMode); @@ -3448,7 +3458,12 @@ void DomainServer::handleOctreeFileReplacementFromURLRequest(QSharedPointererror(); if (networkError == QNetworkReply::NoError) { - handleOctreeFileReplacement(reply->readAll()); + if (modelsURL.fileName().endsWith(".json.gz")) { + handleOctreeFileReplacement(reply->readAll()); + } else if (modelsURL.fileName().endsWith(".zip")) { + auto deferred = makePromise("recoverFromUploadedBackup"); + _contentManager->recoverFromUploadedBackup(deferred, reply->readAll()); + } } else { qDebug() << "Error downloading JSON from specified file: " << modelsURL; } @@ -3456,12 +3471,9 @@ void DomainServer::handleOctreeFileReplacementFromURLRequest(QSharedPointer message) { auto node = DependencyManager::get()->nodeWithUUID(message->getSourceID()); if (node->getCanReplaceContent()) { handleOctreeFileReplacement(message->readAll()); } -} +} \ No newline at end of file diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 6e9fe28c0a..b118008d3d 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -91,7 +91,7 @@ private slots: void processICEServerHeartbeatDenialPacket(QSharedPointer message); void processICEServerHeartbeatACK(QSharedPointer message); - void handleOctreeFileReplacementFromURLRequest(QSharedPointer message); + void handleDomainContentReplacementFromURLRequest(QSharedPointer message); void handleOctreeFileReplacementRequest(QSharedPointer message); void handleOctreeFileReplacement(QByteArray octreeFile); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index bc60d23166..35025ff5e9 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -26,24 +26,23 @@ generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources if (ANDROID) # on Android, don't compress the rcc binary add_custom_command( - OUTPUT ${RESOURCES_RCC} - DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} - COMMAND "${QT_DIR}/bin/rcc" - ARGS ${RESOURCES_QRC} -no-compress -binary -o ${RESOURCES_RCC} + OUTPUT ${RESOURCES_RCC} + DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} + COMMAND "${QT_DIR}/bin/rcc" + ARGS ${RESOURCES_QRC} -no-compress -binary -o ${RESOURCES_RCC} ) else () add_custom_command( - OUTPUT ${RESOURCES_RCC} - DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} - COMMAND "${QT_DIR}/bin/rcc" - ARGS ${RESOURCES_QRC} -binary -o ${RESOURCES_RCC} + OUTPUT ${RESOURCES_RCC} + DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} + COMMAND "${QT_DIR}/bin/rcc" + ARGS ${RESOURCES_QRC} -binary -o ${RESOURCES_RCC} ) endif() list(APPEND GENERATE_QRC_DEPENDS ${RESOURCES_RCC}) add_custom_target(resources ALL DEPENDS ${GENERATE_QRC_DEPENDS}) - # set a default root dir for each of our optional externals if it was not passed set(OPTIONAL_EXTERNALS "LeapMotion") @@ -191,7 +190,11 @@ add_dependencies(${TARGET_NAME} resources) if (WIN32) # These are external plugins, but we need to do the 'add dependency' here so that their # binary directories get added to the fixup path - add_dependency_external_projects(sixense) + + if (USE_SIXENSE) + add_dependency_external_projects(sixense) + endif () + add_dependency_external_projects(sdl2) add_dependency_external_projects(OpenVR) add_dependency_external_projects(neuron) @@ -199,12 +202,6 @@ if (WIN32) add_dependency_external_projects(steamworks) endif() -# include OPENSSL -include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") - -# append OpenSSL to our list of libraries to link -target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) - # disable /OPT:REF and /OPT:ICF for the Debug builds # This will prevent the following linker warnings # LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification @@ -227,6 +224,9 @@ link_hifi_libraries( # include the binary directory of render-utils for shader includes target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/render-utils") +# include OpenSSL +target_openssl() + target_bullet() target_opengl() add_crashpad() @@ -312,35 +312,41 @@ if (APPLE) ) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources") - + set(RESOURCES_DEV_DIR "$/../Resources") # copy script files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" - "$/../Resources/scripts" + "${RESOURCES_DEV_DIR}/scripts" ) # call the fixup_interface macro to add required bundling commands for installation fixup_interface() else() + set(INTERFACE_EXEC_DIR "$") + set(RESOURCES_DEV_DIR "${INTERFACE_EXEC_DIR}/resources") + # copy the resources files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_if_different - "${RESOURCES_RCC}" - "$" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "${RESOURCES_RCC}" + "${INTERFACE_EXEC_DIR}" # FIXME, the edit script code loads HTML from the scripts folder # which in turn relies on CSS that refers to the fonts. In theory - # we should be able to modify the CSS to reference the QRC path to - # the ttf files, but doing so generates a CORS policy violation, + # we should be able to modify the CSS to reference the QRC path to + # the ttf files, but doing so generates a CORS policy violation, # so we have to retain a copy of the fonts outside of the resources binary COMMAND "${CMAKE_COMMAND}" -E copy_directory - "${PROJECT_SOURCE_DIR}/resources/fonts" - "$/resources/fonts" + "${PROJECT_SOURCE_DIR}/resources/fonts" + "${RESOURCES_DEV_DIR}/fonts" COMMAND "${CMAKE_COMMAND}" -E copy_directory - "${CMAKE_SOURCE_DIR}/scripts" - "$/scripts" + "${CMAKE_SOURCE_DIR}/scripts" + "${INTERFACE_EXEC_DIR}/scripts" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "${PROJECT_SOURCE_DIR}/resources/serverless/tutorial.json" + "${RESOURCES_DEV_DIR}/serverless/tutorial.json" ) # link target to external libraries @@ -366,7 +372,6 @@ else() endif() if (SCRIPTS_INSTALL_DIR) - # setup install of scripts beside interface executable install( DIRECTORY "${CMAKE_SOURCE_DIR}/scripts/" @@ -375,6 +380,19 @@ if (SCRIPTS_INSTALL_DIR) ) endif() +if (DOWNLOAD_SERVERLESS_CONTENT) + add_dependency_external_projects(serverless-content) + + ExternalProject_Get_Property(serverless-content SOURCE_DIR) + + # for dev builds, copy the serverless content to the resources folder + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${SOURCE_DIR}" + "${RESOURCES_DEV_DIR}/serverless" + ) +endif () + if (WIN32) set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/resources/qml\"") @@ -388,3 +406,6 @@ endif() add_dependency_external_projects(GifCreator) find_package(GifCreator REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GIFCREATOR_INCLUDE_DIRS}) + +# tell CMake to exclude ui_console.h for policy CMP0071 +set_property(SOURCE ui_console.h PROPERTY SKIP_AUTOMOC ON) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index b3f16a115e..174f9af7d7 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -1,15 +1,10 @@ { "name": "Keyboard/Mouse to Actions", "channels": [ - - { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.A", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.D", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.E", "when": "Keyboard.Shift", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.05 } ] }, - { "from": "Keyboard.C", "when": "Keyboard.Shift", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.05 } ] }, - { "from": "Keyboard.S", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, - { "from": "Keyboard.W", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, + { "from": "Keyboard.E", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.Q", "to": "Actions.LATERAL_LEFT" }, { "comment" : "Mouse turn need to be small continuous increments", @@ -44,9 +39,24 @@ ] }, + { "from": { "makeAxis" : [ - ["Keyboard.A", "Keyboard.Left" ], - ["Keyboard.D", "Keyboard.Right"] + ["Keyboard.Left" ], + ["Keyboard.Right"] + ] + }, + "when": ["Application.InHMD", "Application.SnapTurn", "!Keyboard.Shift"], + "to": "Actions.StepYaw", + "filters": + [ + { "type": "pulse", "interval": 0.5, "resetOnZero": true }, + { "type": "scale", "scale": 22.5 } + ] + }, + + { "from": { "makeAxis" : [ + ["Keyboard.A"], + ["Keyboard.D"] ] }, "when": [ "Application.InHMD", "Application.SnapTurn" ], @@ -59,26 +69,39 @@ }, { "from": { "makeAxis" : [ - ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], - ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] + ["Keyboard.Left"], + ["Keyboard.Right"] + ] + }, + "when": ["Application.CameraFirstPerson", "!Keyboard.Shift"], + "to": "Actions.Yaw" + }, + + { "from": { "makeAxis" : [ + ["Keyboard.Left"], + ["Keyboard.Right"] + ] + }, + "when": ["Application.CameraThirdPerson", "!Keyboard.Shift"], + "to": "Actions.Yaw" + }, + + { "from": { "makeAxis" : [ + ["Keyboard.A", "Keyboard.TouchpadLeft"], + ["Keyboard.D", "Keyboard.TouchpadRight"] ] }, "when": "Application.CameraFirstPerson", "to": "Actions.Yaw" }, { "from": { "makeAxis" : [ - ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], - ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] + ["Keyboard.A", "Keyboard.TouchpadLeft"], + ["Keyboard.D", "Keyboard.TouchpadRight"] ] }, "when": "Application.CameraThirdPerson", "to": "Actions.Yaw" }, - { "from": { "makeAxis" : [ ["Keyboard.A"], ["Keyboard.D"] ] }, - "when": "Application.CameraFSM", - "to": "Actions.Yaw" - }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, "when": "Keyboard.RightMouseButton", "to": "Actions.Yaw", @@ -90,14 +113,10 @@ { "from": "Keyboard.W", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.S", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_BACKWARD" }, - { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, - { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, - { "from": "Keyboard.Left", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.Right", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" }, + { "from": "Keyboard.Control", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.Down", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, - { "from": "Keyboard.Up", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, { "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Down", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_BACKWARD" }, @@ -128,7 +147,7 @@ { "from": "Keyboard.MouseWheelLeft", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.02 } ]}, { "from": "Keyboard.MouseWheelRight", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.02 } ]}, - { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, + { "from": "Keyboard.Space", "to": "Actions.VERTICAL_UP" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, { "from": "Keyboard.T", "to": "Actions.ACTION2" }, { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } diff --git a/interface/resources/controllers/touchscreenvirtualpad.json b/interface/resources/controllers/touchscreenvirtualpad.json index 8c21044c3b..907ff8b403 100644 --- a/interface/resources/controllers/touchscreenvirtualpad.json +++ b/interface/resources/controllers/touchscreenvirtualpad.json @@ -5,8 +5,8 @@ { "from": "TouchscreenVirtualPad.LX", "when": "!Application.CameraIndependent", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Actions.TranslateX" }, - { "from": "TouchscreenVirtualPad.RX", "when": "!Application.CameraIndependent", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Actions.Yaw" }, + { "from": "TouchscreenVirtualPad.RX", "when": "!Application.CameraIndependent", "filters": [ {"type": "deadZone", "min": 0.05} , "invert" ], "to": "Actions.Yaw" }, - { "from": "TouchscreenVirtualPad.RY", "when": "!Application.CameraIndependent", "to": "Actions.Pitch" } + { "from": "TouchscreenVirtualPad.RY", "when": "!Application.CameraIndependent", "filters": [ {"type": "deadZone", "min": 0.05}, "invert" ], "to": "Actions.Pitch" } ] } diff --git a/interface/resources/html/img/tablet-help-keyboard.jpg b/interface/resources/html/img/tablet-help-keyboard.jpg index a62fbe9450..d0f84c17c7 100644 Binary files a/interface/resources/html/img/tablet-help-keyboard.jpg and b/interface/resources/html/img/tablet-help-keyboard.jpg differ diff --git a/interface/resources/icons/+android/stats.svg b/interface/resources/icons/+android/stats.svg new file mode 100644 index 0000000000..f642650c66 --- /dev/null +++ b/interface/resources/icons/+android/stats.svg @@ -0,0 +1,16 @@ + + + + + + diff --git a/interface/resources/icons/tablet-icons/market-a-msg.svg b/interface/resources/icons/tablet-icons/market-a-msg.svg new file mode 100644 index 0000000000..0ab93f3cc8 --- /dev/null +++ b/interface/resources/icons/tablet-icons/market-a-msg.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/market-a.svg b/interface/resources/icons/tablet-icons/market-a.svg index f8ba17301e..db2d948d7b 100644 --- a/interface/resources/icons/tablet-icons/market-a.svg +++ b/interface/resources/icons/tablet-icons/market-a.svg @@ -1,64 +1,15 @@ - - - -image/svg+xml \ No newline at end of file + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/market-i-msg.svg b/interface/resources/icons/tablet-icons/market-i-msg.svg new file mode 100644 index 0000000000..488c507c6e --- /dev/null +++ b/interface/resources/icons/tablet-icons/market-i-msg.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/interface/resources/icons/tablet-icons/market-i.svg b/interface/resources/icons/tablet-icons/market-i.svg index bf9aa9335f..7d11507cdb 100644 --- a/interface/resources/icons/tablet-icons/market-i.svg +++ b/interface/resources/icons/tablet-icons/market-i.svg @@ -1,23 +1,19 @@ - - + - - - - - - - - + + + + diff --git a/interface/resources/qml/+android/AddressBarDialog.qml b/interface/resources/qml/+android/AddressBarDialog.qml index b8d6b5e270..4477d512fc 100644 --- a/interface/resources/qml/+android/AddressBarDialog.qml +++ b/interface/resources/qml/+android/AddressBarDialog.qml @@ -67,6 +67,10 @@ Item { fill: parent } + MouseArea { + anchors.fill: parent + } + QmlHifi.WindowHeader { id: header iconSource: "../../../icons/goto-i.svg" diff --git a/interface/resources/qml/+android/LoginDialog.qml b/interface/resources/qml/+android/LoginDialog.qml index 9eb2c74147..567cca9bcf 100644 --- a/interface/resources/qml/+android/LoginDialog.qml +++ b/interface/resources/qml/+android/LoginDialog.qml @@ -38,7 +38,8 @@ ModalWindow { keyboardOverride: true // Disable ModalWindow's keyboard. function tryDestroy() { - root.destroy() + Controller.setVPadHidden(false); + root.destroy(); } LoginDialog { @@ -52,8 +53,9 @@ ModalWindow { Component.onCompleted: { this.anchors.centerIn = undefined; - this.y=150; - this.x=(parent.width - this.width)/2; + this.y = 150; + this.x = (parent.width - this.width) / 2; + Controller.setVPadHidden(true); } Keys.onPressed: { diff --git a/interface/resources/qml/+android/StatText.qml b/interface/resources/qml/+android/StatText.qml new file mode 100644 index 0000000000..5dc8377030 --- /dev/null +++ b/interface/resources/qml/+android/StatText.qml @@ -0,0 +1,9 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 + +Text { + color: "white"; + style: Text.Outline; + styleColor: "black"; + font.pixelSize: 15; +} diff --git a/interface/resources/qml/AudioScope.qml b/interface/resources/qml/AudioScopeUI.qml similarity index 100% rename from interface/resources/qml/AudioScope.qml rename to interface/resources/qml/AudioScopeUI.qml diff --git a/interface/resources/qml/AvatarInputs.qml b/interface/resources/qml/AvatarInputsBar.qml similarity index 88% rename from interface/resources/qml/AvatarInputs.qml rename to interface/resources/qml/AvatarInputsBar.qml index be4bf03465..a88a42080e 100644 --- a/interface/resources/qml/AvatarInputs.qml +++ b/interface/resources/qml/AvatarInputsBar.qml @@ -14,9 +14,9 @@ import Qt.labs.settings 1.0 import "./hifi/audio" as HifiAudio -Hifi.AvatarInputs { +Item { id: root; - objectName: "AvatarInputs" + objectName: "AvatarInputsBar" property int modality: Qt.NonModal width: audio.width; height: audio.height; @@ -26,7 +26,7 @@ Hifi.AvatarInputs { HifiAudio.MicBar { id: audio; - visible: root.showAudioTools; + visible: AvatarInputs.showAudioTools; standalone: true; dragTarget: parent; } diff --git a/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml index 7eced0c751..38e65af4ca 100644 --- a/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/+android/LinkAccountBody.qml @@ -224,7 +224,7 @@ Item { onClicked: { Qt.inputMethod.hide(); - root.destroy(); + root.tryDestroy(); } } } diff --git a/interface/resources/qml/controls-uit/FilterBar.qml b/interface/resources/qml/controls-uit/FilterBar.qml new file mode 100644 index 0000000000..ecae790b22 --- /dev/null +++ b/interface/resources/qml/controls-uit/FilterBar.qml @@ -0,0 +1,321 @@ +// +// FilterBar.qml +// +// Created by Zach Fox on 17 Feb 2018-03-12 +// 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 QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 + +import "../styles-uit" +import "../controls-uit" as HifiControls + +Item { + id: root; + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + readonly property bool isFaintGrayColorScheme: colorScheme == hifi.colorSchemes.faintGray + property bool error: false; + property alias textFieldHeight: textField.height; + property string placeholderText; + property alias dropdownHeight: dropdownContainer.height; + property alias text: textField.text; + property alias primaryFilterChoices: filterBarModel; + property int primaryFilter_index: -1; + property string primaryFilter_filterName: ""; + property string primaryFilter_displayName: ""; + signal accepted; + + onPrimaryFilter_indexChanged: { + if (primaryFilter_index === -1) { + primaryFilter_filterName = ""; + primaryFilter_displayName = ""; + } else { + primaryFilter_filterName = filterBarModel.get(primaryFilter_index).filterName; + primaryFilter_displayName = filterBarModel.get(primaryFilter_index).displayName; + } + } + + TextField { + id: textField; + + anchors.top: parent.top; + anchors.right: parent.right; + anchors.left: parent.left; + + font.family: "Fira Sans" + font.pixelSize: hifi.fontSizes.textFieldInput; + + placeholderText: root.primaryFilter_index === -1 ? root.placeholderText : ""; + + TextMetrics { + id: primaryFilterTextMetrics; + font.family: "FiraSans Regular"; + font.pixelSize: hifi.fontSizes.textFieldInput; + font.capitalization: Font.AllUppercase; + text: root.primaryFilter_displayName; + } + + // workaround for https://bugreports.qt.io/browse/QTBUG-49297 + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Return: + case Qt.Key_Enter: + event.accepted = true; + + // emit accepted signal manually + if (acceptableInput) { + root.accepted(); + root.forceActiveFocus(); + } + break; + case Qt.Key_Backspace: + if (textField.text === "") { + primaryFilter_index = -1; + } + break; + } + } + + onAccepted: { + root.forceActiveFocus(); + } + + onActiveFocusChanged: { + if (!activeFocus) { + dropdownContainer.visible = false; + } + } + + color: { + if (isLightColorScheme) { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.lightGray + } + } else if (isFaintGrayColorScheme) { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.lightGray + } + } else { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.lightGrayText + } + } + } + + background: Rectangle { + id: mainFilterBarRectangle; + + color: { + if (isLightColorScheme) { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.textFieldLightBackground + } + } else if (isFaintGrayColorScheme) { + if (textField.activeFocus) { + hifi.colors.white + } else { + hifi.colors.faintGray50 + } + } else { + if (textField.activeFocus) { + hifi.colors.black + } else { + hifi.colors.baseGrayShadow + } + } + } + + border.color: textField.error ? hifi.colors.redHighlight : + (textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray)) + border.width: 1 + radius: 4 + + Item { + id: searchButtonContainer; + anchors.left: parent.left; + anchors.verticalCenter: parent.verticalCenter; + height: parent.height; + width: 42; + + // Search icon + HiFiGlyphs { + id: searchIcon; + text: hifi.glyphs.search + color: textField.color + size: 40; + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + width: paintedWidth; + } + + // Carat + HiFiGlyphs { + text: hifi.glyphs.caratDn; + color: textField.color; + size: 40; + anchors.left: parent.left; + anchors.leftMargin: 15; + width: paintedWidth; + } + + MouseArea { + anchors.fill: parent; + onClicked: { + textField.forceActiveFocus(); + dropdownContainer.visible = !dropdownContainer.visible; + } + } + } + + Rectangle { + z: 999; + id: primaryFilterContainer; + color: textField.activeFocus ? hifi.colors.faintGray : hifi.colors.white; + width: primaryFilterTextMetrics.tightBoundingRect.width + 14; + height: parent.height - 8; + anchors.verticalCenter: parent.verticalCenter; + anchors.left: searchButtonContainer.right; + anchors.leftMargin: 4; + visible: primaryFilterText.text !== ""; + radius: height/2; + + FiraSansRegular { + id: primaryFilterText; + text: root.primaryFilter_displayName; + anchors.fill: parent; + color: textField.activeFocus ? hifi.colors.black : hifi.colors.lightGray; + horizontalAlignment: Text.AlignHCenter; + verticalAlignment: Text.AlignVCenter; + size: hifi.fontSizes.textFieldInput; + font.capitalization: Font.AllUppercase; + } + + MouseArea { + anchors.fill: parent; + onClicked: { + textField.forceActiveFocus(); + } + } + } + + // "Clear" button + HiFiGlyphs { + text: hifi.glyphs.error + color: textField.color + size: 40 + anchors.right: parent.right + anchors.rightMargin: hifi.dimensions.textPadding - 2 + anchors.verticalCenter: parent.verticalCenter + visible: root.text !== "" || root.primaryFilter_index !== -1; + + MouseArea { + anchors.fill: parent; + onClicked: { + root.text = ""; + root.primaryFilter_index = -1; + dropdownContainer.visible = false; + textField.forceActiveFocus(); + } + } + } + } + + selectedTextColor: hifi.colors.black + selectionColor: hifi.colors.primaryHighlight + leftPadding: 44 + (root.primaryFilter_index === -1 ? 0 : primaryFilterTextMetrics.tightBoundingRect.width + 20); + rightPadding: 44; + } + + Rectangle { + id: dropdownContainer; + visible: false; + height: 50 * filterBarModel.count; + width: parent.width; + anchors.top: textField.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + color: hifi.colors.white; + + ListModel { + id: filterBarModel; + } + + ListView { + id: dropdownListView; + interactive: false; + anchors.fill: parent; + model: filterBarModel; + delegate: Rectangle { + id: dropDownButton; + color: hifi.colors.white; + width: parent.width; + height: 50; + + RalewaySemiBold { + id: dropDownButtonText; + text: model.displayName; + anchors.fill: parent; + 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; + } + } + } + } + } + + DropShadow { + anchors.fill: dropdownContainer; + horizontalOffset: 0; + verticalOffset: 4; + radius: 4.0; + samples: 9 + color: Qt.rgba(0, 0, 0, 0.25); + source: dropdownContainer; + visible: dropdownContainer.visible; + } + + function changeFilterByDisplayName(name) { + for (var i = 0; i < filterBarModel.count; i++) { + if (filterBarModel.get(i).displayName === name) { + root.primaryFilter_index = i; + return; + } + } + + console.log("Passed displayName not found in filterBarModel! primaryFilter unchanged."); + } +} diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index 782ab454b5..f94541897b 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -163,8 +163,10 @@ TextField { text: textField.label colorScheme: textField.colorScheme anchors.left: parent.left + anchors.right: parent.right anchors.bottom: parent.top anchors.bottomMargin: 3 + wrapMode: Text.WordWrap visible: label != "" } } diff --git a/interface/resources/qml/dialogs/CustomQueryDialog.qml b/interface/resources/qml/dialogs/CustomQueryDialog.qml index 4d6fe74bca..6e1bb4b309 100644 --- a/interface/resources/qml/dialogs/CustomQueryDialog.qml +++ b/interface/resources/qml/dialogs/CustomQueryDialog.qml @@ -270,7 +270,9 @@ ModalWindow { onTriggered: { root.result = null; root.canceled(); - root.destroy(); + // FIXME we are leaking memory to avoid a crash + // root.destroy(); + visible = false; } } @@ -292,7 +294,9 @@ ModalWindow { } root.result = JSON.stringify(result); root.selected(root.result); - root.destroy(); + // FIXME we are leaking memory to avoid a crash + // root.destroy(); + visible = false; } } } diff --git a/interface/resources/qml/dialogs/QueryDialog.qml b/interface/resources/qml/dialogs/QueryDialog.qml index b5de5362f2..6f05179bd5 100644 --- a/interface/resources/qml/dialogs/QueryDialog.qml +++ b/interface/resources/qml/dialogs/QueryDialog.qml @@ -169,7 +169,9 @@ ModalWindow { shortcut: Qt.Key_Escape onTriggered: { root.canceled(); - root.destroy(); + // FIXME we are leaking memory to avoid a crash + // root.destroy(); + visible = false; } } Action { @@ -179,7 +181,9 @@ ModalWindow { onTriggered: { root.result = items ? comboBox.currentText : textResult.text root.selected(root.result); - root.destroy(); + // FIXME we are leaking memory to avoid a crash + // root.destroy(); + visible = false; } } } diff --git a/interface/resources/qml/dialogs/TabletQueryDialog.qml b/interface/resources/qml/dialogs/TabletQueryDialog.qml index e21677c12c..60dbc106dc 100644 --- a/interface/resources/qml/dialogs/TabletQueryDialog.qml +++ b/interface/resources/qml/dialogs/TabletQueryDialog.qml @@ -65,34 +65,33 @@ TabletModalWindow { id: modalWindowItem width: parent.width - 12 height: 240 - anchors { - verticalCenter: parent.verticalCenter - horizontalCenter: parent.horizontalCenter - } + anchors.horizontalCenter: parent.horizontalCenter QtObject { id: d - readonly property int minWidth: 470 - readonly property int maxWidth: 470 + readonly property int minWidth: modalWindowItem.width + readonly property int maxWidth: modalWindowItem.width readonly property int minHeight: 120 readonly property int maxHeight: 720 function resize() { - var targetWidth = Math.max(titleWidth, 470) + var targetWidth = Math.max(titleWidth, modalWindowItem.width) var targetHeight = (items ? comboBox.controlHeight : textResult.controlHeight) + 5 * hifi.dimensions.contentSpacing.y + buttons.height modalWindowItem.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth); - modalWindowItem.height = ((targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + 2 * hifi.dimensions.contentSpacing.y) : 0) + modalWindowItem.frameMarginTop + modalWindowItem.height = ((targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + modalWindowItem.frameMarginTop + modalWindowItem.y = (root.height - (modalWindowItem.height + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + 2 * hifi.dimensions.contentSpacing.y) : 0))) / 2 } } Item { anchors { top: parent.top - bottom: keyboard.top; + bottom: buttons.top; left: parent.left; right: parent.right; margins: 0 bottomMargin: 2 * hifi.dimensions.contentSpacing.y + topMargin: modalWindowItem.frameMarginTop } // FIXME make a text field type that can be bound to a history for autocompletion @@ -106,6 +105,7 @@ TabletModalWindow { right: parent.right; bottom: parent.bottom leftMargin: 5 + rightMargin: 5 } } @@ -124,22 +124,6 @@ TabletModalWindow { } } - property alias keyboardOverride: root.keyboardOverride - property alias keyboardRaised: root.keyboardRaised - property alias punctuationMode: root.punctuationMode - - Keyboard { - id: keyboard - raised: keyboardEnabled && keyboardRaised - numeric: punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: buttons.top - bottomMargin: raised ? 2 * hifi.dimensions.contentSpacing.y : 0 - } - } - Flow { id: buttons focus: true @@ -150,6 +134,7 @@ TabletModalWindow { bottom: parent.bottom right: parent.right margins: 0 + rightMargin: hifi.dimensions.borderRadius bottomMargin: hifi.dimensions.contentSpacing.y } Button { action: cancelAction } @@ -177,7 +162,17 @@ TabletModalWindow { } } - Keys.onPressed: { + Keyboard { + id: keyboard + raised: keyboardEnabled && keyboardRaised + numeric: punctuationMode + anchors { + left: parent.left + right: parent.right + top: modalWindowItem.bottom + } + } + Keys.onPressed: { if (!visible) { return } diff --git a/interface/resources/qml/hifi/+android/StatsBar.qml b/interface/resources/qml/hifi/+android/StatsBar.qml new file mode 100644 index 0000000000..aee438b44f --- /dev/null +++ b/interface/resources/qml/hifi/+android/StatsBar.qml @@ -0,0 +1,71 @@ +import QtQuick 2.5 +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 "../../controls" as HifiControls +import ".." + +Item { + id: bar + x:300 + y:0 + width: 300 + height: 300 + z: -1 + + signal sendToScript(var message); + signal windowClosed(); + + property bool shown: true + + onShownChanged: { + bar.visible = shown; + } + + Rectangle { + anchors.fill : parent + color: "transparent" + Flow { + id: flowMain + spacing: 10 + flow: Flow.TopToBottom + layoutDirection: Flow.TopToBottom + anchors.fill: parent + anchors.margins: 4 + } + } + + Component.onCompleted: { + // put on bottom + x = 300; + y = 0; + width = 300; + height = 300; + } + + function addButton(properties) { + var component = Qt.createComponent("button.qml"); + if (component.status == Component.Ready) { + var button = component.createObject(flowMain); + // copy all properites to button + var keys = Object.keys(properties).forEach(function (key) { + button[key] = properties[key]; + }); + return button; + } else if( component.status == Component.Error) { + console.log("Load button errors " + component.errorString()); + } + } + + function urlHelper(src) { + if (src.match(/\bhttp/)) { + return src; + } else { + return "../../../" + src; + } + } + +} diff --git a/interface/resources/qml/hifi/+android/avatarSelection.qml b/interface/resources/qml/hifi/+android/avatarSelection.qml index 3090204308..afa5634575 100644 --- a/interface/resources/qml/hifi/+android/avatarSelection.qml +++ b/interface/resources/qml/hifi/+android/avatarSelection.qml @@ -58,6 +58,10 @@ Item { width: parent ? parent.width : 0 height: parent ? parent.height : 0 + MouseArea { + anchors.fill: parent + } + gradient: Gradient { GradientStop { position: 0.0; color: android.color.gradientTop } GradientStop { position: 1.0; color: android.color.gradientBottom } diff --git a/interface/resources/qml/hifi/+android/bottombar.qml b/interface/resources/qml/hifi/+android/bottombar.qml index e31dc9453f..66117d0389 100644 --- a/interface/resources/qml/hifi/+android/bottombar.qml +++ b/interface/resources/qml/hifi/+android/bottombar.qml @@ -25,6 +25,7 @@ import "." Item { id: bar x:0 + height: 255 property bool shown: true @@ -45,10 +46,10 @@ Item { anchors.fill: parent } - Rectangle { + Rectangle { id: background anchors.fill : parent - color: "#FF000000" + color: "#FF000000" border.color: "#FFFFFF" anchors.bottomMargin: -1 anchors.leftMargin: -1 @@ -104,13 +105,25 @@ Item { } } } - } + } + + function relocateAndResize(newWindowWidth, newWindowHeight) { + width = newWindowWidth; + y = newWindowHeight - height; + } + + function onWindowGeometryChanged(rect) { + relocateAndResize(rect.width, rect.height); + } Component.onCompleted: { // put on bottom - width = Window.innerWidth; - height = 255; - y = Window.innerHeight - height; + relocateAndResize(Window.innerWidth, Window.innerHeight); + Window.geometryChanged.connect(onWindowGeometryChanged); // In devices with bars appearing at startup we should listen for this + } + + Component.onDestruction: { + Window.geometryChanged.disconnect(onWindowGeometryChanged); } function addButton(properties) { diff --git a/interface/resources/qml/hifi/+android/button.qml b/interface/resources/qml/hifi/+android/button.qml index 4822b6bf33..3e9ce39351 100644 --- a/interface/resources/qml/hifi/+android/button.qml +++ b/interface/resources/qml/hifi/+android/button.qml @@ -118,7 +118,7 @@ Item { tabletRoot.playButtonClickSound(); }*/ } - onEntered: { + onPressed: { button.isEntered = true; button.entered(); if (button.isActive) { @@ -127,7 +127,7 @@ Item { button.state = "hover state"; } } - onExited: { + onReleased: { button.isEntered = false; button.exited() if (button.isActive) { diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index 34be11d4df..1ff954feff 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -24,6 +24,7 @@ Windows.ScrollingWindow { objectName: "AssetServer" title: "Asset Browser" resizable: true + opacity: parent.opacity destroyOnHidden: true implicitWidth: 384; implicitHeight: 640 minSize: Qt.vector2d(200, 300) @@ -57,7 +58,7 @@ Windows.ScrollingWindow { Component.onDestruction: { assetMappingsModel.autoRefreshEnabled = false; } - + function letterbox(headerGlyph, headerText, message) { letterboxMessage.headerGlyph = headerGlyph; letterboxMessage.headerText = headerText; @@ -144,7 +145,7 @@ Windows.ScrollingWindow { function canAddToWorld(path) { var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i]; - + if (selectedItemCount > 1) { return false; } @@ -153,8 +154,8 @@ Windows.ScrollingWindow { return total | new RegExp(current).test(path); }, false); } - - function canRename() { + + function canRename() { if (treeView.selection.hasSelection && selectedItemCount == 1) { return true; } else { @@ -198,7 +199,7 @@ Windows.ScrollingWindow { var SHAPE_TYPE_STATIC_MESH = 3; var SHAPE_TYPE_BOX = 4; var SHAPE_TYPE_SPHERE = 5; - + var SHAPE_TYPES = []; SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; @@ -206,7 +207,7 @@ Windows.ScrollingWindow { SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_SIMPLE_COMPOUND; var DYNAMIC_DEFAULT = false; var prompt = desktop.customInputDialog({ @@ -348,14 +349,14 @@ Windows.ScrollingWindow { } function deleteFile(index) { var paths = []; - + if (!index) { for (var i = 0; i < selectedItemCount; ++i) { index = treeView.selection.selectedIndexes[i]; paths[i] = assetProxyModel.data(index, 0x100); } } - + if (!paths) { return; } @@ -364,13 +365,13 @@ Windows.ScrollingWindow { var items = selectedItemCount.toString(); var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101); var typeString = isFolder ? 'folder' : 'file'; - + if (selectedItemCount > 1) { modalMessage = "You are about to delete " + items + " items \nDo you want to continue?"; } else { modalMessage = "You are about to delete the following " + typeString + ":\n" + paths + "\nDo you want to continue?"; } - + var object = desktop.messageBox({ icon: hifi.icons.question, buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No, @@ -475,11 +476,11 @@ Windows.ScrollingWindow { }); } } - + Item { width: pane.contentWidth height: pane.height - + // The letterbox used for popup messages LetterboxMessage { id: letterboxMessage; @@ -541,7 +542,7 @@ Windows.ScrollingWindow { anchors.margins: hifi.dimensions.contentMargin.x + 2 // Extra for border anchors.left: parent.left anchors.right: parent.right - + treeModel: assetProxyModel selectionMode: SelectionMode.ExtendedSelection headerVisible: true @@ -561,9 +562,13 @@ Windows.ScrollingWindow { id: bakedColumn title: "Use Baked?" role: "baked" - width: 100 + width: 170 } - + + onSortIndicatorOrderChanged: { + Assets.sortProxyModel(sortIndicatorColumn, sortIndicatorOrder); + } + itemDelegate: Loader { id: itemDelegateLoader @@ -599,7 +604,7 @@ Windows.ScrollingWindow { } sourceComponent: getComponent() - + Component { id: labelComponent FiraSansSemiBold { @@ -608,15 +613,15 @@ Windows.ScrollingWindow { color: colorScheme == hifi.colorSchemes.light ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - + horizontalAlignment: styleData.column === 1 ? TextInput.AlignHCenter : TextInput.AlignLeft - + elide: Text.ElideMiddle MouseArea { id: mouseArea anchors.fill: parent - + acceptedButtons: Qt.NoButton hoverEnabled: true @@ -638,7 +643,7 @@ Windows.ScrollingWindow { color: colorScheme == hifi.colorSchemes.light ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - + elide: Text.ElideRight horizontalAlignment: TextInput.AlignHCenter @@ -725,7 +730,7 @@ Windows.ScrollingWindow { size: hifi.fontSizes.tableText color: colorScheme == hifi.colorSchemes.light ? hifi.colors.black : hifi.colors.lightGrayText } - + Timer { id: showTimer interval: 1000 @@ -744,7 +749,7 @@ Windows.ScrollingWindow { treeLabelToolTip.visible = false; } }// End_OF( treeLabelToolTip ) - + MouseArea { propagateComposedEvents: true anchors.fill: parent @@ -802,7 +807,7 @@ Windows.ScrollingWindow { anchors.left: treeView.left anchors.right: treeView.right anchors.bottom: uploadSection.top - + RalewayRegular { anchors.verticalCenter: parent.verticalCenter @@ -846,7 +851,7 @@ Windows.ScrollingWindow { checked = Qt.binding(isChecked); } - + function isEnabled() { if (!treeView.selection.hasSelection) { return false; @@ -870,7 +875,7 @@ Windows.ScrollingWindow { } } - return true; + return true; } function isChecked() { if (!treeView.selection.hasSelection) { @@ -878,10 +883,10 @@ Windows.ScrollingWindow { } var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105); - return isEnabled() && status !== "Not Baked"; - } + return isEnabled() && status !== "Not Baked"; + } } - + Item { anchors.verticalCenter: parent.verticalCenter width: infoGlyph.size; @@ -905,7 +910,7 @@ Windows.ScrollingWindow { "What is baking?", "Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors."); } - } + } }// End_OF( infoRow ) HifiControls.ContentSection { diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 96ffa390bf..3152a1eed6 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -30,25 +30,31 @@ Rectangle { property string activeView: "initialize"; property bool ownershipStatusReceived: false; property bool balanceReceived: false; + property bool availableUpdatesReceived: false; + property string baseItemName: ""; property string itemName; property string itemId; property string itemHref; property string itemAuthor; + property int itemEdition: -1; + property string certificateId; property double balanceAfterPurchase; property bool alreadyOwned: false; property int itemPrice: -1; property bool isCertified; property string itemType; - property var itemTypesArray: ["entity", "wearable", "contentSet", "app", "avatar"]; - property var itemTypesText: ["entity", "wearable", "content set", "app", "avatar"]; - property var buttonTextNormal: ["REZ", "WEAR", "REPLACE CONTENT SET", "INSTALL", "WEAR"]; - property var buttonTextClicked: ["REZZED!", "WORN!", "CONTENT SET REPLACED!", "INSTALLED!", "AVATAR CHANGED!"] - property var buttonGlyph: [hifi.glyphs.wand, hifi.glyphs.hat, hifi.glyphs.globe, hifi.glyphs.install, hifi.glyphs.avatar]; + property var itemTypesArray: ["entity", "wearable", "contentSet", "app", "avatar", "unknown"]; + property var itemTypesText: ["entity", "wearable", "content set", "app", "avatar", "item"]; + property var buttonTextNormal: ["REZ", "WEAR", "REPLACE CONTENT SET", "INSTALL", "WEAR", "REZ"]; + property var buttonTextClicked: ["REZZED!", "WORN!", "CONTENT SET REPLACED!", "INSTALLED!", "AVATAR CHANGED!", "REZZED!"] + property var buttonGlyph: [hifi.glyphs.wand, hifi.glyphs.hat, hifi.glyphs.globe, hifi.glyphs.install, hifi.glyphs.avatar, hifi.glyphs.wand]; property bool shouldBuyWithControlledFailure: false; property bool debugCheckoutSuccess: false; property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified(); property string referrer; property bool isInstalled; + property bool isUpdating; + property string baseAppURL; // Style color: hifi.colors.white; Connections { @@ -103,8 +109,8 @@ Rectangle { if (result.status !== 'success') { console.log("Failed to get balance", result.data.message); } else { - root.balanceReceived = true; root.balanceAfterPurchase = result.data.balance - root.itemPrice; + root.balanceReceived = true; root.refreshBuyUI(); } } @@ -113,13 +119,13 @@ Rectangle { if (result.status !== 'success') { console.log("Failed to get Already Owned status", result.data.message); } else { - root.ownershipStatusReceived = true; if (result.data.marketplace_item_id === root.itemId) { root.alreadyOwned = result.data.already_owned; } else { console.log("WARNING - Received 'Already Owned' status about different Marketplace ID!"); root.alreadyOwned = false; } + root.ownershipStatusReceived = true; root.refreshBuyUI(); } } @@ -129,11 +135,53 @@ Rectangle { root.isInstalled = true; } } + + onAvailableUpdatesResult: { + if (result.status !== 'success') { + console.log("Failed to get Available Updates", result.data.message); + } else { + for (var i = 0; i < result.data.updates.length; i++) { + // If the ItemID of the item we're looking at matches EITHER the ID of a "base" item + // OR the ID of an "updated" item, we're updating. + if (root.itemId === result.data.updates[i].item_id || + root.itemId === result.data.updates[i].updated_item_id) { + if (root.itemEdition !== -1 && root.itemEdition !== parseInt(result.data.updates[i].edition_number)) { + continue; + } + root.isUpdating = true; + root.baseItemName = result.data.updates[i].base_item_title; + // This CertID is the one corresponding to the base item CertID that the user already owns + root.certificateId = result.data.updates[i].certificate_id; + if (root.itemType === "app") { + root.baseAppURL = result.data.updates[i].item_download_url; + } + break; + } + } + root.availableUpdatesReceived = true; + refreshBuyUI(); + } + } + + onUpdateItemResult: { + if (result.status !== 'success') { + failureErrorText.text = result.message; + root.activeView = "checkoutFailure"; + } else { + root.itemHref = result.data.download_url; + if (result.data.categories.indexOf("Wearables") > -1) { + root.itemType = "wearable"; + } + root.activeView = "checkoutSuccess"; + } + } } onItemIdChanged: { root.ownershipStatusReceived = false; Commerce.alreadyOwned(root.itemId); + root.availableUpdatesReceived = false; + Commerce.getAvailableUpdates(root.itemId); itemPreviewImage.source = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg"; } @@ -161,6 +209,7 @@ Rectangle { } onItemPriceChanged: { + root.balanceReceived = false; Commerce.balance(); } @@ -240,6 +289,7 @@ Rectangle { Component.onCompleted: { ownershipStatusReceived = false; balanceReceived = false; + availableUpdatesReceived = false; Commerce.getWalletStatus(); } } @@ -316,7 +366,7 @@ Rectangle { Rectangle { id: loading; z: 997; - visible: !root.ownershipStatusReceived || !root.balanceReceived; + visible: !root.ownershipStatusReceived || !root.balanceReceived || !root.availableUpdatesReceived; anchors.fill: parent; color: hifi.colors.white; @@ -412,6 +462,7 @@ Rectangle { // "HFC" balance label HiFiGlyphs { id: itemPriceTextLabel; + visible: !(root.isUpdating && root.itemEdition > 0); text: hifi.glyphs.hfc; // Size size: 30; @@ -427,9 +478,9 @@ Rectangle { } FiraSansSemiBold { id: itemPriceText; - text: (root.itemPrice === -1) ? "--" : root.itemPrice; + text: (root.isUpdating && root.itemEdition > 0) ? "FREE\nUPDATE" : ((root.itemPrice === -1) ? "--" : root.itemPrice); // Text size - size: 26; + size: (root.isUpdating && root.itemEdition > 0) ? 20 : 26; // Anchors anchors.top: parent.top; anchors.right: parent.right; @@ -529,9 +580,13 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: "VIEW THIS ITEM IN MY PURCHASES"; + text: root.isUpdating ? "UPDATE TO THIS ITEM FOR FREE" : "VIEW THIS ITEM IN MY PURCHASES"; onClicked: { - sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName}); + if (root.isUpdating) { + sendToScript({method: 'checkout_goToPurchases', filterText: root.baseItemName}); + } else { + sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName}); + } } } @@ -539,7 +594,7 @@ Rectangle { HifiControlsUit.Button { id: buyButton; visible: !((root.itemType === "avatar" || root.itemType === "app") && viewInMyPurchasesButton.visible) - enabled: (root.balanceAfterPurchase >= 0 && ownershipStatusReceived && balanceReceived) || (!root.isCertified); + enabled: (root.balanceAfterPurchase >= 0 && ownershipStatusReceived && balanceReceived && availableUpdatesReceived) || (!root.isCertified) || root.isUpdating; color: viewInMyPurchasesButton.visible ? hifi.buttons.white : hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; anchors.top: viewInMyPurchasesButton.visible ? viewInMyPurchasesButton.bottom : @@ -548,10 +603,19 @@ Rectangle { height: 50; anchors.left: parent.left; anchors.right: parent.right; - text: ((root.isCertified) ? ((ownershipStatusReceived && balanceReceived) ? - (viewInMyPurchasesButton.visible ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item"); + text: (root.isUpdating && root.itemEdition > 0) ? "CONFIRM UPDATE" : (((root.isCertified) ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ? + ((viewInMyPurchasesButton.visible && !root.isUpdating) ? "Buy It Again" : "Confirm Purchase") : "--") : "Get Item")); onClicked: { - if (root.isCertified) { + if (root.isUpdating && root.itemEdition > 0) { + // If we're updating an app, the existing app needs to be uninstalled. + // This call will fail/return `false` if the app isn't installed, but that's OK. + if (root.itemType === "app") { + Commerce.uninstallApp(root.baseAppURL); + } + buyButton.enabled = false; + loading.visible = true; + Commerce.updateItem(root.certificateId); + } else if (root.isCertified) { if (!root.shouldBuyWithControlledFailure) { if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { lightboxPopup.titleText = "Purchase Content Set"; @@ -975,7 +1039,7 @@ Rectangle { buyButton.color = hifi.buttons.red; root.shouldBuyWithControlledFailure = true; } else { - buyButton.text = (root.isCertified ? ((ownershipStatusReceived && balanceReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item"); + buyButton.text = (root.isCertified ? ((ownershipStatusReceived && balanceReceived && availableUpdatesReceived) ? (root.alreadyOwned ? "Buy Another" : "Buy"): "--") : "Get Item"); buyButton.color = hifi.buttons.blue; root.shouldBuyWithControlledFailure = false; } @@ -1001,12 +1065,13 @@ Rectangle { function fromScript(message) { switch (message.method) { case 'updateCheckoutQML': - itemId = message.params.itemId; - itemName = message.params.itemName; + root.itemId = message.params.itemId; + root.itemName = message.params.itemName.trim(); root.itemPrice = message.params.itemPrice; - itemHref = message.params.itemHref; - referrer = message.params.referrer; - itemAuthor = message.params.itemAuthor; + root.itemHref = message.params.itemHref; + root.referrer = message.params.referrer; + root.itemAuthor = message.params.itemAuthor; + root.itemEdition = message.params.itemEdition || -1; refreshBuyUI(); break; default: @@ -1015,35 +1080,70 @@ Rectangle { } signal sendToScript(var message); + function canBuyAgain() { + return (root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet" || root.itemType === "unknown"); + } + + function handleContentSets() { + if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { + buyText.text = "The domain owner must enable 'Replace Content' permissions for you in this " + + "domain's server settings before you can replace this domain's content with " + root.itemName + ""; + buyTextContainer.color = "#FFC3CD"; + buyTextContainer.border.color = "#F3808F"; + buyGlyph.text = hifi.glyphs.alert; + buyGlyph.size = 54; + } + } + + function handleBuyAgainLogic() { + // If you can buy this item again... + if (canBuyAgain()) { + // If you can't afford another copy of the item... + 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 again."; + // Else if you don't already own the item... + } else { + buyText.text = "Your Wallet does not have sufficient funds to purchase this item."; + } + buyTextContainer.color = "#FFC3CD"; + buyTextContainer.border.color = "#F3808F"; + buyGlyph.text = hifi.glyphs.alert; + buyGlyph.size = 54; + // If you CAN afford another copy of the item... + } else { + handleContentSets(); + } + } + } + function refreshBuyUI() { if (root.isCertified) { - if (root.ownershipStatusReceived && root.balanceReceived) { - if (root.balanceAfterPurchase < 0) { - if (root.alreadyOwned) { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item again."; - viewInMyPurchasesButton.visible = true; + if (root.ownershipStatusReceived && root.balanceReceived && root.availableUpdatesReceived) { + buyText.text = ""; + + // If the user IS on the checkout page for the updated version of an owned item... + if (root.isUpdating) { + // If the user HAS already selected a specific edition to update... + if (root.itemEdition > 0) { + buyText.text = "By pressing \"Confirm Update\", you agree to trade in your old item for the updated item that replaces it."; + buyTextContainer.color = "#FFFFFF"; + buyTextContainer.border.color = "#FFFFFF"; + // Else if the user HAS NOT selected a specific edition to update... } else { - buyText.text = "Your Wallet does not have sufficient funds to purchase this item."; - } - buyTextContainer.color = "#FFC3CD"; - buyTextContainer.border.color = "#F3808F"; - buyGlyph.text = hifi.glyphs.alert; - buyGlyph.size = 54; + viewInMyPurchasesButton.visible = true; + + handleBuyAgainLogic(); + } + // If the user IS NOT on the checkout page for the updated verison of an owned item... + // (i.e. they are checking out an item "normally") } else { if (root.alreadyOwned) { viewInMyPurchasesButton.visible = true; - } else { - buyText.text = ""; - } - - if (root.itemType === "contentSet" && !Entities.canReplaceContent()) { - buyText.text = "The domain owner must enable 'Replace Content' permissions for you in this " + - "domain's server settings before you can replace this domain's content with " + root.itemName + ""; - buyTextContainer.color = "#FFC3CD"; - buyTextContainer.border.color = "#F3808F"; - buyGlyph.text = hifi.glyphs.alert; - buyGlyph.size = 54; } + + handleBuyAgainLogic(); } } else { buyText.text = ""; @@ -1060,13 +1160,15 @@ Rectangle { function authSuccessStep() { if (!root.debugCheckoutSuccess) { root.activeView = "checkoutMain"; + root.ownershipStatusReceived = false; + Commerce.alreadyOwned(root.itemId); + root.availableUpdatesReceived = false; + Commerce.getAvailableUpdates(root.itemId); + root.balanceReceived = false; + Commerce.balance(); } else { root.activeView = "checkoutSuccess"; } - root.balanceReceived = false; - root.ownershipStatusReceived = false; - Commerce.alreadyOwned(root.itemId); - Commerce.balance(); } // diff --git a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml index 8a7e809b3d..8105688131 100644 --- a/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml +++ b/interface/resources/qml/hifi/commerce/common/EmulatedMarketplaceHeader.qml @@ -28,6 +28,7 @@ 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,6 +39,7 @@ 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); @@ -58,6 +60,14 @@ 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: { @@ -134,13 +144,25 @@ Item { anchors.fill: parent; hoverEnabled: enabled; onClicked: { - sendToParent({method: 'header_goToPurchases'}); + 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/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index fb8e509cde..4cfa61c9ed 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -48,11 +48,14 @@ Item { property bool hasPermissionToRezThis; property bool permissionExplanationCardVisible; property bool isInstalled; + property string upgradeUrl; + property string upgradeTitle; + property bool isShowingMyItems; property string originalStatusText; property string originalStatusColor; - height: 110; + height: (root.upgradeUrl === "" || root.isShowingMyItems) ? 110 : 150; width: parent.width; Connections { @@ -137,6 +140,14 @@ Item { anchors.verticalCenter: parent.verticalCenter; height: root.height - 10; + // START "incorrect indentation to prevent insane diffs" + Item { + id: itemContainer; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: parent.top; + height: 100; + Image { id: itemPreviewImage; source: root.itemPreviewImageUrl; @@ -357,7 +368,7 @@ Item { Item { id: statusContainer; - visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated" || root.purchaseStatusChanged; + visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated" || root.purchaseStatusChanged || root.numberSold > -1; anchors.left: itemName.left; anchors.top: certificateContainer.bottom; anchors.topMargin: 8; @@ -376,7 +387,7 @@ Item { "PENDING..." } else if (root.purchaseStatus === "invalidated") { "INVALIDATED" - } else if (root.numberSold !== -1) { + } else if (root.numberSold > -1) { ("Sales: " + root.numberSold + "/" + (root.limitedRun === -1 ? "\u221e" : root.limitedRun)) } else { "" @@ -634,6 +645,48 @@ Item { } } } + } + // END "incorrect indentation to prevent insane diffs" + + Rectangle { + id: upgradeAvailableContainer; + visible: root.upgradeUrl !== "" && !root.isShowingMyItems; + anchors.top: itemContainer.bottom; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + color: "#B5EAFF"; + + RalewayRegular { + id: updateAvailableText; + text: "UPDATE AVAILABLE"; + size: 13; + anchors.left: parent.left; + anchors.leftMargin: 12; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + width: paintedWidth; + color: hifi.colors.black; + verticalAlignment: Text.AlignVCenter; + } + + RalewaySemiBold { + id: updateNowText; + text: "Update this item now"; + size: 13; + anchors.left: updateAvailableText.right; + anchors.leftMargin: 16; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + width: paintedWidth; + color: hifi.colors.black; + verticalAlignment: Text.AlignVCenter; + + onLinkActivated: { + sendToPurchases({method: 'updateItemClicked', itemId: root.itemId, itemEdition: root.itemEdition, upgradeUrl: root.upgradeUrl}); + } + } + } } DropShadow { diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index c505baebf4..726e6bd338 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -37,6 +37,8 @@ Rectangle { property bool isDebuggingFirstUseTutorial: false; property int pendingItemCount: 0; property string installedApps; + property bool keyboardRaised: false; + property int numUpdatesAvailable: 0; // Style color: hifi.colors.white; Connections { @@ -64,6 +66,7 @@ Rectangle { root.activeView = "purchasesMain"; root.installedApps = Commerce.getInstalledApps(); Commerce.inventory(); + Commerce.getAvailableUpdates(); } } else { console.log("ERROR in Purchases.qml: Unknown wallet status: " + walletStatus); @@ -119,6 +122,15 @@ Rectangle { root.pendingInventoryReply = false; } + + onAvailableUpdatesResult: { + 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.data.updates.length; + } + } } Timer { @@ -273,6 +285,7 @@ Rectangle { root.activeView = "purchasesMain"; root.installedApps = Commerce.getInstalledApps(); Commerce.inventory(); + Commerce.getAvailableUpdates(); break; } } @@ -296,6 +309,7 @@ Rectangle { // FILTER BAR START // Item { + z: 997; id: filterBarContainer; // Size height: 40; @@ -321,28 +335,61 @@ Rectangle { size: 22; } - HifiControlsUit.TextField { + HifiControlsUit.FilterBar { id: filterBar; property string previousText: ""; + property string previousPrimaryFilter: ""; colorScheme: hifi.colorSchemes.faintGray; - hasClearButton: true; - hasRoundedBorder: true; + anchors.top: parent.top; + anchors.right: parent.right; anchors.left: myText.right; anchors.leftMargin: 16; - height: 39; - anchors.verticalCenter: parent.verticalCenter; - anchors.right: parent.right; + textFieldHeight: 39; + height: textFieldHeight + dropdownHeight; placeholderText: "filter items"; + Component.onCompleted: { + var choices = [ + { + "displayName": "App", + "filterName": "app" + }, + { + "displayName": "Avatar", + "filterName": "avatar" + }, + { + "displayName": "Content Set", + "filterName": "contentSet" + }, + { + "displayName": "Entity", + "filterName": "entity" + }, + { + "displayName": "Wearable", + "filterName": "wearable" + }, + { + "displayName": "Updatable", + "filterName": "updatable" + } + ] + filterBar.primaryFilterChoices.clear(); + filterBar.primaryFilterChoices.append(choices); + } + + onPrimaryFilter_displayNameChanged: { + buildFilteredPurchasesModel(); + purchasesContentsList.positionViewAtIndex(0, ListView.Beginning) + filterBar.previousPrimaryFilter = filterBar.primaryFilter_displayName; + } + onTextChanged: { buildFilteredPurchasesModel(); purchasesContentsList.positionViewAtIndex(0, ListView.Beginning) filterBar.previousText = filterBar.text; } - - onAccepted: { - focus = false; - } } } // @@ -350,6 +397,7 @@ Rectangle { // HifiControlsUit.Separator { + z: 996; id: separator; colorScheme: 2; anchors.left: parent.left; @@ -377,12 +425,11 @@ Rectangle { clip: true; model: filteredPurchasesModel; snapMode: ListView.SnapToItem; - highlightRangeMode: ListView.StrictlyEnforceRange; // Anchors anchors.top: separator.bottom; anchors.topMargin: 12; anchors.left: parent.left; - anchors.bottom: parent.bottom; + anchors.bottom: updatesAvailableBanner.visible ? updatesAvailableBanner.top : parent.bottom; width: parent.width; delegate: PurchasedItem { itemName: title; @@ -398,21 +445,10 @@ Rectangle { displayedItemCount: model.displayedItemCount; permissionExplanationCardVisible: model.permissionExplanationCardVisible; isInstalled: model.isInstalled; - itemType: { - if (model.root_file_url.indexOf(".fst") > -1) { - "avatar"; - } else if (model.categories.indexOf("Wearables") > -1) { - "wearable"; - } else if (model.root_file_url.endsWith('.json.gz')) { - "contentSet"; - } else if (model.root_file_url.endsWith('.app.json')) { - "app"; - } else if (model.root_file_url.endsWith('.json')) { - "entity"; - } else { - "unknown"; - } - } + upgradeUrl: model.upgrade_url; + upgradeTitle: model.upgrade_title; + itemType: model.itemType; + isShowingMyItems: root.isShowingMyItems; anchors.topMargin: 10; anchors.bottomMargin: 10; @@ -485,15 +521,80 @@ Rectangle { filteredPurchasesModel.setProperty(i, "permissionExplanationCardVisible", true); } } + } else if (msg.method === "updateItemClicked") { + sendToScript(msg); } } } } } + Rectangle { + id: updatesAvailableBanner; + visible: root.numUpdatesAvailable > 0 && !root.isShowingMyItems; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 75; + color: "#B5EAFF"; + + Rectangle { + id: updatesAvailableGlyph; + anchors.verticalCenter: parent.verticalCenter; + anchors.left: parent.left; + anchors.leftMargin: 16; + // Size + width: 10; + height: width; + radius: width/2; + // Style + color: "red"; + } + + RalewaySemiBold { + text: "You have " + root.numUpdatesAvailable + " item updates available."; + // Text size + size: 18; + // Anchors + anchors.left: updatesAvailableGlyph.right; + anchors.leftMargin: 12; + height: parent.height; + width: paintedWidth; + // Style + color: hifi.colors.black; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + propagateComposedEvents: false; + } + + HifiControlsUit.Button { + color: hifi.buttons.white; + colorScheme: hifi.colorSchemes.dark; + anchors.verticalCenter: parent.verticalCenter; + anchors.right: parent.right; + anchors.rightMargin: 12; + width: 100; + height: 40; + text: "SHOW ME"; + onClicked: { + filterBar.text = ""; + filterBar.changeFilterByDisplayName("Updatable"); + } + } + } + Item { id: noItemsAlertContainer; - visible: !purchasesContentsList.visible && root.purchasesReceived && root.isShowingMyItems && filterBar.text === ""; + visible: !purchasesContentsList.visible && + root.purchasesReceived && + root.isShowingMyItems && + filterBar.text === "" && + filterBar.primaryFilter_displayName === ""; anchors.top: filterBarContainer.bottom; anchors.topMargin: 12; anchors.left: parent.left; @@ -539,7 +640,11 @@ Rectangle { Item { id: noPurchasesAlertContainer; - visible: !purchasesContentsList.visible && root.purchasesReceived && !root.isShowingMyItems && filterBar.text === ""; + visible: !purchasesContentsList.visible && + root.purchasesReceived && + !root.isShowingMyItems && + filterBar.text === "" && + filterBar.primaryFilter_displayName === ""; anchors.top: filterBarContainer.bottom; anchors.topMargin: 12; anchors.left: parent.left; @@ -589,7 +694,7 @@ Rectangle { HifiControlsUit.Keyboard { id: keyboard; - raised: HMD.mounted && filterBar.focus; + raised: HMD.mounted && parent.keyboardRaised; numeric: parent.punctuationMode; anchors { bottom: parent.bottom; @@ -613,6 +718,7 @@ Rectangle { console.log("Refreshing Purchases..."); root.pendingInventoryReply = true; Commerce.inventory(); + Commerce.getAvailableUpdates(); } } } @@ -660,8 +766,13 @@ Rectangle { var sameItemCount = 0; tempPurchasesModel.clear(); + for (var i = 0; i < purchasesModel.count; i++) { if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) { + if (!purchasesModel.get(i).valid) { + continue; + } + if (purchasesModel.get(i).status !== "confirmed" && !root.isShowingMyItems) { tempPurchasesModel.insert(0, purchasesModel.get(i)); } else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === "0") || @@ -671,6 +782,35 @@ Rectangle { } } + // primaryFilter filtering and adding of itemType property to model + var currentItemType, currentRootFileUrl, currentCategories; + for (var i = 0; i < tempPurchasesModel.count; i++) { + currentRootFileUrl = tempPurchasesModel.get(i).root_file_url; + currentCategories = tempPurchasesModel.get(i).categories; + + if (currentRootFileUrl.indexOf(".fst") > -1) { + currentItemType = "avatar"; + } else if (currentCategories.indexOf("Wearables") > -1) { + currentItemType = "wearable"; + } else if (currentRootFileUrl.endsWith('.json.gz')) { + currentItemType = "contentSet"; + } else if (currentRootFileUrl.endsWith('.app.json')) { + currentItemType = "app"; + } else if (currentRootFileUrl.endsWith('.json')) { + currentItemType = "entity"; + } else { + currentItemType = "unknown"; + } + if (filterBar.primaryFilter_displayName !== "" && + ((filterBar.primaryFilter_displayName === "Updatable" && tempPurchasesModel.get(i).upgrade_url === "") || + (filterBar.primaryFilter_displayName !== "Updatable" && filterBar.primaryFilter_filterName.toLowerCase() !== currentItemType.toLowerCase()))) { + tempPurchasesModel.remove(i); + i--; + } else { + tempPurchasesModel.setProperty(i, 'itemType', currentItemType); + } + } + for (var i = 0; i < tempPurchasesModel.count; i++) { if (!filteredPurchasesModel.get(i)) { sameItemCount = -1; @@ -682,12 +822,17 @@ Rectangle { } } - if (sameItemCount !== tempPurchasesModel.count || filterBar.text !== filterBar.previousText) { + if (sameItemCount !== tempPurchasesModel.count || + filterBar.text !== filterBar.previousText || + filterBar.primaryFilter !== filterBar.previousPrimaryFilter) { filteredPurchasesModel.clear(); var currentId; for (var i = 0; i < tempPurchasesModel.count; i++) { currentId = tempPurchasesModel.get(i).id; - + + if (!purchasesModel.get(i).valid) { + continue; + } filteredPurchasesModel.append(tempPurchasesModel.get(i)); filteredPurchasesModel.setProperty(i, 'permissionExplanationCardVisible', false); filteredPurchasesModel.setProperty(i, 'isInstalled', ((root.installedApps).indexOf(currentId) > -1)); @@ -736,7 +881,7 @@ Rectangle { function fromScript(message) { switch (message.method) { case 'updatePurchases': - referrerURL = message.referrerURL; + referrerURL = message.referrerURL || ""; titleBarContainer.referrerURL = message.referrerURL; filterBar.text = message.filterText ? message.filterText : ""; break; diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 27660b5e9e..7a14ee060f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -39,6 +39,7 @@ Item { root.noMoreHistoryData = false; root.historyRequestPending = true; Commerce.history(root.currentHistoryPage); + Commerce.getAvailableUpdates(); } else { refreshTimer.stop(); } @@ -133,6 +134,14 @@ Item { refreshTimer.start(); } } + + onAvailableUpdatesResult: { + if (result.status !== 'success') { + console.log("Failed to get Available Updates", result.data.message); + } else { + sendToScript({method: 'wallet_availableUpdatesReceived', numUpdates: result.data.updates.length }); + } + } } Connections { diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index 00273171df..9e3ebcbab0 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -25,6 +25,7 @@ ScrollingWindow { resizable: true destroyOnHidden: false implicitWidth: 424 + opacity: parent.opacity implicitHeight: isHMD ? 695 : 728 minSize: Qt.vector2d(424, 300) diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index a85e5d4498..138eb5c6f8 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -58,7 +58,7 @@ Rectangle { Component.onDestruction: { assetMappingsModel.autoRefreshEnabled = false; } - + function letterbox(headerGlyph, headerText, message) { letterboxMessage.headerGlyph = headerGlyph; letterboxMessage.headerText = headerText; @@ -66,7 +66,7 @@ Rectangle { letterboxMessage.visible = true; letterboxMessage.popupRadius = 0; } - + function errorMessageBox(message) { return tabletRoot.messageBox({ icon: hifi.icons.warning, @@ -145,7 +145,7 @@ Rectangle { function canAddToWorld(path) { var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i]; - + if (selectedItemCount > 1) { return false; } @@ -154,8 +154,8 @@ Rectangle { return total | new RegExp(current).test(path); }, false); } - - function canRename() { + + function canRename() { if (treeView.selection.hasSelection && selectedItemCount == 1) { return true; } else { @@ -199,7 +199,7 @@ Rectangle { var SHAPE_TYPE_STATIC_MESH = 3; var SHAPE_TYPE_BOX = 4; var SHAPE_TYPE_SPHERE = 5; - + var SHAPE_TYPES = []; SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; @@ -207,7 +207,7 @@ Rectangle { SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_SIMPLE_COMPOUND; var DYNAMIC_DEFAULT = false; var prompt = tabletRoot.customInputDialog({ @@ -349,14 +349,14 @@ Rectangle { } function deleteFile(index) { var paths = []; - + if (!index) { for (var i = 0; i < selectedItemCount; ++i) { index = treeView.selection.selectedIndexes[i]; paths[i] = assetProxyModel.data(index, 0x100); } } - + if (!paths) { return; } @@ -365,7 +365,7 @@ Rectangle { var items = selectedItemCount.toString(); var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101); var typeString = isFolder ? 'folder' : 'file'; - + if (selectedItemCount > 1) { modalMessage = "You are about to delete " + items + " items \nDo you want to continue?"; } else { @@ -476,7 +476,7 @@ Rectangle { }); } } - + // The letterbox used for popup messages LetterboxMessage { id: letterboxMessage; @@ -540,7 +540,7 @@ Rectangle { anchors.margins: hifi.dimensions.contentMargin.x + 2 // Extra for border anchors.left: parent.left anchors.right: parent.right - + treeModel: assetProxyModel selectionMode: SelectionMode.ExtendedSelection headerVisible: true @@ -560,9 +560,13 @@ Rectangle { id: bakedColumn title: "Use Baked?" role: "baked" - width: 100 + width: 170 } - + + onSortIndicatorOrderChanged: { + Assets.sortProxyModel(sortIndicatorColumn, sortIndicatorOrder); + } + itemDelegate: Loader { id: itemDelegateLoader @@ -598,7 +602,7 @@ Rectangle { } sourceComponent: getComponent() - + Component { id: labelComponent FiraSansSemiBold { @@ -607,15 +611,15 @@ Rectangle { color: colorScheme == hifi.colorSchemes.light ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - + horizontalAlignment: styleData.column === 1 ? TextInput.AlignHCenter : TextInput.AlignLeft - + elide: Text.ElideMiddle MouseArea { id: mouseArea anchors.fill: parent - + acceptedButtons: Qt.NoButton hoverEnabled: true @@ -637,7 +641,7 @@ Rectangle { color: colorScheme == hifi.colorSchemes.light ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - + elide: Text.ElideRight horizontalAlignment: TextInput.AlignHCenter @@ -724,7 +728,7 @@ Rectangle { size: hifi.fontSizes.tableText color: colorScheme == hifi.colorSchemes.light ? hifi.colors.black : hifi.colors.lightGrayText } - + Timer { id: showTimer interval: 1000 @@ -743,7 +747,7 @@ Rectangle { treeLabelToolTip.visible = false; } }// End_OF( treeLabelToolTip ) - + MouseArea { propagateComposedEvents: true anchors.fill: parent @@ -801,7 +805,7 @@ Rectangle { anchors.left: treeView.left anchors.right: treeView.right anchors.bottomMargin: hifi.dimensions.contentSpacing.y - + RalewayRegular { anchors.verticalCenter: parent.verticalCenter @@ -845,7 +849,7 @@ Rectangle { checked = Qt.binding(isChecked); } - + function isEnabled() { if (!treeView.selection.hasSelection) { return false; @@ -869,7 +873,7 @@ Rectangle { } } - return true; + return true; } function isChecked() { if (!treeView.selection.hasSelection) { @@ -877,10 +881,10 @@ Rectangle { } var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105); - return isEnabled() && status !== "Not Baked"; - } + return isEnabled() && status !== "Not Baked"; + } } - + Item { anchors.verticalCenter: parent.verticalCenter width: infoGlyph.size; @@ -904,7 +908,7 @@ Rectangle { "What is baking?", "Baking compresses and optimizes files for faster network transfer and display. We recommend you bake your content to reduce initial load times for your visitors."); } - } + } }// End_OF( infoRow ) HifiControls.TabletContentSection { diff --git a/interface/resources/qml/hifi/overlays/TextOverlay.qml b/interface/resources/qml/hifi/overlays/TextOverlay.qml index 301a2aa0bf..2fdc9c7cbb 100644 --- a/interface/resources/qml/hifi/overlays/TextOverlay.qml +++ b/interface/resources/qml/hifi/overlays/TextOverlay.qml @@ -20,6 +20,7 @@ Overlay { font.family: "Helvetica" font.pixelSize: 18 lineHeight: 18 + clip: true } } diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml index 3debc8b9e7..5216bf45d5 100644 --- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml @@ -71,6 +71,14 @@ Rectangle { onAccepted: { newModelDialog.keyboardEnabled = false; } + + onTextChanged : { + if (modelURL.text.length === 0){ + button1.enabled = false; + } else { + button1.enabled = true; + } + } MouseArea { anchors.fill: parent @@ -200,6 +208,7 @@ Rectangle { id: button1 text: qsTr("Add") z: -1 + enabled: false onClicked: { newModelDialog.sendToScript({ method: "newModelDialogAdd", diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index d4550d3843..4b9e1af4f1 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -113,7 +113,6 @@ StackView { id: addressBarDialog property bool keyboardEnabled: false - property bool keyboardRaised: false property bool punctuationMode: false width: parent.width @@ -401,11 +400,10 @@ StackView { addressLine.text = ""; } } - HifiControls.Keyboard { id: keyboard - raised: parent.keyboardEnabled && parent.keyboardRaised + raised: parent.keyboardEnabled numeric: parent.punctuationMode anchors { bottom: parent.bottom @@ -413,7 +411,7 @@ StackView { right: parent.right } } - + } function updateLocationText(enteringAddress) { diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index afa210ea8b..9705d84b7b 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -11,44 +11,34 @@ import QtQuick 2.5 import QtQuick.Window 2.2 -Item { - readonly property alias colors: colors - readonly property alias colorSchemes: colorSchemes - readonly property alias dimensions: dimensions - readonly property alias fontSizes: fontSizes - readonly property alias glyphs: glyphs - readonly property alias icons: icons - readonly property alias buttons: buttons - readonly property alias effects: effects +QtObject { function glyphForIcon(icon) { // Translates icon enum to glyph char. var glyph; switch (icon) { - case hifi.icons.information: - glyph = hifi.glyphs.info; + case icons.information: + glyph = glyphs.info; break; - case hifi.icons.question: - glyph = hifi.glyphs.question; + case icons.question: + glyph = glyphs.question; break; - case hifi.icons.warning: - glyph = hifi.glyphs.alert; + case icons.warning: + glyph = glyphs.alert; break; - case hifi.icons.critical: - glyph = hifi.glyphs.error; + case icons.critical: + glyph = glyphs.error; break; - case hifi.icons.placemark: - glyph = hifi.glyphs.placemark; + case icons.placemark: + glyph = glyphs.placemark; break; default: - glyph = hifi.glyphs.noIcon; + glyph = glyphs.noIcon; } return glyph; } - Item { - id: colors - + readonly property QtObject colors: QtObject { // Base colors readonly property color baseGray: "#393939" readonly property color darkGray: "#121212" @@ -134,15 +124,13 @@ Item { readonly property color tabBackgroundLight: "#d4d4d4" } - Item { - id: colorSchemes + readonly property QtObject colorSchemes: QtObject { readonly property int light: 0 readonly property int dark: 1 readonly property int faintGray: 2 } - Item { - id: dimensions + readonly property QtObject dimensions: QtObject { readonly property bool largeScreen: Screen.width >= 1920 && Screen.height >= 1080 readonly property real borderRadius: largeScreen ? 7.5 : 5.0 readonly property real borderWidth: largeScreen ? 2 : 1 @@ -168,8 +156,8 @@ Item { readonly property real buttonWidth: 120 } - Item { - id: fontSizes // In pixels + readonly property QtObject fontSizes: QtObject { + // In pixels readonly property real overlayTitle: dimensions.largeScreen ? 18 : 14 readonly property real tabName: dimensions.largeScreen ? 12 : 10 readonly property real sectionName: dimensions.largeScreen ? 12 : 10 @@ -194,8 +182,7 @@ Item { readonly property real disclosureButton: dimensions.largeScreen ? 30 : 22 } - Item { - id: icons + readonly property QtObject icons: QtObject { // Values per OffscreenUi::Icon readonly property int none: 0 readonly property int question: 1 @@ -205,8 +192,7 @@ Item { readonly property int placemark: 5 } - Item { - id: buttons + readonly property QtObject buttons: QtObject { readonly property int white: 0 readonly property int blue: 1 readonly property int red: 2 @@ -227,12 +213,11 @@ Item { readonly property int radius: 5 } - QtObject { - id: effects + readonly property QtObject effects: QtObject { readonly property int fadeInDuration: 300 } - Item { - id: glyphs + + readonly property QtObject glyphs: QtObject { readonly property string noIcon: "" readonly property string hmd: "b" readonly property string screen: "c" diff --git a/interface/resources/serverless/tutorial.json b/interface/resources/serverless/tutorial.json new file mode 100644 index 0000000000..f690de6643 --- /dev/null +++ b/interface/resources/serverless/tutorial.json @@ -0,0 +1,18 @@ +{ + "Entities": [ + { + "type": "Box", + "dimensions": { + "x": 20, + "y": 1, + "z": 20 + }, + "position" : { + "x": 0, + "y": -12, + "z": 0 + } + } + ], + "Version": 84 +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 90a3d868d5..4e01cb787c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -351,8 +351,9 @@ static const QString OBJ_EXTENSION = ".obj"; static const QString AVA_JSON_EXTENSION = ".ava.json"; static const QString WEB_VIEW_TAG = "noDownload=true"; static const QString ZIP_EXTENSION = ".zip"; +static const QString CONTENT_ZIP_EXTENSION = ".content.zip"; -static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; +static const float MIRROR_FULLSCREEN_DISTANCE = 0.789f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; @@ -376,9 +377,7 @@ static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; -static const QString DOMAIN_SPAWNING_POINT = "/0, -10, 0"; - -const QHash Application::_acceptedExtensions { +const std::vector> Application::_acceptedExtensions { { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }, @@ -386,6 +385,7 @@ const QHash Application::_acceptedExtensi { JS_EXTENSION, &Application::askToLoadScript }, { FST_EXTENSION, &Application::askToSetAvatarUrl }, { JSON_GZ_EXTENSION, &Application::askToReplaceDomainContent }, + { CONTENT_ZIP_EXTENSION, &Application::askToReplaceDomainContent }, { ZIP_EXTENSION, &Application::importFromZIP }, { JPG_EXTENSION, &Application::importImage }, { PNG_EXTENSION, &Application::importImage } @@ -511,6 +511,27 @@ std::atomic DeadlockWatchdogThread::_maxElapsed; std::atomic DeadlockWatchdogThread::_maxElapsedAverage; ThreadSafeMovingAverage DeadlockWatchdogThread::_movingAverage; +bool isDomainURL(QUrl url) { + if (!url.isValid()) { + return false; + } + if (url.scheme() == URL_SCHEME_HIFI) { + return true; + } + if (url.scheme() != 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 + return false; + } + if (url.path().endsWith(".json", Qt::CaseInsensitive) || + url.path().endsWith(".json.gz", Qt::CaseInsensitive)) { + return true; + } + return false; +} + #ifdef Q_OS_WIN class MyNativeEventFilter : public QAbstractNativeEventFilter { public: @@ -540,7 +561,7 @@ public: if (message->message == WM_COPYDATA) { COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)(message->lParam); QUrl url = QUrl((const char*)(pcds->lpData)); - if (url.isValid() && url.scheme() == HIFI_URL_SCHEME) { + if (isDomainURL(url)) { DependencyManager::get()->handleLookupString(url.toString()); return true; } @@ -920,7 +941,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true), _preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME), _scaleMirror(1.0f), - _rotateMirror(0.0f), + _mirrorYawOffset(0.0f), _raiseMirror(0.0f), _enableProcessOctreeThread(true), _lastNackTime(usecTimestampNow()), @@ -1033,7 +1054,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // setup a timer for domain-server check ins QTimer* domainCheckInTimer = new QTimer(this); - connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn); + connect(domainCheckInTimer, &QTimer::timeout, [this, nodeList] { + if (!isServerlessMode()) { + nodeList->sendDomainServerCheckIn(); + } + }); domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); connect(this, &QCoreApplication::aboutToQuit, [domainCheckInTimer] { domainCheckInTimer->stop(); @@ -1095,9 +1120,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo const DomainHandler& domainHandler = nodeList->getDomainHandler(); - connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); + connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainURLChanged(QUrl))); connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain())); - connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle())); + connect(&domainHandler, SIGNAL(connectedToDomain(QUrl)), SLOT(updateWindowTitle())); connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &Application::clearDomainAvatars); connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this]() { @@ -2044,7 +2069,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&_addAssetToWorldErrorTimer, &QTimer::timeout, this, &Application::addAssetToWorldErrorTimeout); connect(this, &QCoreApplication::aboutToQuit, this, &Application::addAssetToWorldMessageClose); - connect(&domainHandler, &DomainHandler::hostnameChanged, this, &Application::addAssetToWorldMessageClose); + connect(&domainHandler, &DomainHandler::domainURLChanged, this, &Application::addAssetToWorldMessageClose); updateSystemTabletMode(); @@ -2709,6 +2734,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); + surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); surfaceContext->setContextProperty("Selection", DependencyManager::get().data()); surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get().data()); surfaceContext->setContextProperty("Wallet", DependencyManager::get().data()); @@ -2722,10 +2748,12 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { void Application::onDesktopRootItemCreated(QQuickItem* rootItem) { Stats::show(); - AvatarInputs::show(); auto surfaceContext = DependencyManager::get()->getSurfaceContext(); surfaceContext->setContextProperty("Stats", Stats::getInstance()); - surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); + + auto offscreenUi = DependencyManager::get(); + auto qml = PathUtils::qmlUrl("AvatarInputsBar.qml"); + offscreenUi->show(qml, "AvatarInputsBar"); } void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { @@ -2788,8 +2816,9 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _thirdPersonHMDCameraBoomValid= false; + if (isHMDMode()) { - auto mirrorBodyOrientation = myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); + auto mirrorBodyOrientation = myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _mirrorYawOffset, 0.0f)); glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); // Mirror HMD yaw and roll @@ -2812,12 +2841,15 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { + mirrorBodyOrientation * hmdOffset); } else { - _myCamera.setOrientation(myAvatar->getWorldOrientation() - * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); + auto userInputMapper = DependencyManager::get(); + const float YAW_SPEED = TWO_PI / 5.0f; + float deltaYaw = userInputMapper->getActionState(controller::Action::YAW) * YAW_SPEED * deltaTime; + _mirrorYawOffset += deltaYaw; + _myCamera.setOrientation(myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _mirrorYawOffset, 0.0f))); _myCamera.setPosition(myAvatar->getDefaultEyePosition() + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0) - + (myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * - glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); + + (myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, _mirrorYawOffset, 0.0f))) * + glm::vec3(0.0f, 0.0f, -1.0f) * myAvatar->getBoomLength() * _scaleMirror); } renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; } @@ -3023,27 +3055,27 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { QString sentTo; - // If this is a first run we short-circuit the address passed in - if (firstRun.get()) { + // If this is a first run we short-circuit the address passed in + if (firstRun.get()) { #if !defined(Q_OS_ANDROID) - showHelp(); -#endif - if (sandboxIsRunning) { - qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; - DependencyManager::get()->goToLocalSandbox(); - sentTo = SENT_TO_SANDBOX; - } else { - qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; - DependencyManager::get()->goToEntry(); - sentTo = SENT_TO_ENTRY; - } - firstRun.set(false); - + showHelp(); +#endif + if (sandboxIsRunning) { + qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; + DependencyManager::get()->goToLocalSandbox(); + sentTo = SENT_TO_SANDBOX; } else { - qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); - DependencyManager::get()->loadSettings(addressLookupString); - sentTo = SENT_TO_PREVIOUS_LOCATION; + qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; + DependencyManager::get()->goToEntry(); + sentTo = SENT_TO_ENTRY; } + firstRun.set(false); + + } else { + qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); + DependencyManager::get()->loadSettings(addressLookupString); + sentTo = SENT_TO_PREVIOUS_LOCATION; + } UserActivityLogger::getInstance().logAction("startup_sent_to", { { "sent_to", sentTo }, @@ -3083,6 +3115,57 @@ bool Application::importFromZIP(const QString& filePath) { return true; } +bool Application::isServerlessMode() const { + auto tree = getEntities()->getTree(); + if (tree) { + return tree->isServerlessMode(); + } + return false; +} + +void Application::setIsServerlessMode(bool serverlessDomain) { + auto tree = getEntities()->getTree(); + if (tree) { + tree->setIsServerlessMode(serverlessDomain); + } +} + +void Application::loadServerlessDomain(QUrl domainURL) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "loadServerlessDomain", Q_ARG(QUrl, domainURL)); + return; + } + + if (domainURL.isEmpty()) { + return; + } + + QUuid serverlessSessionID = QUuid::createUuid(); + getMyAvatar()->setSessionUUID(serverlessSessionID); + auto nodeList = DependencyManager::get(); + nodeList->setSessionUUID(serverlessSessionID); + + // there is no domain-server to tell us our permissions, so enable all + NodePermissions permissions; + permissions.setAll(true); + nodeList->setPermissions(permissions); + + // we can't import directly into the main tree because we would need to lock it, and + // Octree::readFromURL calls loop.exec which can run code which will also attempt to lock the tree. + EntityTreePointer tmpTree(new EntityTree()); + tmpTree->setIsServerlessMode(true); + tmpTree->createRootElement(); + auto myAvatar = getMyAvatar(); + tmpTree->setMyAvatar(myAvatar); + bool success = tmpTree->readFromURL(domainURL.toString()); + if (success) { + tmpTree->reaverageOctreeElements(); + tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), 0, 0, 0); + } + + _fullSceneReceivedCounter++; +} + bool Application::importImage(const QString& urlString) { qCDebug(interfaceapp) << "An image file has been dropped in"; QString filepath(urlString); @@ -3324,8 +3407,6 @@ void Application::keyPressEvent(QKeyEvent* event) { } else { setFullscreen(nullptr); } - } else { - Menu::getInstance()->triggerOption(MenuOption::AddressBar); } break; @@ -3387,13 +3468,6 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; - case Qt::Key_F: { - if (isOption) { - _physicsEngine->dumpNextStats(); - } - break; - } - case Qt::Key_Asterisk: Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox); break; @@ -3413,22 +3487,25 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_S: if (isShifted && isMeta && !isOption) { Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings); - } else if (!isOption && !isShifted && isMeta) { - AudioInjectorOptions options; - options.localOnly = true; - options.stereo = true; - - if (_snapshotSoundInjector) { - _snapshotSoundInjector->setOptions(options); - _snapshotSoundInjector->restart(); - } else { - QByteArray samples = _snapshotSound->getByteArray(); - _snapshotSoundInjector = AudioInjector::playSound(samples, options); - } - takeSnapshot(true); } break; + case Qt::Key_P: { + AudioInjectorOptions options; + options.localOnly = true; + options.stereo = true; + + if (_snapshotSoundInjector) { + _snapshotSoundInjector->setOptions(options); + _snapshotSoundInjector->restart(); + } else { + QByteArray samples = _snapshotSound->getByteArray(); + _snapshotSoundInjector = AudioInjector::playSound(samples, options); + } + takeSnapshot(true); + break; + } + case Qt::Key_Apostrophe: { if (isMeta) { auto cursor = Cursor::Manager::instance().getCursor(); @@ -3452,38 +3529,6 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::Chat); break; - case Qt::Key_Up: - if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - if (!isShifted) { - _scaleMirror *= 0.95f; - } else { - _raiseMirror += 0.05f; - } - } - break; - - case Qt::Key_Down: - if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - if (!isShifted) { - _scaleMirror *= 1.05f; - } else { - _raiseMirror -= 0.05f; - } - } - break; - - case Qt::Key_Left: - if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - _rotateMirror += PI / 20.0f; - } - break; - - case Qt::Key_Right: - if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - _rotateMirror -= PI / 20.0f; - } - break; - #if 0 case Qt::Key_I: if (isShifted) { @@ -4587,7 +4632,7 @@ void Application::initDisplay() { } void Application::init() { - + // Make sure Login state is up to date DependencyManager::get()->toggleLoginDialog(); if (!DISABLE_DEFERRED) { @@ -4612,7 +4657,9 @@ void Application::init() { qCDebug(interfaceapp) << "Loaded settings"; // fire off an immediate domain-server check in now that settings are loaded - DependencyManager::get()->sendDomainServerCheckIn(); + if (!isServerlessMode()) { + DependencyManager::get()->sendDomainServerCheckIn(); + } // This allows collision to be set up properly for shape entities supported by GeometryCache. // This is before entity setup to ensure that it's ready for whenever instance collision is initialized. @@ -4855,8 +4902,10 @@ void Application::cameraMenuChanged() { auto menu = Menu::getInstance(); if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { if (!isHMDMode() && _myCamera.getMode() != CAMERA_MODE_MIRROR) { + _mirrorYawOffset = 0.0f; _myCamera.setMode(CAMERA_MODE_MIRROR); getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers + getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT); } } else if (menu->isOptionChecked(MenuOption::FirstPerson)) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { @@ -5138,7 +5187,7 @@ void Application::update(float deltaTime) { // FIXME can we drop drive keys and just have the avatar read the action states directly? myAvatar->clearDriveKeys(); if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { - if (!_controllerScriptingInterface->areActionsCaptured()) { + if (!_controllerScriptingInterface->areActionsCaptured() && _myCamera.getMode() != CAMERA_MODE_MIRROR) { myAvatar->setDriveKey(MyAvatar::TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z)); myAvatar->setDriveKey(MyAvatar::TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y)); myAvatar->setDriveKey(MyAvatar::TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X)); @@ -5151,6 +5200,7 @@ void Application::update(float deltaTime) { myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } + myAvatar->setSprintMode((bool)userInputMapper->getActionState(controller::Action::SPRINT)); static const std::vector avatarControllerActions = { controller::Action::LEFT_HAND, controller::Action::RIGHT_HAND, @@ -5748,10 +5798,15 @@ void Application::updateWindowTitle() const { QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)"; QString username = accountManager->getAccountInfo().getUsername(); - QString currentPlaceName = DependencyManager::get()->getHost(); - if (currentPlaceName.isEmpty()) { - currentPlaceName = nodeList->getDomainHandler().getHostname(); + QString currentPlaceName; + if (isServerlessMode()) { + currentPlaceName = "serverless: " + DependencyManager::get()->getDomainURL().toString(); + } else { + currentPlaceName = DependencyManager::get()->getDomainURL().host(); + if (currentPlaceName.isEmpty()) { + currentPlaceName = nodeList->getDomainHandler().getHostname(); + } } QString title = QString() + (!username.isEmpty() ? username + " @ " : QString()) @@ -5764,7 +5819,7 @@ void Application::updateWindowTitle() const { _window->setWindowTitle(title); // updateTitleWindow gets called whenever there's a change regarding the domain, so rather - // than placing this within domainChanged, it's placed here to cover the other potential cases. + // than placing this within domainURLChanged, it's placed here to cover the other potential cases. DependencyManager::get< MessagesClient >()->sendLocalMessage("Toolbar-DomainChanged", ""); } @@ -5803,15 +5858,22 @@ void Application::clearDomainAvatars() { DependencyManager::get()->clearOtherAvatars(); } -void Application::domainChanged(const QString& domainHostname) { - updateWindowTitle(); +void Application::domainURLChanged(QUrl domainURL) { // disable physics until we have enough information about our new location to not cause craziness. resetPhysicsReadyInformation(); + setIsServerlessMode(domainURL.scheme() != URL_SCHEME_HIFI); + if (isServerlessMode()) { + loadServerlessDomain(domainURL); + } + updateWindowTitle(); } void Application::resettingDomain() { _notifiedPacketVersionMismatchThisDomain = false; + + auto nodeList = DependencyManager::get(); + clearDomainOctreeDetails(); } void Application::nodeAdded(SharedNodePointer node) const { @@ -5928,22 +5990,22 @@ bool Application::nearbyEntitiesAreReadyForPhysics() { AABox avatarBox(getMyAvatar()->getWorldPosition() - glm::vec3(PHYSICS_READY_RANGE), glm::vec3(2 * PHYSICS_READY_RANGE)); // create two functions that use avatarBox (entityScan and elementScan), the second calls the first std::function entityScan = [=](EntityItemPointer& entity) { - if (entity->shouldBePhysical()) { - bool success = false; - AABox entityBox = entity->getAABox(success); - // important: bail for entities that cannot supply a valid AABox - return success && avatarBox.touches(entityBox); - } - return false; - }; + if (entity->shouldBePhysical()) { + bool success = false; + AABox entityBox = entity->getAABox(success); + // important: bail for entities that cannot supply a valid AABox + return success && avatarBox.touches(entityBox); + } + return false; + }; std::function elementScan = [&](const OctreeElementPointer& element, void* unused) { - if (element->getAACube().touches(avatarBox)) { - EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->getEntities(entityScan, entities); - return true; - } - return false; - }; + if (element->getAACube().touches(avatarBox)) { + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + entityTreeElement->getEntities(entityScan, entities); + return true; + } + return false; + }; entityTree->withReadLock([&] { // Pass the second function to the general-purpose EntityTree::findEntities() @@ -6180,14 +6242,12 @@ bool Application::canAcceptURL(const QString& urlString) const { QUrl url(urlString); if (url.query().contains(WEB_VIEW_TAG)) { return false; - } else if (urlString.startsWith(HIFI_URL_SCHEME)) { + } else if (urlString.startsWith(URL_SCHEME_HIFI)) { return true; } - QHashIterator i(_acceptedExtensions); QString lowerPath = url.path().toLower(); - while (i.hasNext()) { - i.next(); - if (lowerPath.endsWith(i.key(), Qt::CaseInsensitive)) { + for (auto& pair : _acceptedExtensions) { + if (lowerPath.endsWith(pair.first, Qt::CaseInsensitive)) { return true; } } @@ -6195,21 +6255,18 @@ bool Application::canAcceptURL(const QString& urlString) const { } bool Application::acceptURL(const QString& urlString, bool defaultUpload) { - if (urlString.startsWith(HIFI_URL_SCHEME)) { - // this is a hifi URL - have the AddressManager handle it - emit receivedHifiSchemeURL(urlString); + QUrl url(urlString); + if (isDomainURL(url)) { + // this is a URL for a domain, either hifi:// or serverless - have the AddressManager handle it QMetaObject::invokeMethod(DependencyManager::get().data(), "handleLookupString", Qt::AutoConnection, Q_ARG(const QString&, urlString)); return true; } - QUrl url(urlString); - QHashIterator i(_acceptedExtensions); QString lowerPath = url.path().toLower(); - while (i.hasNext()) { - i.next(); - if (lowerPath.endsWith(i.key(), Qt::CaseInsensitive)) { - AcceptURLMethod method = i.value(); + for (auto& pair : _acceptedExtensions) { + if (lowerPath.endsWith(pair.first, Qt::CaseInsensitive)) { + AcceptURLMethod method = pair.second; return (this->*method)(urlString); } } @@ -6396,13 +6453,11 @@ void Application::replaceDomainContent(const QString& url) { QByteArray urlData(url.toUtf8()); auto limitedNodeList = DependencyManager::get(); const auto& domainHandler = limitedNodeList->getDomainHandler(); - limitedNodeList->eachMatchingNode([](const SharedNodePointer& node) { - return node->getType() == NodeType::EntityServer && node->getActiveSocket(); - }, [&urlData, limitedNodeList, &domainHandler](const SharedNodePointer& octreeNode) { - auto octreeFilePacket = NLPacket::create(PacketType::OctreeFileReplacementFromUrl, urlData.size(), true); - octreeFilePacket->write(urlData); - limitedNodeList->sendPacket(std::move(octreeFilePacket), domainHandler.getSockAddr()); - }); + + auto octreeFilePacket = NLPacket::create(PacketType::DomainContentReplacementFromUrl, urlData.size(), true); + octreeFilePacket->write(urlData); + limitedNodeList->sendPacket(std::move(octreeFilePacket), domainHandler.getSockAddr()); + auto addressManager = DependencyManager::get(); addressManager->handleLookupString(DOMAIN_SPAWNING_POINT); QString newHomeAddress = addressManager->getHost() + DOMAIN_SPAWNING_POINT; @@ -7046,7 +7101,7 @@ void Application::packageModel() { void Application::openUrl(const QUrl& url) const { if (!url.isEmpty()) { - if (url.scheme() == HIFI_URL_SCHEME) { + if (url.scheme() == URL_SCHEME_HIFI) { DependencyManager::get()->handleLookupString(url.toString()); } else { // address manager did not handle - ask QDesktopServices to handle @@ -7360,10 +7415,35 @@ bool Application::isThrottleRendering() const { } bool Application::hasFocus() const { - if (_displayPlugin) { - return getActiveDisplayPlugin()->hasFocus(); + bool result = (QApplication::activeWindow() != nullptr); +#if defined(Q_OS_WIN) + // On Windows, QWidget::activateWindow() - as called in setFocus() - makes the application's taskbar icon flash but doesn't + // take user focus away from their current window. So also check whether the application is the user's current foreground + // window. + result = result && (HWND)QApplication::activeWindow()->winId() == GetForegroundWindow(); +#endif + return result; +} + +void Application::setFocus() { + // Note: Windows doesn't allow a user focus to be taken away from another application. Instead, it changes the color of and + // flashes the taskbar icon. + auto window = qApp->getWindow(); + window->activateWindow(); +} + +void Application::raise() { + auto windowState = qApp->getWindow()->windowState(); + if (windowState & Qt::WindowMinimized) { + if (windowState & Qt::WindowMaximized) { + qApp->getWindow()->showMaximized(); + } else if (windowState & Qt::WindowFullScreen) { + qApp->getWindow()->showFullScreen(); + } else { + qApp->getWindow()->showNormal(); + } } - return (QApplication::activeWindow() != nullptr); + qApp->getWindow()->raise(); } void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) { @@ -7387,7 +7467,7 @@ DisplayPluginPointer Application::getActiveDisplayPlugin() const { return _displayPlugin; } - if (!_displayPlugin) { + if (!_aboutToQuit && !_displayPlugin) { const_cast(this)->updateDisplayMode(); Q_ASSERT(_displayPlugin); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 35ecb91504..f81a51467b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -163,6 +163,8 @@ public: QRect getRecommendedHUDRect() const; glm::vec2 getDeviceSize() const; bool hasFocus() const; + void setFocus(); + void raise(); void showCursor(const Cursor::Icon& cursor); @@ -284,6 +286,8 @@ public: bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; } void saveNextPhysicsStats(QString filename); + bool isServerlessMode() const; + void replaceDomainContent(const QString& url); signals: @@ -295,7 +299,6 @@ signals: void activeDisplayPluginChanged(); void uploadRequest(QString path); - void receivedHifiSchemeURL(const QString& url); public slots: QVector pasteEntities(float x, float y, float z); @@ -391,6 +394,9 @@ public slots: const QString getPreferredCursor() const { return _preferredCursor.get(); } void setPreferredCursor(const QString& cursor); + void setIsServerlessMode(bool serverlessDomain); + void loadServerlessDomain(QUrl domainURL); + Q_INVOKABLE bool askBeforeSetAvatarUrl(const QString& avatarUrl) { return askToSetAvatarUrl(avatarUrl); } private slots: @@ -425,7 +431,7 @@ private slots: void setSessionUUID(const QUuid& sessionUUID) const; - void domainChanged(const QString& domainHostname); + void domainURLChanged(QUrl domainURL); void updateWindowTitle() const; void nodeAdded(SharedNodePointer node) const; void nodeActivated(SharedNodePointer node); @@ -571,7 +577,7 @@ private: Setting::Handle _preferredCursor; float _scaleMirror; - float _rotateMirror; + float _mirrorYawOffset; float _raiseMirror; QSet _keysPressed; @@ -605,7 +611,7 @@ private: GLCanvas* _glWidget{ nullptr }; typedef bool (Application::* AcceptURLMethod)(const QString &); - static const QHash _acceptedExtensions; + static const std::vector> _acceptedExtensions; glm::uvec2 _renderResolution; diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp index 94fdacb5c0..e2d47bf844 100644 --- a/interface/src/DiscoverabilityManager.cpp +++ b/interface/src/DiscoverabilityManager.cpp @@ -49,7 +49,7 @@ void DiscoverabilityManager::updateLocation() { auto accountManager = DependencyManager::get(); auto addressManager = DependencyManager::get(); auto& domainHandler = DependencyManager::get()->getDomainHandler(); - bool discoverable = (_mode.get() != Discoverability::None); + bool discoverable = (_mode.get() != Discoverability::None) && !domainHandler.isServerless(); if (accountManager->isLoggedIn()) { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index fa0e8087f0..6c071defff 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -229,21 +229,21 @@ Menu::Menu() { // View > First Person auto firstPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FirstPerson, Qt::CTRL | Qt::Key_F, + viewMenu, MenuOption::FirstPerson, Qt::Key_1, true, qApp, SLOT(cameraMenuChanged()))); firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); // View > Third Person auto thirdPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::ThirdPerson, Qt::CTRL | Qt::Key_G, + viewMenu, MenuOption::ThirdPerson, Qt::Key_3, false, qApp, SLOT(cameraMenuChanged()))); thirdPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); // View > Mirror auto viewMirrorAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FullscreenMirror, Qt::CTRL | Qt::Key_H, + viewMenu, MenuOption::FullscreenMirror, Qt::Key_2, false, qApp, SLOT(cameraMenuChanged()))); viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9620a2dcec..bd87ec47d8 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2186,7 +2186,6 @@ void MyAvatar::updateActionMotor(float deltaTime) { glm::vec3 direction = forward + right; if (state == CharacterController::State::Hover || _characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { - // we can fly --> support vertical motion glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP; direction += up; } @@ -2204,10 +2203,11 @@ void MyAvatar::updateActionMotor(float deltaTime) { if (state == CharacterController::State::Hover) { // we're flying --> complex acceleration curve that builds on top of current motor speed and caps at some max speed + float motorSpeed = glm::length(_actionMotorVelocity); - float finalMaxMotorSpeed = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_FLYING_SPEED; + float finalMaxMotorSpeed = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_FLYING_SPEED * _walkSpeedScalar; float speedGrowthTimescale = 2.0f; - float speedIncreaseFactor = 1.8f; + float speedIncreaseFactor = 1.8f * _walkSpeedScalar; motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale, 0.0f, 1.0f) * speedIncreaseFactor; const float maxBoostSpeed = getSensorToWorldScale() * MAX_BOOST_SPEED; @@ -2223,7 +2223,7 @@ void MyAvatar::updateActionMotor(float deltaTime) { _actionMotorVelocity = motorSpeed * direction; } else { // we're interacting with a floor --> simple horizontal speed and exponential decay - _actionMotorVelocity = getSensorToWorldScale() * _walkSpeed.get() * direction; + _actionMotorVelocity = getSensorToWorldScale() * (_walkSpeed.get() * _walkSpeedScalar) * direction; } float boomChange = getDriveKey(ZOOM); @@ -2816,7 +2816,11 @@ float MyAvatar::getUserEyeHeight() const { } float MyAvatar::getWalkSpeed() const { - return _walkSpeed.get(); + return _walkSpeed.get() * _walkSpeedScalar; +} + +void MyAvatar::setSprintMode(bool sprint) { + _walkSpeedScalar = sprint ? AVATAR_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR; } void MyAvatar::setWalkSpeed(float value) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 5ca010d128..4830004375 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -395,6 +395,7 @@ public: // Set what driving keys are being pressed to control thrust levels void clearDriveKeys(); void setDriveKey(DriveKeys key, float val); + void setSprintMode(bool sprint); float getDriveKey(DriveKeys key) const; Q_INVOKABLE float getRawDriveKey(DriveKeys key) const; void relayDriveKeysToCharacterController(); @@ -836,7 +837,8 @@ private: std::map _controllerPoseMap; mutable std::mutex _controllerPoseMapMutex; - bool _hmdLeanRecenterEnabled = true; + bool _hmdLeanRecenterEnabled { true }; + bool _sprint { false }; AnimPose _prePhysicsRoomPose; std::mutex _holdActionsMutex; std::vector _holdActions; @@ -866,6 +868,7 @@ private: // max unscaled forward movement speed ThreadSafeValueCache _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED }; + float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR }; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 858af9b13d..0a9e867323 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -52,6 +52,8 @@ Handler(inventory) Handler(transferHfcToNode) Handler(transferHfcToUsername) Handler(alreadyOwned) +Handler(availableUpdates) +Handler(updateItem) void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) { auto accountManager = DependencyManager::get(); @@ -376,3 +378,23 @@ void Ledger::alreadyOwned(const QString& marketplaceId) { qDebug(commerce) << "User attempted to use the alreadyOwned endpoint, but cachedPublicKeys was empty!"; } } + +void Ledger::getAvailableUpdates(const QString& itemId) { + auto wallet = DependencyManager::get(); + QString endpoint = "available_updates"; + QJsonObject request; + request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys()); + if (!itemId.isEmpty()) { + request["marketplace_item_id"] = itemId; + } + send(endpoint, "availableUpdatesSuccess", "availableUpdatesFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request); +} + +void Ledger::updateItem(const QString& hfc_key, const QString& certificate_id) { + QJsonObject transaction; + transaction["public_key"] = hfc_key; + transaction["certificate_id"] = certificate_id; + QJsonDocument transactionDoc{ transaction }; + auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); + signedSend("transaction", transactionString, hfc_key, "update_item", "updateItemSuccess", "updateItemFailure"); +} diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 703ebda2dc..da97206bbc 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -36,6 +36,8 @@ public: void transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage); void transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage); void alreadyOwned(const QString& marketplaceId); + void getAvailableUpdates(const QString& itemId = ""); + void updateItem(const QString& hfc_key, const QString& certificate_id); enum CertificateStatus { CERTIFICATE_STATUS_UNKNOWN = 0, @@ -57,6 +59,8 @@ signals: void transferHfcToNodeResult(QJsonObject result); void transferHfcToUsernameResult(QJsonObject result); void alreadyOwnedResult(QJsonObject result); + void availableUpdatesResult(QJsonObject result); + void updateItemResult(QJsonObject result); void updateCertificateStatus(const QString& certID, uint certStatus); @@ -83,6 +87,10 @@ public slots: void transferHfcToUsernameFailure(QNetworkReply& reply); void alreadyOwnedSuccess(QNetworkReply& reply); void alreadyOwnedFailure(QNetworkReply& reply); + void availableUpdatesSuccess(QNetworkReply& reply); + void availableUpdatesFailure(QNetworkReply& reply); + void updateItemSuccess(QNetworkReply& reply); + void updateItemFailure(QNetworkReply& reply); private: QJsonObject apiResponse(const QString& label, QNetworkReply& reply); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 53ec59049f..8e956249cc 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -38,7 +38,8 @@ QmlCommerce::QmlCommerce() { connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus); connect(ledger.data(), &Ledger::transferHfcToNodeResult, this, &QmlCommerce::transferHfcToNodeResult); connect(ledger.data(), &Ledger::transferHfcToUsernameResult, this, &QmlCommerce::transferHfcToUsernameResult); - connect(ledger.data(), &Ledger::transferHfcToUsernameResult, this, &QmlCommerce::transferHfcToUsernameResult); + connect(ledger.data(), &Ledger::availableUpdatesResult, this, &QmlCommerce::availableUpdatesResult); + connect(ledger.data(), &Ledger::updateItemResult, this, &QmlCommerce::updateItemResult); auto accountManager = DependencyManager::get(); connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { @@ -349,3 +350,20 @@ bool QmlCommerce::openApp(const QString& itemHref) { return true; } + +void QmlCommerce::getAvailableUpdates(const QString& itemId) { + auto ledger = DependencyManager::get(); + ledger->getAvailableUpdates(itemId); +} + +void QmlCommerce::updateItem(const QString& certificateId) { + auto ledger = DependencyManager::get(); + auto wallet = DependencyManager::get(); + QStringList keys = wallet->listPublicKeys(); + if (keys.count() == 0) { + QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } }; + return emit updateItemResult(result); + } + QString key = keys[0]; + ledger->updateItem(key, certificateId); +} diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index b4af4393e3..a3a0ebfd32 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -43,6 +43,8 @@ signals: void accountResult(QJsonObject result); void certificateInfoResult(QJsonObject result); void alreadyOwnedResult(QJsonObject result); + void availableUpdatesResult(QJsonObject result); + void updateItemResult(QJsonObject result); void updateCertificateStatus(const QString& certID, uint certStatus); @@ -89,6 +91,9 @@ 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 updateItem(const QString& certificateId); + private: QString _appsPath; }; diff --git a/interface/src/main.cpp b/interface/src/main.cpp index e80dc1d213..51ec4b1327 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -129,7 +129,7 @@ int main(int argc, const char* argv[]) { if (socket.waitForConnected(LOCAL_SERVER_TIMEOUT_MS)) { if (parser.isSet(urlOption)) { QUrl url = QUrl(parser.value(urlOption)); - if (url.isValid() && url.scheme() == HIFI_URL_SCHEME) { + if (url.isValid() && url.scheme() == URL_SCHEME_HIFI) { qDebug() << "Writing URL to local socket"; socket.write(url.toString().toUtf8()); if (!socket.waitForBytesWritten(5000)) { diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 4bc8ec0bd3..c9bee659af 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -69,11 +69,11 @@ void AssetMappingsScriptingInterface::getMapping(QString path, QJSValue callback void AssetMappingsScriptingInterface::uploadFile(QString path, QString mapping, QJSValue startedCallback, QJSValue completedCallback, bool dropEvent) { static const QString helpText = - "Upload your asset to a specific folder by entering the full path. Specifying\n" + "Upload your asset to a specific folder by entering the full path. Specifying " "a new folder name will automatically create that folder for you."; static const QString dropHelpText = "This file will be added to your Asset Server.\n" - "Use the field below to place your file in a specific folder or to rename it.\n" + "Use the field below to place your file in a specific folder or to rename it. " "Specifying a new folder name will automatically create that folder for you."; auto offscreenUi = DependencyManager::get(); @@ -152,6 +152,10 @@ void AssetMappingsScriptingInterface::deleteMappings(QStringList paths, QJSValue request->start(); } +void AssetMappingsScriptingInterface::sortProxyModel(int column, Qt::SortOrder order) { + _proxyModel.sort(column, order); +} + void AssetMappingsScriptingInterface::getAllMappings(QJSValue callback) { auto assetClient = DependencyManager::get(); auto request = assetClient->createGetAllMappingsRequest(); @@ -287,7 +291,7 @@ void AssetMappingModel::refresh() { item->setData(parts[i], Qt::UserRole + 2); item->setData("atp:" + fullPath, Qt::UserRole + 3); item->setData(fullPath, Qt::UserRole + 4); - + if (lastItem) { lastItem->appendRow(item); } else { diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.h b/interface/src/scripting/AssetMappingsScriptingInterface.h index 1a4c7dae48..b27a72fbd0 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.h +++ b/interface/src/scripting/AssetMappingsScriptingInterface.h @@ -82,6 +82,7 @@ public: Q_INVOKABLE void getAllMappings(QJSValue callback = QJSValue()); Q_INVOKABLE void renameMapping(QString oldPath, QString newPath, QJSValue callback = QJSValue()); Q_INVOKABLE void setBakingEnabled(QStringList paths, bool enabled, QJSValue callback = QJSValue()); + Q_INVOKABLE void sortProxyModel(int column, Qt::SortOrder order = Qt::AscendingOrder); protected: QSet _pendingRequests; diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index a130b46877..34a3630b78 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include @@ -182,7 +183,6 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) { } void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD) { - auto oldDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; selectedDevice = device; @@ -200,32 +200,137 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); } -void AudioDeviceList::onDevicesChanged(const QList& devices, bool isHMD) { - QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; +// Function returns 'strings similarity' as a number. The lesser number - the more similar strings are. Absolutely equal strings should return 0. +// Optimized version kindly provided by Ken +int levenshteinDistance(const QString& s1, const QString& s2) { + const int m = s1.size(); + const int n = s2.size(); - const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName; + if (m == 0) { + return n; + } + if (n == 0) { + return m; + } + + auto cost = (int*)alloca((n + 1) * sizeof(int)); + + for (int j = 0; j <= n; j++) { + cost[j] = j; + } + + for (int i = 0; i < m; i++) { + + int prev = i; + cost[0] = i + 1; + + for (int j = 0; j < n; j++) { + + int temp = cost[j + 1]; + cost[j + 1] = (s1[i] == s2[j]) ? prev : std::min(cost[j], std::min(temp, prev)) + 1; + prev = temp; + } + } + return cost[n]; +} + +std::shared_ptr getSimilarDevice(const QString& deviceName, const QList>& devices) { + + int minDistance = INT_MAX; + int minDistanceIndex = 0; + + for (auto i = 0; i < devices.length(); ++i) { + auto distance = levenshteinDistance(deviceName, devices[i]->info.deviceName()); + if (distance < minDistance) { + minDistance = distance; + minDistanceIndex = i; + } + } + + return devices[minDistanceIndex]; +} + +void AudioDeviceList::onDevicesChanged(const QList& devices) { beginResetModel(); - _devices.clear(); + QList> newDevices; + bool hmdIsSelected = false; + bool desktopIsSelected = false; + + foreach(const QAudioDeviceInfo& deviceInfo, devices) { + for (bool isHMD : {false, true}) { + auto &backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName; + if (deviceInfo.deviceName() == backupSelectedDeviceName) { + QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; + selectedDevice = deviceInfo; + backupSelectedDeviceName.clear(); + } + } + } foreach(const QAudioDeviceInfo& deviceInfo, devices) { AudioDevice device; - bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop; device.info = deviceInfo; device.display = device.info.deviceName() .replace("High Definition", "HD") .remove("Device") .replace(" )", ")"); - if (!selectedDevice.isNull()) { - isSelected = (device.info == selectedDevice); - } else { - //no selected device for context. fallback to saved - isSelected = (device.info.deviceName() == savedDeviceName); + + for (bool isHMD : {false, true}) { + QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; + bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop; + + if (!selectedDevice.isNull()) { + isSelected = (device.info == selectedDevice); + } + else { + //no selected device for context. fallback to saved + const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName; + isSelected = (device.info.deviceName() == savedDeviceName); + } + + if (isSelected) { + if (isHMD) { + hmdIsSelected = isSelected; + } else { + desktopIsSelected = isSelected; + } + + // check if this device *is not* in old devices list - it means it was just re-plugged so needs to be selected explicitly + bool isNewDevice = true; + for (auto& oldDevice : _devices) { + if (oldDevice->info.deviceName() == device.info.deviceName()) { + isNewDevice = false; + break; + } + } + + if (isNewDevice) { + emit selectedDevicePlugged(device.info, isHMD); + } + } } + qDebug() << "adding audio device:" << device.display << device.selectedDesktop << device.selectedHMD << _mode; - _devices.push_back(newDevice(device)); + newDevices.push_back(newDevice(device)); } + if (!newDevices.isEmpty()) { + if (!hmdIsSelected) { + _backupSelectedHMDDeviceName = !_selectedHMDDevice.isNull() ? _selectedHMDDevice.deviceName() : _hmdSavedDeviceName; + auto device = getSimilarDevice(_backupSelectedHMDDeviceName, newDevices); + device->selectedHMD = true; + emit selectedDevicePlugged(device->info, true); + } + if (!desktopIsSelected) { + _backupSelectedDesktopDeviceName = !_selectedDesktopDevice.isNull() ? _selectedDesktopDevice.deviceName() : _desktopSavedDeviceName; + auto device = getSimilarDevice(_backupSelectedDesktopDeviceName, newDevices); + device->selectedDesktop = true; + emit selectedDevicePlugged(device->info, false); + } + } + + _devices.swap(newDevices); endResetModel(); } @@ -271,12 +376,10 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { // connections are made after client is initialized, so we must also fetch the devices const QList& devicesInput = client->getAudioDevices(QAudio::AudioInput); const QList& devicesOutput = client->getAudioDevices(QAudio::AudioOutput); - //setup HMD devices - _inputs.onDevicesChanged(devicesInput, true); - _outputs.onDevicesChanged(devicesOutput, true); - //setup Desktop devices - _inputs.onDevicesChanged(devicesInput, false); - _outputs.onDevicesChanged(devicesOutput, false); + + //setup devices + _inputs.onDevicesChanged(devicesInput); + _outputs.onDevicesChanged(devicesOutput); } AudioDevices::~AudioDevices() {} @@ -375,11 +478,19 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList& devices, bool isHMD); + void onDevicesChanged(const QList& devices); protected: friend class AudioDevices; @@ -64,6 +65,8 @@ protected: const QAudio::Mode _mode; QAudioDeviceInfo _selectedDesktopDevice; QAudioDeviceInfo _selectedHMDDevice; + QString _backupSelectedDesktopDeviceName; + QString _backupSelectedHMDDeviceName; QList> _devices; QString _hmdSavedDeviceName; QString _desktopSavedDeviceName; @@ -117,13 +120,13 @@ public: AudioDevices(bool& contextIsHMD); virtual ~AudioDevices(); - void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD); - void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD); - signals: void nop(); private slots: + void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD); + void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD); + void onContextChanged(const QString& context); void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice, bool isHMD); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index e0a008b25e..58ec744f4e 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -74,17 +74,19 @@ QScriptValue WindowScriptingInterface::hasFocus() { void WindowScriptingInterface::setFocus() { // It's forbidden to call focus() from another thread. qApp->postLambdaEvent([] { - auto window = qApp->getWindow(); - window->activateWindow(); - window->setFocus(); + qApp->setFocus(); + }); +} + +void WindowScriptingInterface::raise() { + // It's forbidden to call raise() from another thread. + qApp->postLambdaEvent([] { + qApp->raise(); }); } void WindowScriptingInterface::raiseMainWindow() { - // It's forbidden to call raise() from another thread. - qApp->postLambdaEvent([] { - qApp->getWindow()->raise(); - }); + raise(); } /// Display an alert box @@ -126,7 +128,7 @@ void WindowScriptingInterface::promptAsync(const QString& message, const QString } void WindowScriptingInterface::disconnectedFromDomain() { - emit domainChanged(""); + emit domainChanged(QUrl()); } QString fixupPathForMac(const QString& directory) { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 5a30f44856..e3b092d011 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -62,14 +62,22 @@ public slots: QScriptValue hasFocus(); /**jsdoc - * Make the Interface window have focus. + * Make the Interface window have focus. On Windows, if Interface doesn't already have focus, the task bar icon flashes to + * indicate that Interface wants attention but focus isn't taken away from the application that the user is using. * @function Window.setFocus */ void setFocus(); /**jsdoc - * Raise the Interface window if it is minimized, and give it focus. + * Raise the Interface window if it is minimized. If raised, the window gains focus. + * @function Window.raise + */ + void raise(); + + /**jsdoc + * Raise the Interface window if it is minimized. If raised, the window gains focus. * @function Window.raiseMainWindow + * @deprecated Use {@link Window.raise|raise} instead. */ void raiseMainWindow(); @@ -523,7 +531,7 @@ signals: * Triggered when you change the domain you're visiting. Warning: Is not emitted if you go to domain that * isn't running. * @function Window.domainChanged - * @param {string} domain - The domain's IP address. + * @param {string} domainURL - The domain's URL. * @returns {Signal} * @example Report when you change domains. * function onDomainChanged(domain) { @@ -532,7 +540,7 @@ signals: * * Window.domainChanged.connect(onDomainChanged); */ - void domainChanged(const QString& domain); + void domainChanged(QUrl domainURL); /**jsdoc * Triggered when you try to navigate to a *.json, *.svo, or *.svo.json URL in a Web browser within Interface. diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 1a23674fa3..87bf09a252 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -45,7 +45,6 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare connect(&domainHandler, &DomainHandler::connectedToDomain, this, &AddressBarDialog::hostChanged); connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &AddressBarDialog::hostChanged); connect(DependencyManager::get().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed); - connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL); } void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions) { diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 7806436774..1254fabbd2 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -36,7 +36,6 @@ signals: void backEnabledChanged(); void forwardEnabledChanged(); void useFeedChanged(); - void receivedHifiSchemeURL(const QString& url); void hostChanged(); protected: diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 51d1c9bf6b..3053cb8855 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -16,19 +16,19 @@ #include "Application.h" #include "Menu.h" -HIFI_QML_DEF(AvatarInputs) - static AvatarInputs* INSTANCE{ nullptr }; Setting::Handle showAudioToolsSetting { QStringList { "AvatarInputs", "showAudioTools" }, false }; AvatarInputs* AvatarInputs::getInstance() { - Q_ASSERT(INSTANCE); + if (!INSTANCE) { + INSTANCE = new AvatarInputs(); + Q_ASSERT(INSTANCE); + } return INSTANCE; } -AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { - INSTANCE = this; +AvatarInputs::AvatarInputs(QObject* parent) : QObject(parent) { _showAudioTools = showAudioToolsSetting.get(); } diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index fb48df9d73..47f520a639 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -19,7 +19,7 @@ public: \ private: \ type _##name{ initialValue }; -class AvatarInputs : public QQuickItem { +class AvatarInputs : public QObject { Q_OBJECT HIFI_QML_DECL @@ -32,7 +32,7 @@ class AvatarInputs : public QQuickItem { public: static AvatarInputs* getInstance(); Q_INVOKABLE float loudnessToAudioLevel(float loudness); - AvatarInputs(QQuickItem* parent = nullptr); + AvatarInputs(QObject* parent = nullptr); void update(); bool showAudioTools() const { return _showAudioTools; } diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index ff5a202910..f4efd1301d 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -37,7 +37,8 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) : _ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection), _drawInFront(base3DOverlay->_drawInFront), _drawHUDLayer(base3DOverlay->_drawHUDLayer), - _isGrabbable(base3DOverlay->_isGrabbable) + _isGrabbable(base3DOverlay->_isGrabbable), + _isVisibleInSecondaryCamera(base3DOverlay->_isVisibleInSecondaryCamera) { setTransform(base3DOverlay->getTransform()); } @@ -142,6 +143,13 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { setIsGrabbable(isGrabbable.toBool()); } + auto isVisibleInSecondaryCamera = properties["isVisibleInSecondaryCamera"]; + if (isVisibleInSecondaryCamera.isValid()) { + bool value = isVisibleInSecondaryCamera.toBool(); + setIsVisibleInSecondaryCamera(value); + needRenderItemUpdate = true; + } + if (properties["position"].isValid()) { setLocalPosition(vec3FromVariant(properties["position"])); needRenderItemUpdate = true; @@ -221,6 +229,8 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. + * @property {boolean} isVisibleInSecondaryCamera=false - If true, the overlay is rendered in secondary + * camera views. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if * parentID is an avatar skeleton. A value of 65535 means "no joint". @@ -259,6 +269,9 @@ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "grabbable") { return _isGrabbable; } + if (property == "isVisibleInSecondaryCamera") { + return _isVisibleInSecondaryCamera; + } if (property == "parentID") { return getParentID(); } diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index bbf064fddd..ab83a64273 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -48,6 +48,7 @@ public: bool getDrawInFront() const { return _drawInFront; } bool getDrawHUDLayer() const { return _drawHUDLayer; } bool getIsGrabbable() const { return _isGrabbable; } + virtual bool getIsVisibleInSecondaryCamera() const override { return _isVisibleInSecondaryCamera; } void setIsSolid(bool isSolid) { _isSolid = isSolid; } void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; } @@ -55,6 +56,7 @@ public: virtual void setDrawInFront(bool value) { _drawInFront = value; } virtual void setDrawHUDLayer(bool value) { _drawHUDLayer = value; } void setIsGrabbable(bool value) { _isGrabbable = value; } + virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; } virtual AABox getBounds() const override = 0; @@ -92,6 +94,7 @@ protected: bool _drawInFront; bool _drawHUDLayer; bool _isGrabbable { false }; + bool _isVisibleInSecondaryCamera { false }; mutable bool _renderVariableDirty { true }; QString _name; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 651213ae99..1c00f57eec 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -89,8 +89,11 @@ void ModelOverlay::update(float deltatime) { } if (_visibleDirty) { _visibleDirty = false; - // don't show overlays in mirrors - _model->setVisibleInScene(getVisible(), scene, render::ItemKey::TAG_BITS_0, false); + // don't show overlays in mirrors or spectator-cam unless _isVisibleInSecondaryCamera is true + _model->setVisibleInScene(getVisible(), scene, + render::ItemKey::TAG_BITS_0 | + (_isVisibleInSecondaryCamera ? render::ItemKey::TAG_BITS_1 : render::ItemKey::TAG_BITS_NONE), + false); } if (_drawInFrontDirty) { _drawInFrontDirty = false; diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 88a1729d68..3ef3f23fec 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -36,6 +36,11 @@ public: void clearSubRenderItemIDs(); void setSubRenderItemIDs(const render::ItemIDs& ids); + virtual void setIsVisibleInSecondaryCamera(bool value) override { + Base3DOverlay::setIsVisibleInSecondaryCamera(value); + _visibleDirty = true; + } + void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index f1be23ed39..2f27d50f7e 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -56,6 +56,8 @@ public: bool isLoaded() { return _isLoaded; } bool getVisible() const { return _visible; } virtual bool isTransparent() { return getAlphaPulse() != 0.0f || getAlpha() != 1.0f; }; + virtual bool getIsVisibleInSecondaryCamera() const { return false; } + xColor getColor(); float getAlpha(); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 35274e4fbe..6556cedb21 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -54,6 +54,7 @@ Overlays::Overlays() { } void Overlays::cleanupAllOverlays() { + _shuttingDown = true; QMap overlaysHUD; QMap overlaysWorld; { @@ -147,6 +148,10 @@ void Overlays::enable() { // Note, can't be invoked by scripts, but can be called by the InterfaceParentFinder // class on packet processing threads Overlay::Pointer Overlays::getOverlay(OverlayID id) const { + if (_shuttingDown) { + return nullptr; + } + QMutexLocker locker(&_mutex); if (_overlaysHUD.contains(id)) { return _overlaysHUD[id]; @@ -157,6 +162,10 @@ Overlay::Pointer Overlays::getOverlay(OverlayID id) const { } OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) { + if (_shuttingDown) { + return UNKNOWN_OVERLAY_ID; + } + if (QThread::currentThread() != thread()) { OverlayID result; PROFILE_RANGE(script, __FUNCTION__); @@ -261,6 +270,10 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) } OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) { + if (_shuttingDown) { + return UNKNOWN_OVERLAY_ID; + } + OverlayID thisID = OverlayID(QUuid::createUuid()); overlay->setOverlayID(thisID); overlay->setStackOrder(_stackOrder++); @@ -283,6 +296,10 @@ OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) { } OverlayID Overlays::cloneOverlay(OverlayID id) { + if (_shuttingDown) { + return UNKNOWN_OVERLAY_ID; + } + if (QThread::currentThread() != thread()) { OverlayID result; PROFILE_RANGE(script, __FUNCTION__); @@ -301,6 +318,10 @@ OverlayID Overlays::cloneOverlay(OverlayID id) { } bool Overlays::editOverlay(OverlayID id, const QVariant& properties) { + if (_shuttingDown) { + return false; + } + auto thisOverlay = getOverlay(id); if (!thisOverlay) { return false; @@ -320,6 +341,10 @@ bool Overlays::editOverlay(OverlayID id, const QVariant& properties) { } bool Overlays::editOverlays(const QVariant& propertiesById) { + if (_shuttingDown) { + return false; + } + bool defer2DOverlays = QThread::currentThread() != thread(); QVariantMap deferrred; @@ -351,6 +376,10 @@ bool Overlays::editOverlays(const QVariant& propertiesById) { } void Overlays::deleteOverlay(OverlayID id) { + if (_shuttingDown) { + return; + } + if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "deleteOverlay", Q_ARG(OverlayID, id)); return; @@ -374,6 +403,9 @@ void Overlays::deleteOverlay(OverlayID id) { } QString Overlays::getOverlayType(OverlayID overlayId) { + if (_shuttingDown) { + return ""; + } if (QThread::currentThread() != thread()) { QString result; PROFILE_RANGE(script, __FUNCTION__); @@ -388,8 +420,23 @@ QString Overlays::getOverlayType(OverlayID overlayId) { return ""; } +QObject* Overlays::getOverlayObject(OverlayID id) { + if (QThread::currentThread() != thread()) { + QObject* result; + PROFILE_RANGE(script, __FUNCTION__); + BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(OverlayID, id)); + return result; + } + + Overlay::Pointer thisOverlay = getOverlay(id); + if (thisOverlay) { + return qobject_cast(&(*thisOverlay)); + } + return nullptr; +} + OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) { - if (!_enabled) { + if (_shuttingDown || !_enabled) { return UNKNOWN_OVERLAY_ID; } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index c3d87642f1..c2f6e3e693 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -235,6 +235,50 @@ public slots: */ QString getOverlayType(OverlayID overlayId); + /**jsdoc + * Get the overlay script object. In particular, this is useful for accessing the event bridge for a web3d + * overlay. + * @function Overlays.getOverlayObject + * @param {Uuid} overlayID - The ID of the overlay to get the script object of. + * @returns {object} The script object for the overlay if found. + * @example Receive "hello" messages from a web3d overlay. + * // HTML file: name "web3d.html". + * + * + * + * HELLO + * + * + *

HELLO

+ * + * + * + * + * // Script file. + * var web3dOverlay = Overlays.addOverlay("web3d", { + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.5, z: -3 })), + * rotation: MyAvatar.orientation, + * url: Script.resolvePath("web3d.html"), + * alpha: 1.0 + * }); + * + * function onWebEventReceived(event) { + * print("onWebEventReceived() : " + JSON.stringify(event)); + * } + * + * overlayObject = Overlays.getOverlayObject(web3dOverlay); + * overlayObject.webEventReceived.connect(onWebEventReceived); + * + * Script.scriptEnding.connect(function () { + * Overlays.deleteOverlay(web3dOverlay); + * }); + */ + QObject* getOverlayObject(OverlayID id); + /**jsdoc * Get the ID of the 2D overlay at a particular point on the screen or HUD. * @function Overlays.getOverlayAtPoint @@ -680,6 +724,7 @@ private: unsigned int _stackOrder { 1 }; bool _enabled = true; + std::atomic _shuttingDown{ false }; PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index f99ced0021..185547a333 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -49,7 +49,11 @@ namespace render { builder.withInvisible(); } - builder.withTagBits(render::ItemKey::TAG_BITS_0); // Only draw overlays in main view + // always visible in primary view. if isVisibleInSecondaryCamera, also draw in secondary view + uint32_t viewTaskBits = render::ItemKey::TAG_BITS_0 | + (overlay->getIsVisibleInSecondaryCamera() ? render::ItemKey::TAG_BITS_1 : render::ItemKey::TAG_BITS_NONE); + + builder.withTagBits(viewTaskBits); return builder.build(); } diff --git a/interface/src/ui/overlays/QmlOverlay.cpp b/interface/src/ui/overlays/QmlOverlay.cpp index f4a9034187..2a583e0450 100644 --- a/interface/src/ui/overlays/QmlOverlay.cpp +++ b/interface/src/ui/overlays/QmlOverlay.cpp @@ -39,18 +39,20 @@ void QmlOverlay::buildQmlElement(const QUrl& url) { auto offscreenUi = DependencyManager::get(); offscreenUi->load(url, [=](QQmlContext* context, QObject* object) { - QQuickItem* rawPtr = dynamic_cast(object); - // Create a shared ptr with a custom deleter lambda, that calls deleteLater - _qmlElement = std::shared_ptr(rawPtr, [](QQuickItem* ptr) { - if (ptr) { - ptr->deleteLater(); - } - }); + _qmlElement = dynamic_cast(object); + connect(_qmlElement, &QObject::destroyed, this, &QmlOverlay::qmlElementDestroyed); }); } +void QmlOverlay::qmlElementDestroyed() { + _qmlElement = nullptr; +} + QmlOverlay::~QmlOverlay() { - _qmlElement.reset(); + if (_qmlElement) { + _qmlElement->deleteLater(); + } + _qmlElement = nullptr; } // QmlOverlay replaces Overlay's properties with those defined in the QML file used but keeps Overlay2D's properties. @@ -62,15 +64,13 @@ void QmlOverlay::setProperties(const QVariantMap& properties) { Overlay2D::setProperties(properties); auto bounds = _bounds; - std::weak_ptr weakQmlElement = _qmlElement; // check to see if qmlElement still exists - auto qmlElement = weakQmlElement.lock(); - if (qmlElement) { - qmlElement->setX(bounds.left()); - qmlElement->setY(bounds.top()); - qmlElement->setWidth(bounds.width()); - qmlElement->setHeight(bounds.height()); - QMetaObject::invokeMethod(qmlElement.get(), "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties)); + if (_qmlElement) { + _qmlElement->setX(bounds.left()); + _qmlElement->setY(bounds.top()); + _qmlElement->setWidth(bounds.width()); + _qmlElement->setHeight(bounds.height()); + QMetaObject::invokeMethod(_qmlElement, "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties)); } } diff --git a/interface/src/ui/overlays/QmlOverlay.h b/interface/src/ui/overlays/QmlOverlay.h index ced2b6fa1f..7f2cf5a918 100644 --- a/interface/src/ui/overlays/QmlOverlay.h +++ b/interface/src/ui/overlays/QmlOverlay.h @@ -32,10 +32,11 @@ public: void render(RenderArgs* args) override; private: + Q_INVOKABLE void qmlElementDestroyed(); Q_INVOKABLE void buildQmlElement(const QUrl& url); protected: - std::shared_ptr _qmlElement; + QQuickItem* _qmlElement{ nullptr }; }; #endif // hifi_QmlOverlay_h diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index fba36f0c77..e7641ab2c2 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -40,8 +40,10 @@ QUrl const TextOverlay::URL(QString("hifi/overlays/TextOverlay.qml")); * * @property {number} margin=0 - Sets the leftMargin and topMargin values, in pixels. * Write-only. - * @property {number} leftMargin=0 - The left margin's size, in pixels. Write-only. - * @property {number} topMargin=0 - The top margin's size, in pixels. Write-only. + * @property {number} leftMargin=0 - The left margin's size, in pixels. This value is also used for the right margin. + * Write-only. + * @property {number} topMargin=0 - The top margin's size, in pixels. This value is also used for the bottom margin. + * Write-only. * @property {string} text="" - The text to display. Text does not automatically wrap; use \n for a line break. Text * is clipped to the bounds. Write-only. * @property {number} font.size=18 - The size of the text, in pixels. Write-only. diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 57d1cc0acf..10050c94d0 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -63,6 +63,19 @@ static const float OPAQUE_ALPHA_THRESHOLD = 0.99f; const QString Web3DOverlay::TYPE = "web3d"; const QString Web3DOverlay::QML = "Web3DOverlay.qml"; + +static auto qmlSurfaceDeleter = [](OffscreenQmlSurface* surface) { + AbstractViewStateInterface::instance()->postLambdaEvent([surface] { + if (AbstractViewStateInterface::instance()->isAboutToQuit()) { + // WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown + // if the application has already stopped its event loop, delete must be explicit + delete surface; + } else { + surface->deleteLater(); + } + }); +}; + Web3DOverlay::Web3DOverlay() { _touchDevice.setCapabilities(QTouchDevice::Position); _touchDevice.setType(QTouchDevice::TouchScreen); @@ -75,7 +88,8 @@ Web3DOverlay::Web3DOverlay() { connect(this, &Web3DOverlay::resizeWebSurface, this, &Web3DOverlay::onResizeWebSurface); //need to be intialized before Tablet 1st open - _webSurface = DependencyManager::get()->acquire(_url); + _webSurface = DependencyManager::get()->acquire(QML); + _cachedWebSurface = true; _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED @@ -114,6 +128,7 @@ void Web3DOverlay::destroyWebSurface() { if (!_webSurface) { return; } + QQuickItem* rootItem = _webSurface->getRootItem(); if (rootItem && rootItem->objectName() == "tabletRoot") { @@ -135,10 +150,15 @@ void Web3DOverlay::destroyWebSurface() { QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent); QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived); - auto offscreenCache = DependencyManager::get(); - // FIXME prevents crash on shutdown, but we shoudln't have to do this check - if (offscreenCache) { - offscreenCache->release(QML, _webSurface); + + // If the web surface was fetched out of the cache, release it back into the cache + if (_cachedWebSurface) { + auto offscreenCache = DependencyManager::get(); + // FIXME prevents crash on shutdown, but we shoudln't have to do this check + if (offscreenCache) { + offscreenCache->release(QML, _webSurface); + } + _cachedWebSurface = false; } _webSurface.reset(); } @@ -147,6 +167,8 @@ void Web3DOverlay::buildWebSurface() { if (_webSurface) { return; } + // FIXME the context save here is most likely unecessary since the QML surfaces now render + // off the main thread, and all GL context work is done off the main thread (I *think*) gl::withSavedContext([&] { // FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces // and the current rendering load) @@ -156,10 +178,13 @@ void Web3DOverlay::buildWebSurface() { if (isWebContent()) { _webSurface = DependencyManager::get()->acquire(QML); + _cachedWebSurface = true; _webSurface->getRootItem()->setProperty("url", _url); _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL); } else { - _webSurface = DependencyManager::get()->acquire(_url); + _webSurface = QSharedPointer(new OffscreenQmlSurface(), qmlSurfaceDeleter); + _webSurface->load(_url); + _cachedWebSurface = false; setupQmlSurface(); } _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getWorldPosition())); diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 4098e98488..d888424cbc 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -88,6 +88,7 @@ private: InputMode _inputMode { Touch }; QSharedPointer _webSurface; + bool _cachedWebSurface{ false }; gpu::TexturePointer _texture; QString _url; QString _scriptURL; diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 849ea5ee6b..dc004fe60d 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -882,6 +882,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar //virtual const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { +#ifdef Q_OS_ANDROID + // disable IK on android + return underPoses; +#endif + // allows solutionSource to be overridden by an animVar auto solutionSource = animVars.lookup(_solutionSourceVar, (int)_solutionSource); diff --git a/libraries/animation/src/ElbowConstraint.cpp b/libraries/animation/src/ElbowConstraint.cpp index 17c6bb2da6..deaf1a0945 100644 --- a/libraries/animation/src/ElbowConstraint.cpp +++ b/libraries/animation/src/ElbowConstraint.cpp @@ -66,16 +66,12 @@ bool ElbowConstraint::apply(glm::quat& rotation) const { bool twistWasClamped = (twistAngle != clampedTwistAngle); // update rotation - const float MIN_SWING_REAL_PART = 0.99999f; - if (twistWasClamped || fabsf(swingRotation.w) < MIN_SWING_REAL_PART) { - if (twistWasClamped) { - twistRotation = glm::angleAxis(clampedTwistAngle, _axis); - } - // we discard all swing and only keep twist - rotation = twistRotation * _referenceRotation; - return true; + if (twistWasClamped) { + twistRotation = glm::angleAxis(clampedTwistAngle, _axis); } - return false; + // we discard all swing and only keep twist + rotation = twistRotation * _referenceRotation; + return true; } glm::quat ElbowConstraint::computeCenterRotation() const { diff --git a/libraries/audio/src/AudioSRCData.h b/libraries/audio/src/AudioSRCData.h index 872a8b7ea9..4a51038683 100644 --- a/libraries/audio/src/AudioSRCData.h +++ b/libraries/audio/src/AudioSRCData.h @@ -294,786 +294,1042 @@ static const float prototypeFilterLQ[PROTOTYPE_COEFS_LQ] = { }; // Minimum-phase equiripple FIR lowpass -// taps = 96, phases = 32 +// taps = 128, phases = 32 // -// passband = 20.4khz @ 44.1khz sample rate -// stopband = 22.3khz @ 44.1khz sample rate +// passband = 20.8khz @ 44.1khz sample rate +// stopband = 22.25khz @ 44.1khz sample rate // passband ripple = +-0.01dB // stopband attn = -120dB (-60dB at Fs/2) // -static const int PROTOTYPE_TAPS_MQ = 96; // filter taps per phase +static const int PROTOTYPE_TAPS_MQ = 128; // filter taps per phase static const int PROTOTYPE_PHASES_MQ = 32; // oversampling factor static const int PROTOTYPE_COEFS_MQ = PROTOTYPE_TAPS_MQ * PROTOTYPE_PHASES_MQ; static const float prototypeFilterMQ[PROTOTYPE_COEFS_MQ] = { - 0.00000000e+00f, 2.66261387e-05f, 2.32573075e-05f, 3.40598514e-05f, - 4.71976232e-05f, 6.41453728e-05f, 8.54998397e-05f, 1.11687756e-04f, - 1.43801203e-04f, 1.82845116e-04f, 2.30066959e-04f, 2.86345358e-04f, - 3.53237802e-04f, 4.32445137e-04f, 5.25448765e-04f, 6.34181809e-04f, - 7.60751248e-04f, 9.07447815e-04f, 1.07664885e-03f, 1.27106372e-03f, - 1.49358697e-03f, 1.74743568e-03f, 2.03588950e-03f, 2.36271119e-03f, - 2.73178932e-03f, 3.14735017e-03f, 3.61394575e-03f, 4.13622717e-03f, - 4.71948430e-03f, 5.36900611e-03f, 6.09056802e-03f, 6.89015275e-03f, - 7.77420063e-03f, 8.74929355e-03f, 9.82247765e-03f, 1.10010827e-02f, - 1.22926302e-02f, 1.37050920e-02f, 1.52467086e-02f, 1.69259236e-02f, - 1.87515164e-02f, 2.07324611e-02f, 2.28780155e-02f, 2.51976248e-02f, - 2.77009067e-02f, 3.03976923e-02f, 3.32978310e-02f, 3.64113360e-02f, - 3.97483567e-02f, 4.33188718e-02f, 4.71330394e-02f, 5.12008208e-02f, - 5.55321084e-02f, 6.01366326e-02f, 6.50238954e-02f, 7.02031239e-02f, - 7.56832648e-02f, 8.14728338e-02f, 8.75798730e-02f, 9.40119584e-02f, - 1.00776061e-01f, 1.07878507e-01f, 1.15324915e-01f, 1.23120096e-01f, - 1.31268060e-01f, 1.39771791e-01f, 1.48633353e-01f, 1.57853754e-01f, - 1.67432845e-01f, 1.77369245e-01f, 1.87660373e-01f, 1.98302270e-01f, - 2.09289566e-01f, 2.20615469e-01f, 2.32271687e-01f, 2.44248375e-01f, - 2.56534006e-01f, 2.69115519e-01f, 2.81978070e-01f, 2.95105197e-01f, - 3.08478608e-01f, 3.22078299e-01f, 3.35882501e-01f, 3.49867623e-01f, - 3.64008344e-01f, 3.78277579e-01f, 3.92646502e-01f, 4.07084554e-01f, - 4.21559509e-01f, 4.36037587e-01f, 4.50483333e-01f, 4.64859851e-01f, - 4.79128834e-01f, 4.93250606e-01f, 5.07184249e-01f, 5.20887717e-01f, - 5.34317956e-01f, 5.47430992e-01f, 5.60182068e-01f, 5.72525885e-01f, - 5.84416587e-01f, 5.95808093e-01f, 6.06654109e-01f, 6.16908418e-01f, - 6.26525013e-01f, 6.35458287e-01f, 6.43663262e-01f, 6.51095737e-01f, - 6.57712516e-01f, 6.63471582e-01f, 6.68332383e-01f, 6.72255982e-01f, - 6.75205267e-01f, 6.77145171e-01f, 6.78042866e-01f, 6.77868024e-01f, - 6.76592912e-01f, 6.74192735e-01f, 6.70645661e-01f, 6.65933116e-01f, - 6.60039959e-01f, 6.52954537e-01f, 6.44668978e-01f, 6.35179268e-01f, - 6.24485354e-01f, 6.12591298e-01f, 5.99505382e-01f, 5.85240204e-01f, - 5.69812635e-01f, 5.53244046e-01f, 5.35560224e-01f, 5.16791389e-01f, - 4.96972230e-01f, 4.76141864e-01f, 4.54343785e-01f, 4.31625775e-01f, - 4.08039850e-01f, 3.83642160e-01f, 3.58492768e-01f, 3.32655624e-01f, - 3.06198278e-01f, 2.79191781e-01f, 2.51710369e-01f, 2.23831288e-01f, - 1.95634574e-01f, 1.67202627e-01f, 1.38620172e-01f, 1.09973678e-01f, - 8.13511972e-02f, 5.28420521e-02f, 2.45362976e-02f, -3.47536059e-03f, - -3.11022613e-02f, -5.82539130e-02f, -8.48404277e-02f, -1.10773085e-01f, - -1.35964505e-01f, -1.60329143e-01f, -1.83783713e-01f, -2.06247521e-01f, - -2.27642912e-01f, -2.47895575e-01f, -2.66934994e-01f, -2.84694759e-01f, - -3.01112940e-01f, -3.16132369e-01f, -3.29701030e-01f, -3.41772346e-01f, - -3.52305311e-01f, -3.61264967e-01f, -3.68622493e-01f, -3.74355402e-01f, - -3.78447769e-01f, -3.80890353e-01f, -3.81680737e-01f, -3.80823346e-01f, - -3.78329604e-01f, -3.74217856e-01f, -3.68513368e-01f, -3.61248333e-01f, - -3.52461771e-01f, -3.42199371e-01f, -3.30513331e-01f, -3.17462278e-01f, - -3.03110942e-01f, -2.87529909e-01f, -2.70795402e-01f, -2.52988932e-01f, - -2.34196901e-01f, -2.14510337e-01f, -1.94024366e-01f, -1.72837915e-01f, - -1.51053161e-01f, -1.28775054e-01f, -1.06110949e-01f, -8.31699904e-02f, - -6.00625904e-02f, -3.68999700e-02f, -1.37935592e-02f, 9.14553458e-03f, - 3.18070733e-02f, 5.40822352e-02f, 7.58641385e-02f, 9.70484745e-02f, - 1.17533938e-01f, 1.37222760e-01f, 1.56021286e-01f, 1.73840407e-01f, - 1.90596011e-01f, 2.06209480e-01f, 2.20608090e-01f, 2.33725388e-01f, - 2.45501544e-01f, 2.55883728e-01f, 2.64826361e-01f, 2.72291364e-01f, - 2.78248393e-01f, 2.82675023e-01f, 2.85556849e-01f, 2.86887621e-01f, - 2.86669233e-01f, 2.84911794e-01f, 2.81633533e-01f, 2.76860737e-01f, - 2.70627618e-01f, 2.62976159e-01f, 2.53955831e-01f, 2.43623409e-01f, - 2.32042617e-01f, 2.19283824e-01f, 2.05423637e-01f, 1.90544470e-01f, - 1.74734152e-01f, 1.58085383e-01f, 1.40695263e-01f, 1.22664710e-01f, - 1.04097951e-01f, 8.51019110e-02f, 6.57856138e-02f, 4.62595576e-02f, - 2.66351206e-02f, 7.02392479e-03f, -1.24628349e-02f, -3.17149760e-02f, - -5.06239785e-02f, -6.90836040e-02f, -8.69905146e-02f, -1.04244853e-01f, - -1.20750775e-01f, -1.36417108e-01f, -1.51157814e-01f, -1.64892426e-01f, - -1.77546604e-01f, -1.89052578e-01f, -1.99349459e-01f, -2.08383604e-01f, - -2.16108994e-01f, -2.22487452e-01f, -2.27488844e-01f, -2.31091294e-01f, - -2.33281282e-01f, -2.34053710e-01f, -2.33411914e-01f, -2.31367695e-01f, - -2.27941159e-01f, -2.23160659e-01f, -2.17062542e-01f, -2.09691005e-01f, - -2.01097713e-01f, -1.91341620e-01f, -1.80488420e-01f, -1.68610294e-01f, - -1.55785433e-01f, -1.42097452e-01f, -1.27634978e-01f, -1.12491085e-01f, - -9.67626792e-02f, -8.05498893e-02f, -6.39555117e-02f, -4.70843056e-02f, - -3.00423695e-02f, -1.29364721e-02f, 4.12658213e-03f, 2.10406497e-02f, - 3.77009117e-02f, 5.40045195e-02f, 6.98511685e-02f, 8.51438453e-02f, - 9.97893434e-02f, 1.13698854e-01f, 1.26788523e-01f, 1.38979975e-01f, - 1.50200835e-01f, 1.60385073e-01f, 1.69473512e-01f, 1.77414145e-01f, - 1.84162459e-01f, 1.89681664e-01f, 1.93943026e-01f, 1.96925891e-01f, - 1.98617880e-01f, 1.99014945e-01f, 1.98121379e-01f, 1.95949757e-01f, - 1.92520816e-01f, 1.87863336e-01f, 1.82013990e-01f, 1.75016936e-01f, - 1.66923648e-01f, 1.57792538e-01f, 1.47688544e-01f, 1.36682666e-01f, - 1.24851512e-01f, 1.12276865e-01f, 9.90449817e-02f, 8.52461277e-02f, - 7.09739571e-02f, 5.63248677e-02f, 4.13973844e-02f, 2.62914655e-02f, - 1.11078925e-02f, -4.05239147e-03f, -1.90890617e-02f, -3.39029253e-02f, - -4.83966164e-02f, -6.24753705e-02f, -7.60474312e-02f, -8.90248310e-02f, - -1.01323842e-01f, -1.12865634e-01f, -1.23576716e-01f, -1.33389421e-01f, - -1.42242349e-01f, -1.50080822e-01f, -1.56857133e-01f, -1.62530890e-01f, - -1.67069336e-01f, -1.70447432e-01f, -1.72648091e-01f, -1.73662223e-01f, - -1.73488855e-01f, -1.72134990e-01f, -1.69615638e-01f, -1.65953690e-01f, - -1.61179685e-01f, -1.55331592e-01f, -1.48454607e-01f, -1.40600737e-01f, - -1.31828470e-01f, -1.22202323e-01f, -1.11792473e-01f, -1.00674140e-01f, - -8.89271262e-02f, -7.66352871e-02f, -6.38858531e-02f, -5.07689244e-02f, - -3.73767275e-02f, -2.38031024e-02f, -1.01427700e-02f, 3.50934751e-03f, - 1.70586918e-02f, 3.04117852e-02f, 4.34768954e-02f, 5.61645819e-02f, - 6.83884030e-02f, 8.00654394e-02f, 9.11168932e-02f, 1.01468633e-01f, - 1.11051648e-01f, 1.19802610e-01f, 1.27664173e-01f, 1.34585462e-01f, - 1.40522346e-01f, 1.45437787e-01f, 1.49302022e-01f, 1.52092772e-01f, - 1.53795398e-01f, 1.54402941e-01f, 1.53916196e-01f, 1.52343637e-01f, - 1.49701396e-01f, 1.46013026e-01f, 1.41309468e-01f, 1.35628632e-01f, - 1.29015253e-01f, 1.21520496e-01f, 1.13201567e-01f, 1.04121296e-01f, - 9.43477288e-02f, 8.39535107e-02f, 7.30154990e-02f, 6.16141003e-02f, - 4.98327306e-02f, 3.77571918e-02f, 2.54750964e-02f, 1.30751821e-02f, - 6.46703310e-04f, -1.17211963e-02f, -2.39401591e-02f, -3.59231836e-02f, - -4.75853156e-02f, -5.88442141e-02f, -6.96206901e-02f, -7.98393844e-02f, - -8.94291504e-02f, -9.83236922e-02f, -1.06461911e-01f, -1.13788431e-01f, - -1.20253885e-01f, -1.25815350e-01f, -1.30436540e-01f, -1.34088139e-01f, - -1.36747929e-01f, -1.38400952e-01f, -1.39039622e-01f, -1.38663699e-01f, - -1.37280328e-01f, -1.34903937e-01f, -1.31556108e-01f, -1.27265426e-01f, - -1.22067204e-01f, -1.16003256e-01f, -1.09121554e-01f, -1.01475836e-01f, - -9.31252402e-02f, -8.41338287e-02f, -7.45701074e-02f, -6.45065373e-02f, - -5.40189487e-02f, -4.31860278e-02f, -3.20886964e-02f, -2.08095155e-02f, - -9.43209682e-03f, 1.95952348e-03f, 1.32815415e-02f, 2.44509086e-02f, - 3.53860502e-02f, 4.60073767e-02f, 5.62379012e-02f, 6.60038045e-02f, - 7.52349145e-02f, 8.38653047e-02f, 9.18337009e-02f, 9.90839224e-02f, - 1.05565308e-01f, 1.11233075e-01f, 1.16048569e-01f, 1.19979672e-01f, - 1.23000853e-01f, 1.25093440e-01f, 1.26245746e-01f, 1.26453062e-01f, - 1.25717722e-01f, 1.24049103e-01f, 1.21463405e-01f, 1.17983660e-01f, - 1.13639474e-01f, 1.08466740e-01f, 1.02507425e-01f, 9.58092271e-02f, - 8.84251889e-02f, 8.04132568e-02f, 7.18358762e-02f, 6.27595398e-02f, - 5.32541869e-02f, 4.33927283e-02f, 3.32505260e-02f, 2.29047308e-02f, - 1.24337991e-02f, 1.91679812e-03f, -8.56707996e-03f, -1.89392067e-02f, - -2.91220390e-02f, -3.90397544e-02f, -4.86187492e-02f, -5.77882518e-02f, - -6.64808274e-02f, -7.46328184e-02f, -8.21849070e-02f, -8.90824700e-02f, - -9.52760405e-02f, -1.00721606e-01f, -1.05380953e-01f, -1.09221973e-01f, - -1.12218800e-01f, -1.14352078e-01f, -1.15609043e-01f, -1.15983584e-01f, - -1.15476334e-01f, -1.14094552e-01f, -1.11852147e-01f, -1.08769485e-01f, - -1.04873243e-01f, -1.00196154e-01f, -9.47768230e-02f, -8.86593336e-02f, - -8.18929115e-02f, -7.45315642e-02f, -6.66336429e-02f, -5.82614122e-02f, - -4.94804661e-02f, -4.03593526e-02f, -3.09689748e-02f, -2.13820268e-02f, - -1.16724544e-02f, -1.91492456e-03f, 7.81582939e-03f, 1.74454843e-02f, - 2.69007705e-02f, 3.61099984e-02f, 4.50035684e-02f, 5.35145862e-02f, - 6.15792651e-02f, 6.91374558e-02f, 7.61331132e-02f, 8.25146874e-02f, - 8.82354960e-02f, 9.32540795e-02f, 9.75345512e-02f, 1.01046761e-01f, - 1.03766581e-01f, 1.05676059e-01f, 1.06763536e-01f, 1.07023704e-01f, - 1.06457645e-01f, 1.05072793e-01f, 1.02882886e-01f, 9.99077707e-02f, - 9.61733515e-02f, 9.17112437e-02f, 8.65585836e-02f, 8.07577320e-02f, - 7.43559143e-02f, 6.74048223e-02f, 5.99602286e-02f, 5.20815485e-02f, - 4.38313441e-02f, 3.52748647e-02f, 2.64794784e-02f, 1.75142425e-02f, - 8.44923569e-03f, -6.44874246e-04f, -9.69744245e-03f, -1.86383724e-02f, - -2.73986645e-02f, -3.59109639e-02f, -4.41100113e-02f, -5.19332538e-02f, - -5.93212174e-02f, -6.62180197e-02f, -7.25717930e-02f, -7.83350496e-02f, - -8.34650279e-02f, -8.79240738e-02f, -9.16798665e-02f, -9.47056367e-02f, - -9.69804367e-02f, -9.84892103e-02f, -9.92229391e-02f, -9.91786555e-02f, - -9.83594994e-02f, -9.67746323e-02f, -9.44391692e-02f, -9.13740384e-02f, - -8.76057968e-02f, -8.31664374e-02f, -7.80930677e-02f, -7.24276448e-02f, - -6.62166182e-02f, -5.95105803e-02f, -5.23638154e-02f, -4.48338833e-02f, - -3.69811953e-02f, -2.88684416e-02f, -2.05602211e-02f, -1.21223919e-02f, - -3.62166500e-03f, 4.87497472e-03f, 1.33008118e-02f, 2.15898817e-02f, - 2.96774903e-02f, 3.75007710e-02f, 4.49990900e-02f, 5.21145974e-02f, - 5.87926126e-02f, 6.49820878e-02f, 7.06359734e-02f, 7.57115940e-02f, - 8.01709511e-02f, 8.39810244e-02f, 8.71140281e-02f, 8.95475866e-02f, - 9.12649204e-02f, 9.22549366e-02f, 9.25123481e-02f, 9.20376276e-02f, - 9.08370647e-02f, 8.89226233e-02f, 8.63119090e-02f, 8.30279355e-02f, - 7.90990135e-02f, 7.45584160e-02f, 6.94441713e-02f, 6.37987430e-02f, - 5.76686372e-02f, 5.11040823e-02f, 4.41585505e-02f, 3.68884245e-02f, - 2.93523999e-02f, 2.16111886e-02f, 1.37268658e-02f, 5.76249790e-03f, - -2.21844412e-03f, -1.01525268e-02f, -1.79769055e-02f, -2.56297984e-02f, - -3.30509798e-02f, -4.01822180e-02f, -4.69678148e-02f, -5.33549679e-02f, - -5.92942039e-02f, -6.47397915e-02f, -6.96500034e-02f, -7.39875785e-02f, - -7.77198807e-02f, -8.08192090e-02f, -8.32629929e-02f, -8.50339403e-02f, - -8.61201727e-02f, -8.65153352e-02f, -8.62185642e-02f, -8.52345328e-02f, - -8.35733873e-02f, -8.12506452e-02f, -7.82870548e-02f, -7.47083936e-02f, - -7.05453033e-02f, -6.58330086e-02f, -6.06109755e-02f, -5.49226267e-02f, - -4.88150284e-02f, -4.23383952e-02f, -3.55457806e-02f, -2.84925949e-02f, - -2.12361386e-02f, -1.38352158e-02f, -6.34954674e-03f, 1.16060193e-03f, - 8.63496366e-03f, 1.60137521e-02f, 2.32381310e-02f, 3.02506552e-02f, - 3.69957912e-02f, 4.34202737e-02f, 4.94736003e-02f, 5.51084064e-02f, - 6.02807966e-02f, 6.49507475e-02f, 6.90823813e-02f, 7.26442553e-02f, - 7.56096025e-02f, 7.79565049e-02f, 7.96681192e-02f, 8.07327409e-02f, - 8.11438831e-02f, 8.09003904e-02f, 8.00063059e-02f, 7.84709455e-02f, - 7.63087509e-02f, 7.35391471e-02f, 7.01864251e-02f, 6.62794512e-02f, - 6.18515053e-02f, 5.69399870e-02f, 5.15860100e-02f, 4.58342039e-02f, - 3.97322256e-02f, 3.33304486e-02f, 2.66814783e-02f, 1.98397856e-02f, - 1.28612257e-02f, 5.80259393e-03f, -1.27886112e-03f, -8.32584019e-03f, - -1.52814930e-02f, -2.20899334e-02f, -2.86965591e-02f, -3.50486065e-02f, - -4.10954995e-02f, -4.67893136e-02f, -5.20850948e-02f, -5.69412403e-02f, - -6.13198474e-02f, -6.51869890e-02f, -6.85129647e-02f, -7.12725306e-02f, - -7.34451585e-02f, -7.50150843e-02f, -7.59714899e-02f, -7.63085524e-02f, - -7.60255166e-02f, -7.51266167e-02f, -7.36211027e-02f, -7.15230741e-02f, - -6.88514539e-02f, -6.56297130e-02f, -6.18857220e-02f, -5.76515554e-02f, - -5.29631207e-02f, -4.78599233e-02f, -4.23847313e-02f, -3.65832079e-02f, - -3.05035130e-02f, -2.41959566e-02f, -1.77125171e-02f, -1.11065052e-02f, - -4.43199940e-03f, 2.25646608e-03f, 8.90442951e-03f, 1.54579021e-02f, - 2.18638281e-02f, 2.80705462e-02f, 3.40280794e-02f, 3.96887204e-02f, - 4.50072604e-02f, 4.99414316e-02f, 5.44522079e-02f, 5.85041536e-02f, - 6.20656397e-02f, 6.51091720e-02f, 6.76114887e-02f, 6.95538952e-02f, - 7.09222580e-02f, 7.17071898e-02f, 7.19040960e-02f, 7.15131728e-02f, - 7.05394351e-02f, 6.89926231e-02f, 6.68871545e-02f, 6.42419416e-02f, - 6.10802598e-02f, 5.74295313e-02f, 5.33211189e-02f, 4.87899934e-02f, - 4.38745121e-02f, 3.86160670e-02f, 3.30586967e-02f, 2.72487970e-02f, - 2.12346816e-02f, 1.50661848e-02f, 8.79427315e-03f, 2.47061577e-03f, - -3.85286592e-03f, -1.01243770e-02f, -1.62927172e-02f, -2.23076581e-02f, - -2.81203948e-02f, -3.36839076e-02f, -3.89533679e-02f, -4.38864517e-02f, - -4.84437422e-02f, -5.25890421e-02f, -5.62895706e-02f, -5.95163477e-02f, - -6.22443207e-02f, -6.44526403e-02f, -6.61247499e-02f, -6.72485494e-02f, - -6.78164946e-02f, -6.78255920e-02f, -6.72774647e-02f, -6.61782995e-02f, - -6.45388112e-02f, -6.23740965e-02f, -5.97035328e-02f, -5.65505921e-02f, - -5.29426881e-02f, -4.89108139e-02f, -4.44894260e-02f, -3.97160733e-02f, - -3.46310671e-02f, -2.92771850e-02f, -2.36993008e-02f, -1.79439902e-02f, - -1.20591515e-02f, -6.09362869e-03f, -9.67646869e-05f, 5.88193161e-03f, - 1.17933059e-02f, 1.75888522e-02f, 2.32211575e-02f, 2.86443333e-02f, - 3.38142963e-02f, 3.86892021e-02f, 4.32297427e-02f, 4.73994738e-02f, - 5.11651476e-02f, 5.44969062e-02f, 5.73685597e-02f, 5.97578063e-02f, - 6.16463827e-02f, 6.30201712e-02f, 6.38694025e-02f, 6.41886234e-02f, - 6.39767725e-02f, 6.32372156e-02f, 6.19775942e-02f, 6.02098911e-02f, - 5.79502219e-02f, 5.52186945e-02f, 5.20393403e-02f, 4.84397091e-02f, - 4.44508421e-02f, 4.01068641e-02f, 3.54447399e-02f, 3.05039700e-02f, - 2.53262499e-02f, 1.99550952e-02f, 1.44355284e-02f, 8.81365390e-03f, - 3.13628120e-03f, -2.54943138e-03f, -8.19639523e-03f, -1.37579753e-02f, - -1.91883895e-02f, -2.44430618e-02f, -2.94789913e-02f, -3.42551088e-02f, - -3.87326249e-02f, -4.28753310e-02f, -4.66498901e-02f, -5.00261035e-02f, - -5.29771799e-02f, -5.54799037e-02f, -5.75148383e-02f, -5.90665317e-02f, - -6.01234993e-02f, -6.06784603e-02f, -6.07283027e-02f, -6.02740708e-02f, - -5.93210354e-02f, -5.78785948e-02f, -5.59601225e-02f, -5.35829951e-02f, - -5.07682523e-02f, -4.75405757e-02f, -4.39279759e-02f, -3.99615901e-02f, - -3.56753971e-02f, -3.11059445e-02f, -2.62920307e-02f, -2.12743678e-02f, - -1.60952574e-02f, -1.07981986e-02f, -5.42758786e-03f, -2.82820897e-05f, - 5.35475816e-03f, 1.06767918e-02f, 1.58937793e-02f, 2.09626000e-02f, - 2.58415407e-02f, 3.04905869e-02f, 3.48717018e-02f, 3.89492209e-02f, - 4.26901199e-02f, 4.60642317e-02f, 4.90445439e-02f, 5.16074029e-02f, - 5.37326813e-02f, 5.54039762e-02f, 5.66086803e-02f, 5.73381350e-02f, - 5.75876520e-02f, 5.73565533e-02f, 5.66481680e-02f, 5.54698025e-02f, - 5.38326410e-02f, 5.17516706e-02f, 4.92455315e-02f, 4.63363691e-02f, - 4.30496184e-02f, 3.94137631e-02f, 3.54601772e-02f, 3.12227524e-02f, - 2.67376509e-02f, 2.20430273e-02f, 1.71786786e-02f, 1.21856622e-02f, - 7.10604036e-03f, 1.98245601e-03f, -3.14218264e-03f, -8.22510738e-03f, - -1.32239734e-02f, -1.80973012e-02f, -2.28047577e-02f, -2.73075056e-02f, - -3.15685177e-02f, -3.55529069e-02f, -3.92281902e-02f, -4.25645528e-02f, - -4.55351013e-02f, -4.81160565e-02f, -5.02870006e-02f, -5.20309689e-02f, - -5.33346001e-02f, -5.41883074e-02f, -5.45862315e-02f, -5.45263860e-02f, - -5.40105902e-02f, -5.30444830e-02f, -5.16374652e-02f, -4.98025685e-02f, - -4.75564009e-02f, -4.49189535e-02f, -4.19134372e-02f, -3.85660549e-02f, - -3.49058685e-02f, -3.09643739e-02f, -2.67754233e-02f, -2.23747988e-02f, - -1.77999754e-02f, -1.30897755e-02f, -8.28403078e-03f, -3.42329111e-03f, - 1.45156003e-03f, 6.29963147e-03f, 1.10803882e-02f, 1.57539476e-02f, - 2.02814494e-02f, 2.46253752e-02f, 2.87498179e-02f, 3.26208554e-02f, - 3.62067406e-02f, 3.94782545e-02f, 4.24088471e-02f, 4.49749385e-02f, - 4.71560491e-02f, 4.89349963e-02f, 5.02980346e-02f, 5.12349025e-02f, - 5.17389769e-02f, 5.18073110e-02f, 5.14405369e-02f, 5.06430401e-02f, - 4.94227353e-02f, 4.77911027e-02f, 4.57630135e-02f, 4.33566667e-02f, - 4.05933433e-02f, 3.74973028e-02f, 3.40955069e-02f, 3.04173973e-02f, - 2.64947139e-02f, 2.23610806e-02f, 1.80518625e-02f, 1.36037814e-02f, - 9.05461294e-03f, 4.44289505e-03f, -1.92440179e-04f, -4.81233436e-03f, - -9.37799893e-03f, -1.38512169e-02f, -1.81946099e-02f, -2.23720431e-02f, - -2.63488613e-02f, -3.00921879e-02f, -3.35711998e-02f, -3.67574146e-02f, - -3.96248452e-02f, -4.21502967e-02f, -4.43135012e-02f, -4.60972960e-02f, - -4.74877737e-02f, -4.84743534e-02f, -4.90498864e-02f, -4.92107432e-02f, - -4.89567473e-02f, -4.82912269e-02f, -4.72210048e-02f, -4.57562287e-02f, - -4.39103965e-02f, -4.17001535e-02f, -3.91451580e-02f, -3.62679524e-02f, - -3.30936915e-02f, -2.96499930e-02f, -2.59666361e-02f, -2.20753766e-02f, - -1.80096001e-02f, -1.38040903e-02f, -9.49470398e-03f, -5.11805324e-03f, - -7.11263614e-04f, 3.68845212e-03f, 8.04397633e-03f, 1.23187361e-02f, - 1.64768878e-02f, 2.04837107e-02f, 2.43058243e-02f, 2.79115316e-02f, - 3.12710307e-02f, 3.43566819e-02f, 3.71432406e-02f, 3.96080600e-02f, - 4.17312589e-02f, 4.34958998e-02f, 4.48881324e-02f, 4.58972728e-02f, - 4.65159011e-02f, 4.67399486e-02f, 4.65686301e-02f, 4.60045573e-02f, - 4.50536126e-02f, 4.37249572e-02f, 4.20309099e-02f, 3.99868149e-02f, - 3.76109658e-02f, 3.49244023e-02f, 3.19507068e-02f, 2.87158399e-02f, - 2.52478981e-02f, 2.15768722e-02f, 1.77343611e-02f, 1.37533396e-02f, - 9.66788343e-03f, 5.51281005e-03f, 1.32343973e-03f, -2.86465940e-03f, - -7.01611627e-03f, -1.10959241e-02f, -1.50697748e-02f, -1.89043738e-02f, - -2.25676833e-02f, -2.60292100e-02f, -2.92602731e-02f, -3.22341600e-02f, - -3.49264870e-02f, -3.73152937e-02f, -3.93812165e-02f, -4.11077217e-02f, - -4.24811803e-02f, -4.34909828e-02f, -4.41296386e-02f, -4.43928125e-02f, - -4.42793688e-02f, -4.37913678e-02f, -4.29339923e-02f, -4.17156128e-02f, - -4.01475749e-02f, -3.82441650e-02f, -3.60224960e-02f, -3.35022818e-02f, - -3.07057642e-02f, -2.76574300e-02f, -2.43838133e-02f, -2.09133302e-02f, - -1.72759525e-02f, -1.35029658e-02f, -9.62678336e-03f, -5.68051801e-03f, - -1.69782450e-03f, 2.28741709e-03f, 6.24142092e-03f, 1.01307640e-02f, - 1.39226541e-02f, 1.75852206e-02f, 2.10877784e-02f, 2.44011197e-02f, - 2.74976630e-02f, 3.03517861e-02f, 3.29399891e-02f, 3.52410789e-02f, - 3.72364000e-02f, 3.89098736e-02f, 4.02482712e-02f, 4.12411943e-02f, - 4.18812295e-02f, 4.21639571e-02f, 4.20880553e-02f, 4.16551548e-02f, - 4.08700173e-02f, 3.97403235e-02f, 3.82766485e-02f, 3.64924037e-02f, - 3.44036670e-02f, 3.20290679e-02f, 2.93895781e-02f, 2.65084084e-02f, - 2.34107263e-02f, 2.01234514e-02f, 1.66750736e-02f, 1.30953481e-02f, - 9.41506492e-03f, 5.66577314e-03f, 1.87953721e-03f, -1.91138340e-03f, - -5.67474970e-03f, -9.37866958e-03f, -1.29918571e-02f, -1.64838496e-02f, - -1.98253468e-02f, -2.29883663e-02f, -2.59465660e-02f, -2.86753884e-02f, - -3.11523087e-02f, -3.33570024e-02f, -3.52715334e-02f, -3.68804752e-02f, - -3.81710220e-02f, -3.91331489e-02f, -3.97596215e-02f, -4.00461207e-02f, - -3.99911716e-02f, -3.95962751e-02f, -3.88657519e-02f, -3.78068352e-02f, - -3.64294686e-02f, -3.47463159e-02f, -3.27725952e-02f, -3.05259482e-02f, - -2.80262873e-02f, -2.52956368e-02f, -2.23579079e-02f, -1.92387018e-02f, - -1.59651184e-02f, -1.25654653e-02f, -9.06903728e-03f, -5.50589383e-03f, - -1.90658612e-03f, 1.69819644e-03f, 5.27767591e-03f, 8.80149662e-03f, - 1.22398002e-02f, 1.55635605e-02f, 1.87448095e-02f, 2.17568784e-02f, - 2.45746067e-02f, 2.71745749e-02f, 2.95352660e-02f, 3.16372730e-02f, - 3.34634806e-02f, 3.49991083e-02f, 3.62319569e-02f, 3.71524003e-02f, - 3.77535229e-02f, 3.80311360e-02f, 3.79838595e-02f, 3.76130115e-02f, - 3.69227353e-02f, 3.59198588e-02f, 3.46138556e-02f, 3.30167422e-02f, - 3.11429955e-02f, 2.90094255e-02f, 2.66349737e-02f, 2.40406079e-02f, - 2.12490922e-02f, 1.82848227e-02f, 1.51735699e-02f, 1.19423378e-02f, - 8.61899874e-03f, 5.23220696e-03f, 1.81100681e-03f, -1.61529876e-03f, - -5.01747104e-03f, -8.36657631e-03f, -1.16341674e-02f, -1.47926336e-02f, - -1.78153429e-02f, -2.06769168e-02f, -2.33533934e-02f, -2.58225130e-02f, - -2.80638107e-02f, -3.00588248e-02f, -3.17912703e-02f, -3.32471461e-02f, - -3.44148496e-02f, -3.52852564e-02f, -3.58518459e-02f, -3.61106776e-02f, - -3.60604528e-02f, -3.57025239e-02f, -3.50408687e-02f, -3.40820566e-02f, - -3.28351332e-02f, -3.13116305e-02f, -2.95253828e-02f, -2.74924261e-02f, - -2.52308529e-02f, -2.27606768e-02f, -2.01036311e-02f, -1.72829328e-02f, - -1.43232049e-02f, -1.12501406e-02f, -8.09033460e-03f, -4.87102079e-03f, - -1.61991454e-03f, 1.63511391e-03f, 4.86626363e-03f, 8.04597224e-03f, - 1.11472224e-02f, 1.41437409e-02f, 1.70102606e-02f, 1.97226336e-02f, - 2.22581567e-02f, 2.45957058e-02f, 2.67158611e-02f, 2.86011679e-02f, - 3.02362198e-02f, 3.16077891e-02f, 3.27049359e-02f, 3.35191077e-02f, - 3.40442066e-02f, 3.42766034e-02f, 3.42151913e-02f, 3.38613937e-02f, - 3.32191151e-02f, 3.22947056e-02f, 3.10969473e-02f, 2.96368987e-02f, - 2.79278426e-02f, 2.59851513e-02f, 2.38261551e-02f, 2.14699783e-02f, - 1.89373695e-02f, 1.62505430e-02f, 1.34329466e-02f, 1.05090853e-02f, - 7.50431274e-03f, 4.44457496e-03f, 1.35623378e-03f, -1.73420401e-03f, - -4.80027676e-03f, -7.81581580e-03f, -1.07551646e-02f, -1.35933944e-02f, - -1.63065125e-02f, -1.88716732e-02f, -2.12673829e-02f, -2.34736329e-02f, - -2.54721072e-02f, -2.72463321e-02f, -2.87818120e-02f, -3.00661165e-02f, - -3.10890008e-02f, -3.18425307e-02f, -3.23210635e-02f, -3.25213258e-02f, - -3.24424720e-02f, -3.20860357e-02f, -3.14558962e-02f, -3.05582934e-02f, - -2.94017600e-02f, -2.79969684e-02f, -2.63567172e-02f, -2.44957911e-02f, - -2.24308005e-02f, -2.01800752e-02f, -1.77634589e-02f, -1.52021705e-02f, - -1.25186056e-02f, -9.73612725e-03f, -6.87889048e-03f, -3.97162201e-03f, - -1.03936748e-03f, 1.89264412e-03f, 4.79928751e-03f, 7.65573326e-03f, - 1.04376485e-02f, 1.31214182e-02f, 1.56843167e-02f, 1.81047816e-02f, - 2.03624722e-02f, 2.24385491e-02f, 2.43157747e-02f, 2.59786613e-02f, - 2.74136059e-02f, 2.86090270e-02f, 2.95554085e-02f, 3.02453933e-02f, - 3.06738695e-02f, 3.08379709e-02f, 3.07371080e-02f, 3.03729625e-02f, - 2.97494799e-02f, 2.88728239e-02f, 2.77512742e-02f, 2.63952326e-02f, - 2.48170551e-02f, 2.30309672e-02f, 2.10529469e-02f, 1.89005531e-02f, - 1.65928079e-02f, 1.41499753e-02f, 1.15934547e-02f, 8.94553439e-03f, - 6.22921565e-03f, 3.46802456e-03f, 6.85807660e-04f, -2.09351762e-03f, - -4.84607801e-03f, -7.54834950e-03f, -1.01772976e-02f, -1.27105702e-02f, - -1.51267312e-02f, -1.74054170e-02f, -1.95274752e-02f, -2.14752102e-02f, - -2.32324132e-02f, -2.47846112e-02f, -2.61191248e-02f, -2.72251852e-02f, - -2.80939857e-02f, -2.87188307e-02f, -2.90951296e-02f, -2.92204205e-02f, - -2.90944185e-02f, -2.87189899e-02f, -2.80981757e-02f, -2.72380366e-02f, - -2.61467465e-02f, -2.48343900e-02f, -2.33129547e-02f, -2.15961399e-02f, - -1.96993447e-02f, -1.76394103e-02f, -1.54345417e-02f, -1.31041691e-02f, - -1.06686731e-02f, -8.14934589e-03f, -5.56806233e-03f, -2.94722846e-03f, - -3.09480282e-04f, 2.32248018e-03f, 4.92603655e-03f, 7.47890755e-03f, - 9.95933342e-03f, 1.23462104e-02f, 1.46193259e-02f, 1.67595114e-02f, - 1.87488002e-02f, 2.05705588e-02f, 2.22096708e-02f, 2.36526409e-02f, - 2.48876669e-02f, 2.59048208e-02f, 2.66960464e-02f, 2.72552501e-02f, - 2.75783775e-02f, 2.76634150e-02f, 2.75103717e-02f, 2.71213402e-02f, - 2.65004170e-02f, 2.56536729e-02f, 2.45891352e-02f, 2.33166362e-02f, - 2.18477931e-02f, 2.01958648e-02f, 1.83756573e-02f, 1.64033346e-02f, - 1.42963876e-02f, 1.20733364e-02f, 9.75368948e-03f, 7.35772256e-03f, - 4.90625681e-03f, 2.42058549e-03f, -7.78123459e-05f, -2.56740681e-03f, - -5.02679577e-03f, -7.43492567e-03f, -9.77127015e-03f, -1.20159192e-02f, - -1.41498747e-02f, -1.61551576e-02f, -1.80148982e-02f, -1.97135784e-02f, - -2.12371093e-02f, -2.25729462e-02f, -2.37102058e-02f, -2.46397233e-02f, - -2.53542230e-02f, -2.58482111e-02f, -2.61181554e-02f, -2.61624917e-02f, - -2.59815513e-02f, -2.55776241e-02f, -2.49549139e-02f, -2.41194964e-02f, - -2.30792716e-02f, -2.18438342e-02f, -2.04244587e-02f, -1.88339919e-02f, - -1.70866610e-02f, -1.51980228e-02f, -1.31848099e-02f, -1.10647537e-02f, - -8.85646007e-03f, -6.57922197e-03f, -4.25289625e-03f, -1.89763580e-03f, - 4.66153419e-04f, 2.81808458e-03f, 5.13797202e-03f, 7.40592877e-03f, - 9.60259119e-03f, 1.17092799e-02f, 1.37081595e-02f, 1.55823451e-02f, - 1.73161398e-02f, 1.88950442e-02f, 2.03059901e-02f, 2.15373720e-02f, - 2.25791706e-02f, 2.34230473e-02f, 2.40623953e-02f, 2.44923608e-02f, - 2.47099481e-02f, 2.47139948e-02f, 2.45051449e-02f, 2.40859346e-02f, - 2.34606570e-02f, 2.26353881e-02f, 2.16179088e-02f, 2.04175981e-02f, - 1.90454439e-02f, 1.75138196e-02f, 1.58364356e-02f, 1.40282761e-02f, - 1.21053168e-02f, 1.00845358e-02f, 7.98365780e-03f, 5.82106543e-03f, - 3.61560367e-03f, 1.38641929e-03f, -8.47218784e-04f, -3.06599466e-03f, - -5.25087774e-03f, -7.38313991e-03f, -9.44455890e-03f, -1.14176395e-02f, - -1.32856363e-02f, -1.50328238e-02f, -1.66445337e-02f, -1.81073376e-02f, - -1.94091057e-02f, -2.05391909e-02f, -2.14883924e-02f, -2.22491632e-02f, - -2.28155723e-02f, -2.31833780e-02f, -2.33501014e-02f, -2.33149599e-02f, - -2.30789680e-02f, -2.26448166e-02f, -2.20169170e-02f, -2.12013570e-02f, - -2.02057946e-02f, -1.90394186e-02f, -1.77128837e-02f, -1.62381452e-02f, - -1.46284655e-02f, -1.28981349e-02f, -1.10625252e-02f, -9.13782192e-03f, - -7.14089458e-03f, -5.08925289e-03f, -3.00076224e-03f, -8.93563929e-04f, - 1.21412493e-03f, 3.30408095e-03f, 5.35835840e-03f, 7.35936068e-03f, - 9.29000657e-03f, 1.11338879e-02f, 1.28753714e-02f, 1.44998123e-02f, - 1.59935874e-02f, 1.73442409e-02f, 1.85406270e-02f, 1.95729684e-02f, - 2.04328853e-02f, 2.11135770e-02f, 2.16097716e-02f, 2.19178390e-02f, - 2.20357483e-02f, 2.19631551e-02f, 2.17013288e-02f, 2.12531940e-02f, - 2.06232436e-02f, 1.98175530e-02f, 1.88436810e-02f, 1.77106134e-02f, - 1.64286792e-02f, 1.50094525e-02f, 1.34656661e-02f, 1.18110672e-02f, - 1.00603222e-02f, 8.22887936e-03f, 6.33283241e-03f, 4.38874672e-03f, - 2.41360251e-03f, 4.24522718e-04f, -1.56124462e-03f, -3.52657933e-03f, - -5.45459389e-03f, -7.32876872e-03f, -9.13310880e-03f, -1.08522951e-02f, - -1.24717730e-02f, -1.39778955e-02f, -1.53580868e-02f, -1.66008540e-02f, - -1.76959435e-02f, -1.86344131e-02f, -1.94087423e-02f, -2.00127825e-02f, - -2.04419305e-02f, -2.06931003e-02f, -2.07647439e-02f, -2.06568662e-02f, - -2.03710410e-02f, -1.99103562e-02f, -1.92794106e-02f, -1.84842504e-02f, - -1.75323167e-02f, -1.64323977e-02f, -1.51944972e-02f, -1.38297917e-02f, - -1.23505473e-02f, -1.07699177e-02f, -9.10190890e-03f, -7.36125336e-03f, - -5.56322740e-03f, -3.72358380e-03f, -1.85834629e-03f, 1.62797268e-05f, - 1.88406706e-03f, 3.72890929e-03f, 5.53494881e-03f, 7.28672421e-03f, - 8.96929998e-03f, 1.05683732e-02f, 1.20704430e-02f, 1.34628666e-02f, - 1.47340226e-02f, 1.58733625e-02f, 1.68714835e-02f, 1.77202945e-02f, - 1.84129995e-02f, 1.89441562e-02f, 1.93097640e-02f, 1.95072568e-02f, - 1.95355280e-02f, 1.93949451e-02f, 1.90873200e-02f, 1.86159281e-02f, - 1.79854186e-02f, 1.72018134e-02f, 1.62724351e-02f, 1.52058602e-02f, - 1.40117601e-02f, 1.27009429e-02f, 1.12851595e-02f, 9.77701852e-03f, - 8.18987013e-03f, 6.53774227e-03f, 4.83514325e-03f, 3.09695569e-03f, - 1.33834551e-03f, -4.25417736e-04f, -2.17907719e-03f, -3.90750635e-03f, - -5.59584405e-03f, -7.22967588e-03f, -8.79504135e-03f, -1.02786946e-02f, - -1.16680841e-02f, -1.29515500e-02f, -1.41183852e-02f, -1.51589335e-02f, - -1.60646521e-02f, -1.68282128e-02f, -1.74435295e-02f, -1.79058268e-02f, - -1.82116669e-02f, -1.83589783e-02f, -1.83470786e-02f, -1.81766429e-02f, - -1.78497359e-02f, -1.73697470e-02f, -1.67414052e-02f, -1.59706714e-02f, - -1.50647456e-02f, -1.40319474e-02f, -1.28817007e-02f, -1.16243487e-02f, - -1.02711700e-02f, -8.83423668e-03f, -7.32626993e-03f, -5.76055872e-03f, - -4.15088347e-03f, -2.51130684e-03f, -8.56121139e-04f, 8.00308080e-04f, - 2.44364656e-03f, 4.05971160e-03f, 5.63466432e-03f, 7.15502326e-03f, - 8.60784346e-03f, 9.98079276e-03f, 1.12623356e-02f, 1.24416991e-02f, - 1.35090584e-02f, 1.44556378e-02f, 1.52736545e-02f, 1.59565533e-02f, - 1.64989049e-02f, 1.68965640e-02f, 1.71466261e-02f, 1.72474699e-02f, - 1.71988001e-02f, 1.70016009e-02f, 1.66581092e-02f, 1.61718740e-02f, - 1.55476166e-02f, 1.47912749e-02f, 1.39098794e-02f, 1.29115146e-02f, - 1.18052872e-02f, 1.06011344e-02f, 9.30987887e-03f, 7.94299705e-03f, - 6.51261294e-03f, 5.03132373e-03f, 3.51216505e-03f, 1.96838522e-03f, - 4.13468447e-04f, -1.13910857e-03f, -2.67593573e-03f, -4.18373241e-03f, - -5.64960835e-03f, -7.06102465e-03f, -8.40597413e-03f, -9.67306954e-03f, - -1.08516566e-02f, -1.19318601e-02f, -1.29047339e-02f, -1.37622291e-02f, - -1.44974073e-02f, -1.51043649e-02f, -1.55783142e-02f, -1.59157002e-02f, - -1.61141039e-02f, -1.61723481e-02f, -1.60904670e-02f, -1.58696900e-02f, - -1.55124969e-02f, -1.50224681e-02f, -1.44043694e-02f, -1.36640534e-02f, - -1.28083790e-02f, -1.18451989e-02f, -1.07832734e-02f, -9.63216695e-03f, - -8.40217979e-03f, -7.10427993e-03f, -5.74993358e-03f, -4.35111118e-03f, - -2.92006835e-03f, -1.46932453e-03f, -1.15175859e-05f, 1.44071303e-03f, - 2.87483552e-03f, 4.27848214e-03f, 5.63962178e-03f, 6.94667140e-03f, - 8.18849175e-03f, 9.35461319e-03f, 1.04352259e-02f, 1.14213169e-02f, - 1.23047082e-02f, 1.30781646e-02f, 1.37354286e-02f, 1.42712333e-02f, - 1.46814453e-02f, 1.49630117e-02f, 1.51139756e-02f, 1.51335483e-02f, - 1.50220835e-02f, 1.47810620e-02f, 1.44130827e-02f, 1.39218007e-02f, - 1.33119894e-02f, 1.25893957e-02f, 1.17606823e-02f, 1.08334768e-02f, - 9.81618503e-03f, 8.71796809e-03f, 7.54864594e-03f, 6.31863400e-03f, - 5.03883063e-03f, 3.72048341e-03f, 2.37517536e-03f, 1.01468348e-03f, - -3.49196130e-04f, -1.70461937e-03f, -3.03987673e-03f, -4.34350332e-03f, - -5.60433339e-03f, -6.81160996e-03f, -7.95512104e-03f, -9.02519096e-03f, - -1.00128542e-02f, -1.09099085e-02f, -1.17089124e-02f, -1.24033803e-02f, - -1.29876865e-02f, -1.34572282e-02f, -1.38083913e-02f, -1.40386233e-02f, - -1.41463921e-02f, -1.41312813e-02f, -1.39939047e-02f, -1.37359379e-02f, - -1.33601191e-02f, -1.28701544e-02f, -1.22707533e-02f, -1.15675607e-02f, - -1.07670757e-02f, -9.87660975e-03f, -8.90426422e-03f, -7.85876691e-03f, - -6.74947754e-03f, -5.58627059e-03f, -4.37942163e-03f, -3.13956447e-03f, - -1.87757286e-03f, -6.04471683e-04f, 6.68687153e-04f, 1.93086297e-03f, - 3.17116571e-03f, 4.37896308e-03f, 5.54391133e-03f, 6.65608640e-03f, - 7.70610236e-03f, 8.68508463e-03f, 9.58487893e-03f, 1.03979809e-02f, - 1.11177300e-02f, 1.17382727e-02f, 1.22546165e-02f, 1.26627517e-02f, - 1.29595607e-02f, 1.31429469e-02f, 1.32117696e-02f, 1.31659380e-02f, - 1.30062790e-02f, 1.27346679e-02f, 1.23539183e-02f, 1.18677784e-02f, - 1.12808819e-02f, 1.05987296e-02f, 9.82765005e-03f, 8.97466282e-03f, - 8.04750011e-03f, 7.05450097e-03f, 6.00455814e-03f, 4.90697069e-03f, - 3.77147007e-03f, 2.60803032e-03f, 1.42685205e-03f, 2.38252620e-04f, - -9.47456984e-04f, -2.12001557e-03f, -3.26930604e-03f, -4.38549571e-03f, - -5.45903303e-03f, -6.48080118e-03f, -7.44217435e-03f, -8.33506789e-03f, - -9.15202991e-03f, -9.88632138e-03f, -1.05319082e-02f, -1.10835810e-02f, - -1.15369475e-02f, -1.18884995e-02f, -1.21356183e-02f, -1.22765935e-02f, - -1.23106741e-02f, -1.22379903e-02f, -1.20596309e-02f, -1.17775907e-02f, - -1.13947504e-02f, -1.09148490e-02f, -1.03424524e-02f, -9.68292516e-03f, - -8.94233597e-03f, -8.12744125e-03f, -7.24562722e-03f, -6.30481777e-03f, - -5.31340458e-03f, -4.28019210e-03f, -3.21432388e-03f, -2.12514582e-03f, - -1.02222518e-03f, 8.48402655e-05f, 1.18644055e-03f, 2.27305998e-03f, - 3.33534080e-03f, 4.36417880e-03f, 5.35082505e-03f, 6.28688841e-03f, - 7.16448437e-03f, 7.97626556e-03f, 8.71547416e-03f, 9.37602127e-03f, - 9.95252480e-03f, 1.04403423e-02f, 1.08356349e-02f, 1.11353709e-02f, - 1.13373727e-02f, 1.14403200e-02f, 1.14437206e-02f, 1.13479929e-02f, - 1.11544081e-02f, 1.08650423e-02f, 1.04828191e-02f, 1.00114740e-02f, - 9.45546226e-03f, 8.81995782e-03f, 8.11083408e-03f, 7.33454940e-03f, - 6.49810508e-03f, 5.60902655e-03f, 4.67524423e-03f, 3.70504943e-03f, - 2.70698716e-03f, 1.68984103e-03f, 6.62511289e-04f, -3.66063830e-04f, - -1.38696193e-03f, -2.39137620e-03f, -3.37067114e-03f, -4.31647732e-03f, - -5.22074273e-03f, -6.07581700e-03f, -6.87449892e-03f, -7.61012299e-03f, - -8.27659902e-03f, -8.86843891e-03f, -9.38086115e-03f, -9.80976718e-03f, - -1.01518071e-02f, -1.04044062e-02f, -1.05657705e-02f, -1.06349047e-02f, - -1.06116070e-02f, -1.04965065e-02f, -1.02910004e-02f, -9.99726062e-03f, - -9.61823783e-03f, -9.15759359e-03f, -8.61966847e-03f, -8.00948048e-03f, - -7.33263803e-03f, -6.59530070e-03f, -5.80411820e-03f, -4.96619474e-03f, - -4.08901524e-03f, -3.18032417e-03f, -2.24815040e-03f, -1.30069573e-03f, - -3.46202014e-04f, 6.07005678e-04f, 1.55067386e-03f, 2.47666319e-03f, - 3.37705799e-03f, 4.24414413e-03f, 5.07055312e-03f, 5.84933606e-03f, - 6.57394125e-03f, 7.23833180e-03f, 7.83701102e-03f, 8.36512776e-03f, - 8.81838829e-03f, 9.19320845e-03f, 9.48670950e-03f, 9.69671225e-03f, - 9.82176780e-03f, 9.86117239e-03f, 9.81500060e-03f, 9.68401019e-03f, - 9.46973376e-03f, 9.17440866e-03f, 8.80096882e-03f, 8.35299798e-03f, - 7.83470675e-03f, 7.25092919e-03f, 6.60700863e-03f, 5.90880044e-03f, - 5.16260232e-03f, 4.37511499e-03f, 3.55333719e-03f, 2.70453571e-03f, - 1.83621654e-03f, 9.55971892e-04f, 7.14941081e-05f, -8.09546859e-04f, - -1.67949997e-03f, -2.53091770e-03f, -3.35648519e-03f, -4.14916820e-03f, - -4.90226718e-03f, -5.60941116e-03f, -6.26471868e-03f, -6.86271228e-03f, - -7.39850829e-03f, -7.86773441e-03f, -8.26664253e-03f, -8.59209655e-03f, - -8.84162491e-03f, -9.01340035e-03f, -9.10631046e-03f, -9.11992414e-03f, - -9.05447739e-03f, -8.91090648e-03f, -8.69081572e-03f, -8.39650138e-03f, - -8.03081656e-03f, -7.59728914e-03f, -7.09998516e-03f, -6.54351866e-03f, - -5.93297162e-03f, -5.27391890e-03f, -4.57225558e-03f, -3.83429395e-03f, - -3.06659425e-03f, -2.27593987e-03f, -1.46929411e-03f, -6.53717718e-04f, - 1.63676664e-04f, 9.75799545e-04f, 1.77563501e-03f, 2.55633177e-03f, - 3.31116336e-03f, 4.03378434e-03f, 4.71802893e-03f, 5.35816666e-03f, - 5.94887641e-03f, 6.48526029e-03f, 6.96292147e-03f, 7.37801172e-03f, - 7.72723246e-03f, 8.00785932e-03f, 8.21780315e-03f, 8.35557459e-03f, - 8.42032149e-03f, 8.41183281e-03f, 8.33054181e-03f, 8.17747564e-03f, - 7.95433786e-03f, 7.66336298e-03f, 7.30743051e-03f, 6.88990453e-03f, - 6.41471602e-03f, 5.88625192e-03f, 5.30935851e-03f, 4.68925554e-03f, - 4.03151761e-03f, 3.34203719e-03f, 2.62693694e-03f, 1.89253809e-03f, - 1.14528552e-03f, 3.91735329e-04f, -3.61576948e-04f, -1.10810138e-03f, - -1.84142652e-03f, -2.55525216e-03f, -3.24348047e-03f, -3.90028979e-03f, - -4.52012845e-03f, -5.09781875e-03f, -5.62855343e-03f, -6.10796080e-03f, - -6.53213670e-03f, -6.89768993e-03f, -7.20172730e-03f, -7.44190362e-03f, - -7.61648070e-03f, -7.72424208e-03f, -7.76459042e-03f, -7.73750201e-03f, - -7.64355603e-03f, -7.48387832e-03f, -7.26015909e-03f, -6.97470458e-03f, - -6.63023185e-03f, -6.23005432e-03f, -5.77789750e-03f, -5.27794195e-03f, - -4.73473676e-03f, -4.15323407e-03f, -3.53862105e-03f, -2.89641236e-03f, - -2.23229690e-03f, -1.55213945e-03f, -8.61924039e-04f, -1.67646019e-04f, - 5.24634726e-04f, 1.20893687e-03f, 1.87936876e-03f, 2.53020242e-03f, - 3.15589017e-03f, 3.75112921e-03f, 4.31093227e-03f, 4.83063434e-03f, - 5.30589437e-03f, 5.73285894e-03f, 6.10805341e-03f, 6.42847724e-03f, - 6.69165721e-03f, 6.89558077e-03f, 7.03876861e-03f, 7.12031596e-03f, - 7.13979497e-03f, 7.09737416e-03f, 6.99372292e-03f, 6.83002861e-03f, - 6.60805053e-03f, 6.32997518e-03f, 5.99849919e-03f, 5.61676889e-03f, - 5.18833988e-03f, 4.71716487e-03f, 4.20753440e-03f, 3.66405499e-03f, - 3.09162548e-03f, 2.49532169e-03f, 1.88042982e-03f, 1.25240798e-03f, - 6.16721933e-04f, -2.10678481e-05f, -6.55427849e-04f, -1.28088666e-03f, - -1.89206206e-03f, -2.48374616e-03f, -3.05092627e-03f, -3.58876931e-03f, - -4.09281312e-03f, -4.55886002e-03f, -4.98304465e-03f, -5.36192844e-03f, - -5.69246645e-03f, -5.97202847e-03f, -6.19845814e-03f, -6.37007698e-03f, - -6.48568424e-03f, -6.54455208e-03f, -6.54646884e-03f, -6.49172972e-03f, - -6.38107878e-03f, -6.21576754e-03f, -5.99751984e-03f, -5.72848662e-03f, - -5.41128232e-03f, -5.04886935e-03f, -4.64464895e-03f, -4.20232731e-03f, - -3.72592908e-03f, -3.21976323e-03f, -2.68837079e-03f, -2.13647137e-03f, - -1.56895819e-03f, -9.90826814e-04f, -4.07137101e-04f, 1.77034128e-04f, - 7.56614045e-04f, 1.32662280e-03f, 1.88216158e-03f, 2.41851689e-03f, - 2.93111653e-03f, 3.41567923e-03f, 3.86812645e-03f, 4.28473921e-03f, - 4.66208309e-03f, 4.99709627e-03f, 5.28710944e-03f, 5.52983700e-03f, - 5.72344791e-03f, 5.86650915e-03f, 5.95806206e-03f, 5.99756713e-03f, - 5.98498044e-03f, 5.92067495e-03f, 5.80547493e-03f, 5.64067393e-03f, - 5.42792578e-03f, 5.16935371e-03f, 4.86743833e-03f, 4.52499899e-03f, - 4.14524387e-03f, 3.73165445e-03f, 3.28798006e-03f, 2.81822731e-03f, - 2.32660442e-03f, 1.81747333e-03f, 1.29535738e-03f, 7.64811514e-04f, - 2.30499750e-04f, -3.02941233e-04f, -8.30904036e-04f, -1.34884935e-03f, - -1.85232914e-03f, -2.33709184e-03f, -2.79902951e-03f, -3.23425712e-03f, - -3.63917195e-03f, -4.01042034e-03f, -4.34498121e-03f, -4.64014771e-03f, - -4.89357867e-03f, -5.10333096e-03f, -5.26778390e-03f, -5.38579823e-03f, - -5.45658844e-03f, -5.47979294e-03f, -5.45548594e-03f, -5.38411670e-03f, - -5.26658130e-03f, -5.10413250e-03f, -4.89843368e-03f, -4.65147045e-03f, - -4.36565944e-03f, -4.04362854e-03f, -3.68839513e-03f, -3.30322131e-03f, - -2.89157726e-03f, -2.45718506e-03f, -2.00393056e-03f, -1.53582784e-03f, - -1.05702424e-03f, -5.71697826e-04f, -8.41072148e-05f, 4.01536814e-04f, - 8.81019836e-04f, 1.35024965e-03f, 1.80521520e-03f, 2.24205417e-03f, - 2.65710363e-03f, 3.04687963e-03f, 3.40815186e-03f, 3.73795216e-03f, - 4.03361599e-03f, 4.29276556e-03f, 4.51335779e-03f, 4.69370830e-03f, - 4.83246574e-03f, 4.92869877e-03f, 4.98175710e-03f, 4.99146876e-03f, - 4.95797374e-03f, 4.88180168e-03f, 4.76385107e-03f, 4.60539108e-03f, - 4.40799947e-03f, 4.17361084e-03f, 3.90446439e-03f, 3.60306851e-03f, - 3.27223243e-03f, 2.91495118e-03f, 2.53450126e-03f, 2.13428258e-03f, - 1.71785917e-03f, 1.28894440e-03f, 8.51307681e-04f, 4.08783855e-04f, - -3.47765574e-05f, -4.75529382e-04f, -9.09669659e-04f, -1.33349225e-03f, - -1.74339270e-03f, -2.13590830e-03f, -2.50772263e-03f, -2.85576594e-03f, - -3.17714507e-03f, -3.46924367e-03f, -3.72969621e-03f, -3.95645033e-03f, - -4.14770845e-03f, -4.30203167e-03f, -4.41831180e-03f, -4.49574815e-03f, - -4.53388522e-03f, -4.53264318e-03f, -4.49224360e-03f, -4.41325534e-03f, - -4.29659437e-03f, -4.14348253e-03f, -3.95545723e-03f, -3.73433539e-03f, - -3.48222611e-03f, -3.20148621e-03f, -2.89470832e-03f, -2.56468722e-03f, - -2.21442685e-03f, -1.84705410e-03f, -1.46585762e-03f, -1.07420242e-03f, - -6.75541964e-04f, -2.73362726e-04f, 1.28857758e-04f, 5.27625747e-04f, - 9.19505126e-04f, 1.30117518e-03f, 1.66938725e-03f, 2.02102017e-03f, - 2.35316570e-03f, 2.66303554e-03f, 2.94810484e-03f, 3.20604525e-03f, - 3.43477820e-03f, 3.63252281e-03f, 3.79773367e-03f, 3.92918244e-03f, - 4.02592670e-03f, 4.08733570e-03f, 4.11309343e-03f, 4.10318081e-03f, - 4.05789653e-03f, 3.97783502e-03f, 3.86391767e-03f, 3.71728516e-03f, - 3.53944503e-03f, 3.33208164e-03f, 3.09717361e-03f, 2.83693803e-03f, - 2.55373764e-03f, 2.25016474e-03f, 1.92898540e-03f, 1.59304145e-03f, - 1.24535047e-03f, 8.88983788e-04f, 5.27054162e-04f, 1.62739254e-04f, - -2.00809162e-04f, -5.60458110e-04f, -9.13122723e-04f, -1.25578987e-03f, - -1.58556683e-03f, -1.89968711e-03f, -2.19553534e-03f, -2.47065402e-03f, - -2.72279518e-03f, -2.94992092e-03f, -3.15023183e-03f, -3.32214990e-03f, - -3.46435451e-03f, -3.57581566e-03f, -3.65574132e-03f, -3.70363730e-03f, - -3.71929064e-03f, -3.70275212e-03f, -3.65435442e-03f, -3.57474125e-03f, - -3.46473848e-03f, -3.32552683e-03f, -3.15846353e-03f, -2.96515654e-03f, - -2.74746347e-03f, -2.50737272e-03f, -2.24712016e-03f, -1.96906951e-03f, - -1.67571539e-03f, -1.36971428e-03f, -1.05376024e-03f, -7.30644072e-04f, - -4.03206799e-04f, -7.42995897e-05f, 2.53245135e-04f, 5.76588008e-04f, - 8.92968397e-04f, 1.19970438e-03f, 1.49420457e-03f, 1.77402045e-03f, - 2.03680128e-03f, 2.28039302e-03f, 2.50282909e-03f, 2.70229005e-03f, - 2.87722528e-03f, 3.02626345e-03f, 3.14829228e-03f, 3.24241372e-03f, - 3.30798804e-03f, 3.34463320e-03f, 3.35222123e-03f, 3.33084720e-03f, - 3.28088558e-03f, 3.20297004e-03f, 3.09790204e-03f, 2.96678891e-03f, - 2.81092150e-03f, 2.63179675e-03f, 2.43109407e-03f, 2.21069486e-03f, - 1.97261876e-03f, 1.71903709e-03f, 1.45221718e-03f, 1.17455819e-03f, - 8.88520565e-04f, 5.96615677e-04f, 3.01413631e-04f, 5.46569794e-06f, - -2.88648335e-04f, -5.78440455e-04f, -8.61415446e-04f, -1.13516540e-03f, - -1.39741163e-03f, -1.64595773e-03f, -1.87874945e-03f, -2.09387292e-03f, - -2.28959324e-03f, -2.46433942e-03f, -2.61675008e-03f, -2.74564089e-03f, - -2.85005436e-03f, -2.92925341e-03f, -2.98270791e-03f, -3.01012152e-03f, - -3.01144172e-03f, -2.98680121e-03f, -2.93658715e-03f, -2.86141187e-03f, - -2.76205629e-03f, -2.63955281e-03f, -2.49510204e-03f, -2.33009109e-03f, - -2.14606014e-03f, -1.94473859e-03f, -1.72796009e-03f, -1.49770401e-03f, - -1.25602813e-03f, -1.00509543e-03f, -7.47131162e-04f, -4.84384241e-04f, - -2.19190438e-04f, 4.61883980e-05f, 3.09442481e-04f, 5.68335816e-04f, - 8.20650412e-04f, 1.06426195e-03f, 1.29712378e-03f, 1.51732041e-03f, - 1.72299209e-03f, 1.91251660e-03f, 2.08433504e-03f, 2.23708584e-03f, - 2.36958407e-03f, 2.48081558e-03f, 2.56998499e-03f, 2.63642962e-03f, - 2.67976001e-03f, 2.69973545e-03f, 2.69635545e-03f, 2.66978646e-03f, - 2.62041612e-03f, 2.54882653e-03f, 2.45577849e-03f, 2.34219502e-03f, - 2.20921910e-03f, 2.05811735e-03f, 1.89029043e-03f, 1.70732601e-03f, - 1.51087190e-03f, 1.30273238e-03f, 1.08474708e-03f, 8.58888422e-04f, - 6.27141283e-04f, 3.91541843e-04f, 1.54125345e-04f, -8.30221557e-05f, - -3.17874320e-04f, -5.48434822e-04f, -7.72741386e-04f, -9.88897922e-04f, - -1.19510300e-03f, -1.38966446e-03f, -1.57096128e-03f, -1.73754052e-03f, - -1.88804822e-03f, -2.02133530e-03f, -2.13633027e-03f, -2.23217932e-03f, - -2.30820740e-03f, -2.36387261e-03f, -2.39883936e-03f, -2.41294460e-03f, - -2.40621942e-03f, -2.37884693e-03f, -2.33121609e-03f, -2.26385904e-03f, - -2.17751835e-03f, -2.07302810e-03f, -1.95142870e-03f, -1.81390123e-03f, - -1.66170287e-03f, -1.49626799e-03f, -1.31909211e-03f, -1.13178731e-03f, - -9.36049438e-04f, -7.33582746e-04f, -5.26210187e-04f, -3.15727877e-04f, - -1.03964727e-04f, 1.07230526e-04f, 3.16062176e-04f, 5.20749304e-04f, - 7.19556957e-04f, 9.10822165e-04f, 1.09293790e-03f, 1.26442573e-03f, - 1.42387037e-03f, 1.56997858e-03f, 1.70160712e-03f, 1.81770393e-03f, - 1.91739676e-03f, 1.99993376e-03f, 2.06472082e-03f, 2.11132364e-03f, - 2.13948959e-03f, 2.14907506e-03f, 2.14015415e-03f, 2.11291404e-03f, - 2.06772062e-03f, 2.00509948e-03f, 1.92568205e-03f, 1.83030593e-03f, - 1.71988793e-03f, 1.59546278e-03f, 1.45820958e-03f, 1.30941455e-03f, - 1.15041754e-03f, 9.82655662e-04f, 8.07633897e-04f, 6.26913392e-04f, - 4.42086406e-04f, 2.54752525e-04f, 6.65589815e-05f, -1.20878459e-04f, - -3.05964268e-04f, -4.87118900e-04f, -6.62815511e-04f, -8.31597430e-04f, - -9.92029288e-04f, -1.14283171e-03f, -1.28276198e-03f, -1.41069924e-03f, - -1.52563857e-03f, -1.62665705e-03f, -1.71301571e-03f, -1.78407215e-03f, - -1.83932486e-03f, -1.87840593e-03f, -1.90110668e-03f, -1.90733981e-03f, - -1.89718173e-03f, -1.87081315e-03f, -1.82861635e-03f, -1.77102421e-03f, - -1.69867585e-03f, -1.61227322e-03f, -1.51267564e-03f, -1.40081900e-03f, - -1.27776296e-03f, -1.14464853e-03f, -1.00266848e-03f, -8.53125745e-04f, - -6.97343266e-04f, -5.36715682e-04f, -3.72641111e-04f, -2.06566113e-04f, - -3.99112362e-05f, 1.25872577e-04f, 2.89379042e-04f, 4.49221338e-04f, - 6.04060361e-04f, 7.52597124e-04f, 8.93603965e-04f, 1.02593625e-03f, - 1.14851696e-03f, 1.26037089e-03f, 1.36060965e-03f, 1.44847187e-03f, - 1.52327677e-03f, 1.58450468e-03f, 1.63170530e-03f, 1.66459341e-03f, - 1.68299465e-03f, 1.68684492e-03f, 1.67622052e-03f, 1.65133740e-03f, - 1.61248307e-03f, 1.56013086e-03f, 1.49479356e-03f, 1.41715103e-03f, - 1.32795375e-03f, 1.22803948e-03f, 1.11836065e-03f, 9.99922127e-04f, - 8.73805232e-04f, 7.41131956e-04f, 6.03105296e-04f, 4.60940344e-04f, - 3.15890241e-04f, 1.69211261e-04f, 2.21743638e-05f, -1.23952607e-04f, - -2.67936295e-04f, -4.08555731e-04f, -5.44640381e-04f, -6.75036718e-04f, - -7.98694471e-04f, -9.14605111e-04f, -1.02181314e-03f, -1.11948762e-03f, - -1.20686651e-03f, -1.28326662e-03f, -1.34811836e-03f, -1.40096877e-03f, - -1.44142571e-03f, -1.46927100e-03f, -1.48434461e-03f, -1.48661782e-03f, - -1.47616538e-03f, -1.45317443e-03f, -1.41795006e-03f, -1.37085590e-03f, - -1.31241749e-03f, -1.24319769e-03f, -1.16388507e-03f, -1.07522322e-03f, - -9.78051491e-04f, -8.73267846e-04f, -7.61808745e-04f, -6.44689116e-04f, - -5.22962242e-04f, -3.97698841e-04f, -2.69974946e-04f, -1.40936535e-04f, - -1.16687372e-05f, 1.16707935e-04f, 2.43105542e-04f, 3.66459900e-04f, - 4.85746921e-04f, 5.99973582e-04f, 7.08198423e-04f, 8.09540404e-04f, - 9.03214311e-04f, 9.88445526e-04f, 1.06458286e-03f, 1.13105319e-03f, - 1.18736348e-03f, 1.23309711e-03f, 1.26795195e-03f, 1.29173611e-03f, - 1.30429959e-03f, 1.30564635e-03f, 1.29584484e-03f, 1.27505558e-03f, - 1.24356178e-03f, 1.20169119e-03f, 1.14990793e-03f, 1.08871802e-03f, - 1.01870853e-03f, 9.40552377e-04f, 8.55007232e-04f, 7.62816811e-04f, - 6.64843157e-04f, 5.61968820e-04f, 4.55113887e-04f, 3.45206551e-04f, - 2.33202943e-04f, 1.20101771e-04f, 6.86134614e-06f, -1.05554304e-04f, - -2.16195795e-04f, -3.24112900e-04f, -4.28434961e-04f, -5.28276628e-04f, - -6.22838378e-04f, -7.11351653e-04f, -7.93096664e-04f, -8.67456763e-04f, - -9.33831175e-04f, -9.91726329e-04f, -1.04072669e-03f, -1.08046986e-03f, - -1.11070332e-03f, -1.13122472e-03f, -1.14196584e-03f, -1.14289978e-03f, - -1.13408364e-03f, -1.11569239e-03f, -1.08794173e-03f, -1.05115450e-03f, - -1.00570315e-03f, -9.52066366e-04f, -8.90742618e-04f, -8.22333877e-04f, - -7.47460323e-04f, -6.66834457e-04f, -5.81183573e-04f, -4.91248607e-04f, - -3.97865398e-04f, -3.01852004e-04f, -2.04027600e-04f, -1.05245839e-04f, - -6.36777404e-06f, 9.17617553e-05f, 1.88340766e-04f, 2.82546451e-04f, - 3.73570554e-04f, 4.60711065e-04f, 5.43222306e-04f, 6.20446879e-04f, - 6.91790312e-04f, 7.56668845e-04f, 8.14584433e-04f, 8.65136460e-04f, - 9.07903855e-04f, 9.42617554e-04f, 9.69039487e-04f, 9.87009408e-04f, - 9.96454207e-04f, 9.97361571e-04f, 9.89773454e-04f, 9.73845965e-04f, - 9.49773639e-04f, 9.17830053e-04f, 8.78358737e-04f, 8.31731409e-04f, - 7.78436246e-04f, 7.18952835e-04f, 6.53849580e-04f, 5.83726405e-04f, - 5.09215445e-04f, 4.30986185e-04f, 3.49740961e-04f, 2.66183564e-04f, - 1.81053864e-04f, 9.50814188e-05f, 9.00101989e-06f, -7.64476466e-05f, - -1.60555268e-04f, -2.42615492e-04f, -3.21939784e-04f, -3.97891256e-04f, - -4.69842651e-04f, -5.37218972e-04f, -5.99490711e-04f, -6.56157341e-04f, - -7.06805747e-04f, -7.51040629e-04f, -7.88542406e-04f, -8.19057673e-04f, - -8.42365219e-04f, -8.58354952e-04f, -8.66924438e-04f, -8.68087147e-04f, - -8.61872021e-04f, -8.48416980e-04f, -8.27864530e-04f, -8.00485643e-04f, - -7.66529791e-04f, -7.26368966e-04f, -6.80390151e-04f, -6.29012607e-04f, - -5.72740364e-04f, -5.12074275e-04f, -4.47577967e-04f, -3.79824551e-04f, - -3.09404546e-04f, -2.36955845e-04f, -1.63096797e-04f, -8.84666689e-05f, - -1.36918345e-05f, 6.05605282e-05f, 1.33706352e-04f, 2.05104934e-04f, - 2.74177775e-04f, 3.40364816e-04f, 4.03127428e-04f, 4.61941353e-04f, - 5.16384122e-04f, 5.65987864e-04f, 6.10406960e-04f, 6.49289729e-04f, - 6.82362251e-04f, 7.09386220e-04f, 7.30190172e-04f, 7.44646203e-04f, - 7.52685452e-04f, 7.54304020e-04f, 7.49522579e-04f, 7.38444760e-04f, - 7.21207483e-04f, 6.98036730e-04f, 6.69144529e-04f, 6.34844810e-04f, - 5.95456793e-04f, 5.51362735e-04f, 5.02982434e-04f, 4.50740873e-04f, - 3.95141218e-04f, 3.36652582e-04f, 2.75799155e-04f, 2.13134574e-04f, - 1.49173607e-04f, 8.44743226e-05f, 1.96125552e-05f, -4.48903541e-05f, - -1.08473952e-04f, -1.70630967e-04f, -2.30822756e-04f, -2.88566894e-04f, - -3.43417761e-04f, -3.94905833e-04f, -4.42640702e-04f, -4.86249354e-04f, - -5.25408193e-04f, -5.59793704e-04f, -5.89180289e-04f, -6.13368302e-04f, - -6.32178408e-04f, -6.45490725e-04f, -6.53274170e-04f, -6.55472793e-04f, - -6.52128126e-04f, -6.43307606e-04f, -6.29155845e-04f, -6.09800934e-04f, - -5.85463412e-04f, -5.56409754e-04f, -5.22887459e-04f, -4.85231865e-04f, - -4.43797790e-04f, -3.98970397e-04f, -3.51128444e-04f, -3.00728426e-04f, - -2.48188000e-04f, -1.94019174e-04f, -1.38619235e-04f, -8.25046619e-05f, - -2.61663235e-05f, 2.99583710e-05f, 8.53795123e-05f, 1.39627331e-04f, - 1.92270974e-04f, 2.42876498e-04f, 2.91041116e-04f, 3.36365129e-04f, - 3.78493606e-04f, 4.17129701e-04f, 4.51931766e-04f, 4.82649784e-04f, - 5.09080493e-04f, 5.31030056e-04f, 5.48333695e-04f, 5.60878484e-04f, - 5.68641598e-04f, 5.71552610e-04f, 5.69629306e-04f, 5.62942331e-04f, - 5.51586568e-04f, 5.35680325e-04f, 5.15392595e-04f, 4.90931349e-04f, - 4.62558320e-04f, 4.30500957e-04f, 3.95081151e-04f, 3.56619808e-04f, - 3.15458928e-04f, 2.71954945e-04f, 2.26519129e-04f, 1.79516051e-04f, - 1.31357899e-04f, 8.24572190e-05f, 3.32391965e-05f, -1.58850698e-05f, - -6.45330169e-05f, -1.12264134e-04f, -1.58698628e-04f, -2.03467556e-04f, - -2.46210106e-04f, -2.86570115e-04f, -3.24236629e-04f, -3.58912356e-04f, - -3.90340467e-04f, -4.18264108e-04f, -4.42494467e-04f, -4.62859927e-04f, - -4.79201468e-04f, -4.91408046e-04f, -4.99443631e-04f, -5.03240684e-04f, - -5.02785830e-04f, -4.98157139e-04f, -4.89378704e-04f, -4.76587119e-04f, - -4.59890738e-04f, -4.39477548e-04f, -4.15523795e-04f, -3.88271943e-04f, - -3.57945858e-04f, -3.24835243e-04f, -2.89240231e-04f, -2.51465546e-04f, - -2.11835672e-04f, -1.70699525e-04f, -1.28404932e-04f, -8.52894686e-05f, - -4.17402676e-05f, 1.86488651e-06f, 4.51992598e-05f, 8.79090103e-05f, - 1.29605134e-04f, 1.69965889e-04f, 2.08696862e-04f, 2.45456482e-04f, - 2.79955019e-04f, 3.11933102e-04f, 3.41137711e-04f, 3.67347210e-04f, - 3.90354753e-04f, 4.10008585e-04f, 4.26152950e-04f, 4.38673341e-04f, - 4.47495015e-04f, 4.52569681e-04f, 4.53871341e-04f, 4.51401451e-04f, - 4.45215411e-04f, 4.35379482e-04f, 4.22000265e-04f, 4.05191759e-04f, - 3.85119408e-04f, 3.61974661e-04f, 3.35944547e-04f, 3.07269134e-04f, - 2.76178449e-04f, 2.42964749e-04f, 2.07892199e-04f, 1.71254500e-04f, - 1.33357821e-04f, 9.45229736e-05f, 5.50557762e-05f, 1.52985188e-05f, - -2.44379242e-05f, -6.38226873e-05f, -1.02547166e-04f, -1.40299443e-04f, - -1.76768344e-04f, -2.11670093e-04f, -2.44743896e-04f, -2.75718512e-04f, - -3.04347022e-04f, -3.30418232e-04f, -3.53736841e-04f, -3.74109356e-04f, - -3.91381845e-04f, -4.05451309e-04f, -4.16191977e-04f, -4.23521256e-04f, - -4.27426658e-04f, -4.27869490e-04f, -4.24853386e-04f, -4.18413250e-04f, - -4.08611901e-04f, -3.95543058e-04f, -3.79292301e-04f, -3.60028810e-04f, - -3.37902489e-04f, -3.13061225e-04f, -2.85740223e-04f, -2.56168414e-04f, - -2.24556060e-04f, -1.91176716e-04f, -1.56272512e-04f, -1.20139890e-04f, - -8.30537602e-05f, -4.52873563e-05f, -7.15972468e-06f, 3.10302032e-05f, - 6.90244932e-05f, 1.06496284e-04f, 1.43158198e-04f, 1.78736255e-04f, - 2.12976701e-04f, 2.45591364e-04f, 2.76326944e-04f, 3.04976111e-04f, - 3.31299044e-04f, 3.55114336e-04f, 3.76239459e-04f, 3.94517329e-04f, - 4.09785367e-04f, 4.21936769e-04f, 4.30916344e-04f, 4.36588118e-04f, - 4.38939238e-04f, 4.37964002e-04f, 4.33639999e-04f, 4.26009328e-04f, - 4.15120198e-04f, 4.01062409e-04f, 3.83911295e-04f, 3.63806383e-04f, - 3.40873899e-04f, 3.15295099e-04f, 2.87229071e-04f, 2.56841831e-04f, - 2.24418352e-04f, 1.90132129e-04f, 1.54208573e-04f, 1.16966099e-04f, - 7.85931871e-05f, 3.94169348e-05f, -3.38391999e-07f, -4.03917338e-05f, - -8.04238542e-05f, -1.20226558e-04f, -1.59470944e-04f, -1.97919204e-04f, - -2.35294348e-04f, -2.71326824e-04f, -3.05778818e-04f, -3.38410918e-04f, - -3.69010644e-04f, -3.97338842e-04f, -4.23216844e-04f, -4.46466259e-04f, - -4.66912010e-04f, -4.84405189e-04f, -4.98827952e-04f, -5.10071530e-04f, - -5.18032256e-04f, -5.22640255e-04f, -5.23881412e-04f, -5.21676098e-04f, - -5.16039363e-04f, -5.06992324e-04f, -4.94563784e-04f, -4.78787143e-04f, - -4.59750953e-04f, -4.37555727e-04f, -4.12275557e-04f, -3.84054035e-04f, - -3.53037193e-04f, -3.19340180e-04f, -2.83173899e-04f, -2.44687364e-04f, - -2.04094207e-04f, -1.61583244e-04f, -1.17361228e-04f, -7.16348417e-05f, - -2.46348477e-05f, 2.33922164e-05f, 7.22418000e-05f, 1.21684779e-04f, - 1.71499457e-04f, 2.21438013e-04f, 2.71280140e-04f, 3.20801296e-04f, - 3.69771761e-04f, 4.18013477e-04f, 4.65284057e-04f, 5.11428788e-04f, - 5.56212641e-04f, 5.99478253e-04f, 6.41075472e-04f, 6.80832949e-04f, - 7.18609100e-04f, 7.54279009e-04f, 7.87742278e-04f, 8.18871508e-04f, - 8.47567713e-04f, 8.73805352e-04f, 8.97474148e-04f, 9.18529976e-04f, - 9.36959592e-04f, 9.52736827e-04f, 9.65854080e-04f, 9.76300491e-04f, - 9.84128104e-04f, 9.89353301e-04f, 9.92012052e-04f, 9.92188548e-04f, - 9.89909273e-04f, 9.85280796e-04f, 9.78335267e-04f, 9.69211001e-04f, - 9.58007113e-04f, 9.44804463e-04f, 9.29714666e-04f, 9.12874477e-04f, - 8.94419257e-04f, 8.74438162e-04f, 8.53087607e-04f, 8.30499113e-04f, - 8.06785316e-04f, 7.82049426e-04f, 7.56478510e-04f, 7.30171081e-04f, - 7.03266226e-04f, 6.75883876e-04f, 6.48150302e-04f, 6.20186681e-04f, - 5.92085453e-04f, 5.63985129e-04f, 5.35999572e-04f, 5.08203069e-04f, - 4.80689533e-04f, 4.53557971e-04f, 4.26884566e-04f, 4.00740812e-04f, - 3.75236819e-04f, 3.50353190e-04f, 3.26243777e-04f, 3.02893629e-04f, - 2.80331434e-04f, 2.58652176e-04f, 2.37852072e-04f, 2.17971034e-04f, - 1.99026311e-04f, 1.81011783e-04f, 1.63957631e-04f, 1.47844848e-04f, - 1.32718875e-04f, 1.18546172e-04f, 1.05256179e-04f, 9.29127235e-05f, - 8.14745498e-05f, 7.08726925e-05f, 6.11573299e-05f, 5.22357210e-05f, - 4.41150988e-05f, 3.67197275e-05f, 3.01315396e-05f, 2.41484723e-05f, - 1.88541281e-05f, 1.41686879e-05f, 1.00524473e-05f, 6.50288873e-06f, - 3.38594106e-06f, 7.66607975e-07f, -1.44302379e-06f, -3.26787635e-06f, - -4.78247665e-06f, -5.93452415e-06f, -6.76352831e-06f, -7.34758715e-06f, - -7.79331112e-06f, -7.91311421e-06f, -7.86244933e-06f, -7.86157694e-06f, - -7.64215127e-06f, -7.28290836e-06f, -6.81887853e-06f, -6.49158597e-06f, - -6.02222593e-06f, -5.26738395e-06f, -5.09005650e-06f, -1.73022428e-05f, + 0.00000000e+00f, 2.57738229e-05f, 2.28312635e-05f, 3.29301720e-05f, + 4.62792766e-05f, 6.28392813e-05f, 8.34298526e-05f, 1.09345366e-04f, + 1.40765613e-04f, 1.79085823e-04f, 2.25515927e-04f, 2.80686140e-04f, + 3.46440184e-04f, 4.24213060e-04f, 5.15679758e-04f, 6.22703072e-04f, + 7.47248606e-04f, 8.91648830e-04f, 1.05819097e-03f, 1.24970360e-03f, + 1.46896184e-03f, 1.71909900e-03f, 2.00352112e-03f, 2.32585325e-03f, + 2.69004589e-03f, 3.10009770e-03f, 3.56066709e-03f, 4.07650766e-03f, + 4.65266984e-03f, 5.29443820e-03f, 6.00761218e-03f, 6.79817892e-03f, + 7.67241666e-03f, 8.63705786e-03f, 9.69902462e-03f, 1.08655464e-02f, + 1.21441926e-02f, 1.35431147e-02f, 1.50703831e-02f, 1.67342248e-02f, + 1.85434518e-02f, 2.05073885e-02f, 2.26351995e-02f, 2.49359480e-02f, + 2.74195152e-02f, 3.00957201e-02f, 3.29746797e-02f, 3.60660148e-02f, + 3.93800865e-02f, 4.29269667e-02f, 4.67167355e-02f, 5.07595322e-02f, + 5.50652027e-02f, 5.96435730e-02f, 6.45041914e-02f, 6.96563995e-02f, + 7.51092741e-02f, 8.08713050e-02f, 8.69506086e-02f, 9.33550143e-02f, + 1.00091521e-01f, 1.07166524e-01f, 1.14585856e-01f, 1.22354427e-01f, + 1.30476332e-01f, 1.38954781e-01f, 1.47791858e-01f, 1.56988836e-01f, + 1.66545712e-01f, 1.76461162e-01f, 1.86732857e-01f, 1.97356887e-01f, + 2.08328235e-01f, 2.19640042e-01f, 2.31284315e-01f, 2.43251224e-01f, + 2.55529580e-01f, 2.68106380e-01f, 2.80966931e-01f, 2.94094913e-01f, + 3.07472157e-01f, 3.21078662e-01f, 3.34892936e-01f, 3.48891418e-01f, + 3.63048947e-01f, 3.77338339e-01f, 3.91731016e-01f, 4.06196375e-01f, + 4.20702327e-01f, 4.35214921e-01f, 4.49698829e-01f, 4.64117192e-01f, + 4.78431501e-01f, 4.92602156e-01f, 5.06588058e-01f, 5.20347244e-01f, + 5.33836291e-01f, 5.47011324e-01f, 5.59827304e-01f, 5.72238858e-01f, + 5.84199812e-01f, 5.95663881e-01f, 6.06584530e-01f, 6.16915479e-01f, + 6.26610227e-01f, 6.35623009e-01f, 6.43908415e-01f, 6.51422044e-01f, + 6.58120303e-01f, 6.63960857e-01f, 6.68902787e-01f, 6.72906783e-01f, + 6.75935360e-01f, 6.77953091e-01f, 6.78926764e-01f, 6.78825652e-01f, + 6.77621638e-01f, 6.75289589e-01f, 6.71807256e-01f, 6.67155714e-01f, + 6.61319462e-01f, 6.54286514e-01f, 6.46048652e-01f, 6.36601561e-01f, + 6.25944910e-01f, 6.14082478e-01f, 6.01022343e-01f, 5.86776774e-01f, + 5.71362584e-01f, 5.54800895e-01f, 5.37117377e-01f, 5.18342181e-01f, + 4.98509921e-01f, 4.77659701e-01f, 4.55834999e-01f, 4.33083714e-01f, + 4.09457911e-01f, 3.85013844e-01f, 3.59811788e-01f, 3.33915906e-01f, + 3.07393970e-01f, 2.80317366e-01f, 2.52760583e-01f, 2.24801315e-01f, + 1.96519969e-01f, 1.67999385e-01f, 1.39324761e-01f, 1.10583043e-01f, + 8.18628436e-02f, 5.32540056e-02f, 2.48471803e-02f, -3.26636649e-03f, + -3.09952967e-02f, -5.82485562e-02f, -8.49355930e-02f, -1.10967034e-01f, + -1.36254903e-01f, -1.60712966e-01f, -1.84257387e-01f, -2.06806776e-01f, + -2.28282907e-01f, -2.48610910e-01f, -2.67719669e-01f, -2.85542260e-01f, + -3.02016257e-01f, -3.17084071e-01f, -3.30693194e-01f, -3.42796661e-01f, + -3.53353212e-01f, -3.62327576e-01f, -3.69690653e-01f, -3.75419813e-01f, + -3.79499075e-01f, -3.81919103e-01f, -3.82677491e-01f, -3.81778733e-01f, + -3.79234384e-01f, -3.75063017e-01f, -3.69290152e-01f, -3.61948304e-01f, + -3.53076910e-01f, -3.42722096e-01f, -3.30936620e-01f, -3.17779633e-01f, + -3.03316514e-01f, -2.87618587e-01f, -2.70762756e-01f, -2.52831320e-01f, + -2.33911539e-01f, -2.14095259e-01f, -1.93478520e-01f, -1.72161169e-01f, + -1.50246285e-01f, -1.27839839e-01f, -1.05050082e-01f, -8.19871599e-02f, + -5.87624360e-02f, -3.54880619e-02f, -1.22764049e-02f, 1.07605263e-02f, + 3.35116028e-02f, 5.58671830e-02f, 7.77196474e-02f, 9.89638589e-02f, + 1.19497902e-01f, 1.39223354e-01f, 1.58046039e-01f, 1.75876344e-01f, + 1.92629792e-01f, 2.08227445e-01f, 2.22596376e-01f, 2.35669935e-01f, + 2.47388321e-01f, 2.57698757e-01f, 2.66555789e-01f, 2.73921619e-01f, + 2.79766215e-01f, 2.84067669e-01f, 2.86812042e-01f, 2.87993788e-01f, + 2.87615536e-01f, 2.85688209e-01f, 2.82230951e-01f, 2.77271050e-01f, + 2.70843807e-01f, 2.62992310e-01f, 2.53767274e-01f, 2.43226727e-01f, + 2.31435732e-01f, 2.18465973e-01f, 2.04395453e-01f, 1.89308050e-01f, + 1.73292986e-01f, 1.56444403e-01f, 1.38860793e-01f, 1.20644581e-01f, + 1.01901339e-01f, 8.27393357e-02f, 6.32689072e-02f, 4.36018454e-02f, + 2.38507071e-02f, 4.12822827e-03f, -1.54533490e-02f, -3.47829031e-02f, + -5.37510504e-02f, -7.22508561e-02f, -9.01782817e-02f, -1.07433017e-01f, + -1.23918854e-01f, -1.39544366e-01f, -1.54223358e-01f, -1.67875437e-01f, + -1.80426472e-01f, -1.91808926e-01f, -2.01962423e-01f, -2.10833914e-01f, + -2.18378136e-01f, -2.24557777e-01f, -2.29343716e-01f, -2.32715250e-01f, + -2.34660116e-01f, -2.35174592e-01f, -2.34263552e-01f, -2.31940408e-01f, + -2.28226909e-01f, -2.23153240e-01f, -2.16757610e-01f, -2.09086092e-01f, + -2.00192330e-01f, -1.90137237e-01f, -1.78988601e-01f, -1.66820607e-01f, + -1.53713424e-01f, -1.39752706e-01f, -1.25029052e-01f, -1.09637434e-01f, + -9.36766468e-02f, -7.72485626e-02f, -6.04577178e-02f, -4.34104123e-02f, + -2.62142727e-02f, -8.97736641e-03f, 8.19224333e-03f, 2.51873938e-02f, + 4.19023475e-02f, 5.82335223e-02f, 7.40800352e-02f, 8.93445817e-02f, + 1.03933643e-01f, 1.17758514e-01f, 1.30735488e-01f, 1.42786597e-01f, + 1.53840027e-01f, 1.63830572e-01f, 1.72700012e-01f, 1.80397490e-01f, + 1.86879846e-01f, 1.92111822e-01f, 1.96066362e-01f, 1.98724630e-01f, + 2.00076290e-01f, 2.00119400e-01f, 1.98860492e-01f, 1.96314478e-01f, + 1.92504587e-01f, 1.87462157e-01f, 1.81226343e-01f, 1.73844017e-01f, + 1.65369327e-01f, 1.55863248e-01f, 1.45393459e-01f, 1.34033511e-01f, + 1.21862692e-01f, 1.08965144e-01f, 9.54296309e-02f, 8.13487144e-02f, + 6.68182119e-02f, 5.19365830e-02f, 3.68042391e-02f, 2.15228293e-02f, + 6.19473181e-03f, -9.07786386e-03f, -2.41934219e-02f, -3.90518897e-02f, + -5.35552556e-02f, -6.76082701e-02f, -8.11190181e-02f, -9.39995712e-02f, + -1.06166560e-01f, -1.17541673e-01f, -1.28052194e-01f, -1.37631580e-01f, + -1.46219738e-01f, -1.53763473e-01f, -1.60216884e-01f, -1.65541564e-01f, + -1.69706914e-01f, -1.72690307e-01f, -1.74477196e-01f, -1.75061256e-01f, + -1.74444314e-01f, -1.72636437e-01f, -1.69655694e-01f, -1.65528167e-01f, + -1.60287634e-01f, -1.53975364e-01f, -1.46639831e-01f, -1.38336334e-01f, + -1.29126633e-01f, -1.19078480e-01f, -1.08265109e-01f, -9.67648277e-02f, + -8.46603321e-02f, -7.20381983e-02f, -5.89882902e-02f, -4.56030440e-02f, + -3.19769405e-02f, -1.82057654e-02f, -4.38589399e-03f, 9.38618442e-03f, + 2.30147873e-02f, 3.64055026e-02f, 4.94659847e-02f, 6.21064640e-02f, + 7.42404823e-02f, 8.57854026e-02f, 9.66630881e-02f, 1.06800214e-01f, + 1.16129062e-01f, 1.24587714e-01f, 1.32120704e-01f, 1.38679150e-01f, + 1.44221352e-01f, 1.48712781e-01f, 1.52126551e-01f, 1.54443364e-01f, + 1.55651872e-01f, 1.55748483e-01f, 1.54737594e-01f, 1.52631324e-01f, + 1.49449613e-01f, 1.45219867e-01f, 1.39976951e-01f, 1.33762695e-01f, + 1.26625732e-01f, 1.18621127e-01f, 1.09809835e-01f, 1.00258461e-01f, + 9.00385966e-02f, 7.92263112e-02f, 6.79017514e-02f, 5.61483242e-02f, + 4.40522753e-02f, 3.17019988e-02f, 1.91873411e-02f, 6.59909466e-03f, + -5.97180657e-03f, -1.84349074e-02f, -3.07007132e-02f, -4.26816417e-02f, + -5.42923896e-02f, -6.54506079e-02f, -7.60775609e-02f, -8.60985825e-02f, + -9.54437408e-02f, -1.04048142e-01f, -1.11852553e-01f, -1.18803711e-01f, + -1.24854813e-01f, -1.29965653e-01f, -1.34103041e-01f, -1.37241039e-01f, + -1.39360993e-01f, -1.40451792e-01f, -1.40509793e-01f, -1.39538986e-01f, + -1.37550804e-01f, -1.34564089e-01f, -1.30604905e-01f, -1.25706354e-01f, + -1.19908276e-01f, -1.13257015e-01f, -1.05804921e-01f, -9.76101298e-02f, + -8.87359691e-02f, -7.92505483e-02f, -6.92262319e-02f, -5.87391125e-02f, + -4.78684160e-02f, -3.66959289e-02f, -2.53053541e-02f, -1.37817249e-02f, + -2.21079428e-03f, 9.32167747e-03f, 2.07305160e-02f, 3.19316883e-02f, + 4.28430566e-02f, 5.33848786e-02f, 6.34804441e-02f, 7.30565856e-02f, + 8.20442897e-02f, 9.03790776e-02f, 9.80016231e-02f, 1.04858052e-01f, + 1.10900371e-01f, 1.16086840e-01f, 1.20382198e-01f, 1.23757969e-01f, + 1.26192651e-01f, 1.27671770e-01f, 1.28188137e-01f, 1.27741675e-01f, + 1.26339596e-01f, 1.23996183e-01f, 1.20732705e-01f, 1.16577307e-01f, + 1.11564669e-01f, 1.05735813e-01f, 9.91377187e-02f, 9.18230275e-02f, + 8.38495092e-02f, 7.52797839e-02f, 6.61806838e-02f, 5.66228090e-02f, + 4.66799931e-02f, 3.64286945e-02f, 2.59474847e-02f, 1.53163689e-02f, + 4.61622490e-03f, -6.07182197e-03f, -1.66669799e-02f, -2.70894704e-02f, + -3.72610297e-02f, -4.71056290e-02f, -5.65498931e-02f, -6.55237625e-02f, + -7.39609699e-02f, -8.17995235e-02f, -8.89821548e-02f, -9.54568508e-02f, + -1.01177041e-01f, -1.06102108e-01f, -1.10197566e-01f, -1.13435384e-01f, + -1.15794171e-01f, -1.17259227e-01f, -1.17822757e-01f, -1.17483887e-01f, + -1.16248548e-01f, -1.14129572e-01f, -1.11146430e-01f, -1.07325180e-01f, + -1.02698169e-01f, -9.73037750e-02f, -9.11861728e-02f, -8.43949139e-02f, + -7.69845152e-02f, -6.90140891e-02f, -6.05468325e-02f, -5.16495384e-02f, + -4.23921120e-02f, -3.28470026e-02f, -2.30885659e-02f, -1.31926321e-02f, + -3.23579390e-03f, 6.70506976e-03f, 1.65535306e-02f, 2.62340587e-02f, + 3.56726723e-02f, 4.47975155e-02f, 5.35393359e-02f, 6.18320891e-02f, + 6.96133711e-02f, 7.68249575e-02f, 8.34132047e-02f, 8.93294053e-02f, + 9.45302615e-02f, 9.89780776e-02f, 1.02641187e-01f, 1.05494003e-01f, + 1.07517350e-01f, 1.08698506e-01f, 1.09031319e-01f, 1.08516239e-01f, + 1.07160277e-01f, 1.04976947e-01f, 1.01986161e-01f, 9.82139821e-02f, + 9.36925268e-02f, 8.84595879e-02f, 8.25583806e-02f, 7.60371894e-02f, + 6.89489402e-02f, 6.13508561e-02f, 5.33039225e-02f, 4.48724192e-02f, + 3.61234643e-02f, 2.71263730e-02f, 1.79522593e-02f, 8.67332814e-03f, + -6.37560549e-04f, -9.90757623e-03f, -1.90643762e-02f, -2.80367915e-02f, + -3.67552453e-02f, -4.51524120e-02f, -5.31636754e-02f, -6.07276053e-02f, + -6.77865076e-02f, -7.42868063e-02f, -8.01794199e-02f, -8.54202472e-02f, + -8.99703936e-02f, -9.37965226e-02f, -9.68710320e-02f, -9.91723636e-02f, + -1.00685055e-01f, -1.01399924e-01f, -1.01314027e-01f, -1.00430752e-01f, + -9.87597996e-02f, -9.63169323e-02f, -9.31240228e-02f, -8.92087380e-02f, + -8.46043840e-02f, -7.93495604e-02f, -7.34879129e-02f, -6.70676790e-02f, + -6.01414310e-02f, -5.27655243e-02f, -4.49997042e-02f, -3.69066235e-02f, + -2.85513234e-02f, -2.00007483e-02f, -1.13231172e-02f, -2.58753819e-03f, + 6.13671174e-03f, 1.47805601e-02f, 2.32758739e-02f, 3.15558319e-02f, + 3.95555353e-02f, 4.72125037e-02f, 5.44671546e-02f, 6.12632712e-02f, + 6.75484439e-02f, 7.32744268e-02f, 7.83976489e-02f, 8.28793328e-02f, + 8.66860430e-02f, 8.97896293e-02f, 9.21678120e-02f, 9.38039664e-02f, + 9.46875290e-02f, 9.48139007e-02f, 9.41845125e-02f, 9.28068505e-02f, + 9.06942851e-02f, 8.78660470e-02f, 8.43469508e-02f, 8.01672994e-02f, + 7.53625613e-02f, 6.99730444e-02f, 6.40436219e-02f, 5.76234074e-02f, + 5.07651752e-02f, 4.35251559e-02f, 3.59623612e-02f, 2.81382801e-02f, + 2.01163116e-02f, 1.19611978e-02f, 3.73860852e-03f, -4.48544815e-03f, + -1.26451529e-02f, -2.06753686e-02f, -2.85122123e-02f, -3.60935203e-02f, + -4.33593322e-02f, -5.02524350e-02f, -5.67187066e-02f, -6.27076379e-02f, + -6.81726899e-02f, -7.30716527e-02f, -7.73669645e-02f, -8.10260558e-02f, + -8.40214980e-02f, -8.63313064e-02f, -8.79390729e-02f, -8.88340404e-02f, + -8.90112500e-02f, -8.84714905e-02f, -8.72213317e-02f, -8.52730391e-02f, + -8.26444808e-02f, -7.93588812e-02f, -7.54447661e-02f, -7.09355706e-02f, + -6.58695010e-02f, -6.02890903e-02f, -5.42408964e-02f, -4.77751478e-02f, + -4.09452872e-02f, -3.38075119e-02f, -2.64204001e-02f, -1.88443271e-02f, + -1.11410841e-02f, -3.37322640e-03f, 4.39625224e-03f, 1.21045621e-02f, + 1.96895966e-02f, 2.70903872e-02f, 3.42476479e-02f, 4.11042063e-02f, + 4.76055071e-02f, 5.36999666e-02f, 5.93395286e-02f, 6.44798549e-02f, + 6.90808373e-02f, 7.31067984e-02f, 7.65268692e-02f, 7.93151321e-02f, + 8.14508652e-02f, 8.29187318e-02f, 8.37087874e-02f, 8.38166939e-02f, + 8.32435667e-02f, 8.19961230e-02f, 8.00865044e-02f, 7.75321668e-02f, + 7.43557925e-02f, 7.05850463e-02f, 6.62523340e-02f, 6.13945573e-02f, + 5.60527587e-02f, 5.02718197e-02f, 4.41000820e-02f, 3.75889077e-02f, + 3.07923072e-02f, 2.37664500e-02f, 1.65692144e-02f, 9.25974471e-03f, + 1.89789131e-03f, -5.45616102e-03f, -1.27425262e-02f, -1.99019879e-02f, + -2.68765329e-02f, -3.36098353e-02f, -4.00476211e-02f, -4.61382887e-02f, + -5.18330845e-02f, -5.70866854e-02f, -6.18574768e-02f, -6.61078954e-02f, + -6.98047314e-02f, -7.29193713e-02f, -7.54280733e-02f, -7.73120518e-02f, + -7.85577193e-02f, -7.91567321e-02f, -7.91060672e-02f, -7.84079778e-02f, + -7.70700917e-02f, -7.51051477e-02f, -7.25310474e-02f, -6.93706386e-02f, + -6.56514718e-02f, -6.14056206e-02f, -5.66693718e-02f, -5.14829473e-02f, + -4.58901735e-02f, -3.99380331e-02f, -3.36763702e-02f, -2.71574389e-02f, + -2.04354475e-02f, -1.35661532e-02f, -6.60630727e-03f, 3.86646824e-04f, + 7.35520973e-03f, 1.42422280e-02f, 2.09913641e-02f, 2.75475284e-02f, + 3.38574221e-02f, 3.98698307e-02f, 4.55362181e-02f, 5.08109216e-02f, + 5.56516759e-02f, 6.00198762e-02f, 6.38809158e-02f, 6.72044395e-02f, + 6.99645791e-02f, 7.21402152e-02f, 7.37150597e-02f, 7.46778107e-02f, + 7.50222360e-02f, 7.47472565e-02f, 7.38568363e-02f, 7.23600417e-02f, + 7.02708761e-02f, 6.76082238e-02f, 6.43956576e-02f, 6.06611745e-02f, + 5.64370176e-02f, 5.17594495e-02f, 4.66682739e-02f, 4.12066978e-02f, + 3.54208167e-02f, 2.93593188e-02f, 2.30730217e-02f, 1.66144782e-02f, + 1.00375317e-02f, 3.39684147e-03f, -3.25251124e-03f, -9.85551584e-03f, + -1.63577490e-02f, -2.27056595e-02f, -2.88471998e-02f, -3.47320840e-02f, + -4.03123540e-02f, -4.55426353e-02f, -5.03805924e-02f, -5.47872529e-02f, + -5.87272646e-02f, -6.21693020e-02f, -6.50861771e-02f, -6.74551714e-02f, + -6.92581408e-02f, -7.04816306e-02f, -7.11171304e-02f, -7.11609040e-02f, + -7.06142341e-02f, -6.94832169e-02f, -6.77788399e-02f, -6.55167566e-02f, + -6.27172654e-02f, -5.94050389e-02f, -5.56089420e-02f, -5.13618140e-02f, + -4.67001077e-02f, -4.16636667e-02f, -3.62953408e-02f, -3.06405937e-02f, + -2.47472033e-02f, -1.86647681e-02f, -1.24443758e-02f, -6.13808338e-03f, + 2.01411978e-04f, 6.52133427e-03f, 1.27691320e-02f, 1.88930538e-02f, + 2.48424465e-02f, 3.05682613e-02f, 3.60234752e-02f, 4.11633315e-02f, + 4.59459248e-02f, 5.03323304e-02f, 5.42870835e-02f, 5.77784019e-02f, + 6.07783651e-02f, 6.32633288e-02f, 6.52139121e-02f, 6.66152616e-02f, + 6.74571587e-02f, 6.77340253e-02f, 6.74450634e-02f, 6.65941743e-02f, + 6.51898969e-02f, 6.32454230e-02f, 6.07783949e-02f, 5.78107648e-02f, + 5.43686030e-02f, 5.04819132e-02f, 4.61842717e-02f, 4.15126988e-02f, + 3.65071237e-02f, 3.12102820e-02f, 2.56671547e-02f, 1.99247189e-02f, + 1.40314897e-02f, 8.03710981e-03f, 1.99194113e-03f, -4.05328806e-03f, + -1.00480950e-02f, -1.59424027e-02f, -2.16871978e-02f, -2.72348278e-02f, + -3.25393096e-02f, -3.75569641e-02f, -4.22464745e-02f, -4.65694440e-02f, + -5.04906287e-02f, -5.39782262e-02f, -5.70041442e-02f, -5.95442285e-02f, + -6.15784134e-02f, -6.30910059e-02f, -6.40706329e-02f, -6.45104738e-02f, + -6.44081853e-02f, -6.37660587e-02f, -6.25908310e-02f, -6.08937512e-02f, + -5.86903214e-02f, -5.60003749e-02f, -5.28477193e-02f, -4.92599533e-02f, + -4.52683132e-02f, -4.09073183e-02f, -3.62145264e-02f, -3.12301828e-02f, + -2.59968465e-02f, -2.05591356e-02f, -1.49632303e-02f, -9.25651553e-03f, + -3.48724314e-03f, 2.29598571e-03f, 8.04450724e-03f, 1.37101015e-02f, + 1.92453689e-02f, 2.46040942e-02f, 2.97416606e-02f, 3.46154341e-02f, + 3.91850779e-02f, 4.34129264e-02f, 4.72642978e-02f, 5.07076760e-02f, + 5.37151661e-02f, 5.62625282e-02f, 5.83294584e-02f, 5.98998097e-02f, + 6.09615551e-02f, 6.15070547e-02f, 6.15329990e-02f, 6.10404716e-02f, + 6.00349135e-02f, 5.85260610e-02f, 5.65279401e-02f, 5.40585286e-02f, + 5.11398898e-02f, 4.77976785e-02f, 4.40611761e-02f, 3.99628438e-02f, + 3.55380989e-02f, 3.08251587e-02f, 2.58644088e-02f, 2.06983747e-02f, + 1.53710739e-02f, 9.92792727e-03f, 4.41510532e-03f, -1.12066218e-03f, + -6.63261302e-03f, -1.20742595e-02f, -1.73998395e-02f, -2.25646758e-02f, + -2.75255332e-02f, -3.22410401e-02f, -3.66719333e-02f, -4.07815241e-02f, + -4.45358322e-02f, -4.79040656e-02f, -5.08586430e-02f, -5.33756630e-02f, + -5.54348806e-02f, -5.70200545e-02f, -5.81188903e-02f, -5.87233379e-02f, + -5.88294685e-02f, -5.84376077e-02f, -5.75522891e-02f, -5.61822154e-02f, + -5.43401522e-02f, -5.20428669e-02f, -4.93108603e-02f, -4.61683834e-02f, + -4.26429808e-02f, -3.87654608e-02f, -3.45695151e-02f, -3.00914369e-02f, + -2.53698514e-02f, -2.04453289e-02f, -1.53600900e-02f, -1.01575989e-02f, + -4.88223250e-03f, 4.21140683e-04f, 5.70749346e-03f, 1.09320714e-02f, + 1.60507423e-02f, 2.10203384e-02f, 2.57991258e-02f, 3.03469891e-02f, + 3.46259681e-02f, 3.86003807e-02f, 4.22372656e-02f, 4.55065977e-02f, + 4.83814916e-02f, 5.08385122e-02f, 5.28578492e-02f, 5.44233532e-02f, + 5.55228686e-02f, 5.61481799e-02f, 5.62951181e-02f, 5.59635641e-02f, + 5.51575059e-02f, 5.38848971e-02f, 5.21577067e-02f, 4.99916630e-02f, + 4.74062222e-02f, 4.44243762e-02f, 4.10723813e-02f, 3.73796334e-02f, + 3.33783371e-02f, 2.91031994e-02f, 2.45912699e-02f, 1.98814471e-02f, + 1.50142456e-02f, 1.00314492e-02f, 4.97569860e-03f, -1.09829558e-04f, + -5.18180429e-03f, -1.01971104e-02f, -1.51132009e-02f, -1.98884909e-02f, + -2.44826770e-02f, -2.88570632e-02f, -3.29749774e-02f, -3.68019407e-02f, + -4.03060834e-02f, -4.34583201e-02f, -4.62326394e-02f, -4.86063256e-02f, + -5.05600942e-02f, -5.20783017e-02f, -5.31490320e-02f, -5.37642601e-02f, + -5.39197625e-02f, -5.36153455e-02f, -5.28546569e-02f, -5.16452744e-02f, + -4.99985519e-02f, -4.79295687e-02f, -4.54569365e-02f, -4.26027299e-02f, + -3.93921382e-02f, -3.58534069e-02f, -3.20174780e-02f, -2.79177659e-02f, + -2.35898897e-02f, -1.90712489e-02f, -1.44009108e-02f, -9.61905551e-03f, + -4.76675190e-03f, 1.14429175e-04f, 4.98275197e-03f, 9.79667060e-03f, + 1.45152450e-02f, 1.90984000e-02f, 2.35073004e-02f, 2.77047166e-02f, + 3.16552879e-02f, 3.53258717e-02f, 3.86857329e-02f, 4.17069009e-02f, + 4.43643859e-02f, 4.66362841e-02f, 4.85040977e-02f, 4.99528118e-02f, + 5.09710058e-02f, 5.15509899e-02f, 5.16888143e-02f, 5.13843182e-02f, + 5.06411841e-02f, 4.94667221e-02f, 4.78719944e-02f, 4.58716454e-02f, + 4.34836680e-02f, 4.07294565e-02f, 3.76333478e-02f, 3.42226856e-02f, + 3.05273460e-02f, 2.65796084e-02f, 2.24138588e-02f, 1.80662406e-02f, + 1.35743725e-02f, 8.97708082e-03f, 4.31392223e-03f, -3.74997817e-04f, + -5.04949488e-03f, -9.66954442e-03f, -1.41957488e-02f, -1.85895102e-02f, + -2.28135517e-02f, -2.68320150e-02f, -3.06110148e-02f, -3.41186844e-02f, + -3.73255948e-02f, -4.02049849e-02f, -4.27328870e-02f, -4.48884826e-02f, + -4.66541442e-02f, -4.80155982e-02f, -4.89621376e-02f, -4.94865911e-02f, + -4.95854066e-02f, -4.92587543e-02f, -4.85103568e-02f, -4.73476557e-02f, + -4.57815259e-02f, -4.38263403e-02f, -4.14997758e-02f, -3.88226280e-02f, + -3.58186618e-02f, -3.25144195e-02f, -2.89389208e-02f, -2.51234952e-02f, + -2.11014173e-02f, -1.69076786e-02f, -1.25787051e-02f, -8.15193430e-03f, + -3.66561199e-03f, 8.41612730e-04f, 5.33096998e-03f, 9.76396982e-03f, + 1.41026633e-02f, 1.83099747e-02f, 2.23500907e-02f, 2.61886650e-02f, + 2.97931947e-02f, 3.31332165e-02f, 3.61806686e-02f, 3.89099840e-02f, + 4.12984711e-02f, 4.33263397e-02f, 4.49769909e-02f, 4.62370906e-02f, + 4.70967034e-02f, 4.75493101e-02f, 4.75919973e-02f, 4.72252655e-02f, + 4.64532495e-02f, 4.52834651e-02f, 4.37268913e-02f, 4.17977928e-02f, + 3.95136051e-02f, 3.68947746e-02f, 3.39645897e-02f, 3.07489848e-02f, + 2.72762475e-02f, 2.35768654e-02f, 1.96831666e-02f, 1.56291178e-02f, + 1.14499678e-02f, 7.18199244e-03f, 2.86208797e-03f, -1.47237323e-03f, + -5.78408986e-03f, -1.00359644e-02f, -1.41915102e-02f, -1.82151627e-02f, + -2.20725868e-02f, -2.57308876e-02f, -2.91590343e-02f, -3.23279893e-02f, + -3.52110559e-02f, -3.77840233e-02f, -4.00254088e-02f, -4.19166660e-02f, + -4.34422559e-02f, -4.45898688e-02f, -4.53504693e-02f, -4.57183342e-02f, + -4.56912080e-02f, -4.52701881e-02f, -4.44598082e-02f, -4.32679041e-02f, + -4.17056221e-02f, -3.97872721e-02f, -3.75301757e-02f, -3.49545539e-02f, + -3.20833642e-02f, -2.89420199e-02f, -2.55582198e-02f, -2.19616927e-02f, + -1.81839563e-02f, -1.42580179e-02f, -1.02180663e-02f, -6.09921811e-03f, + -1.93723037e-03f, 2.23190874e-03f, 6.37220013e-03f, 1.04479531e-02f, + 1.44241290e-02f, 1.82666389e-02f, 2.19425802e-02f, 2.54206248e-02f, + 2.86711138e-02f, 3.16665395e-02f, 3.43815887e-02f, 3.67934228e-02f, + 3.88819070e-02f, 4.06296963e-02f, 4.20224632e-02f, 4.30489649e-02f, + 4.37011440e-02f, 4.39741987e-02f, 4.38666105e-02f, 4.33801492e-02f, + 4.25198834e-02f, 4.12940568e-02f, 3.97140893e-02f, 3.77944134e-02f, + 3.55523815e-02f, 3.30080847e-02f, 3.01842181e-02f, 2.71057696e-02f, + 2.37999951e-02f, 2.02958932e-02f, 1.66242890e-02f, 1.28172569e-02f, + 8.90804027e-03f, 4.93068765e-03f, 9.19732115e-04f, -3.09003828e-03f, + -7.06392168e-03f, -1.09676549e-02f, -1.47675919e-02f, -1.84310619e-02f, + -2.19267124e-02f, -2.52246331e-02f, -2.82967223e-02f, -3.11168905e-02f, + -3.36612820e-02f, -3.59084899e-02f, -3.78397204e-02f, -3.94389199e-02f, + -4.06930177e-02f, -4.15918929e-02f, -4.21285586e-02f, -4.22991498e-02f, + -4.21030212e-02f, -4.15426637e-02f, -4.06237373e-02f, -3.93549944e-02f, + -3.77482044e-02f, -3.58180208e-02f, -3.35818746e-02f, -3.10598205e-02f, + -2.82743551e-02f, -2.52501753e-02f, -2.20140228e-02f, -1.85944012e-02f, + -1.50213539e-02f, -1.13261978e-02f, -7.54120713e-03f, -3.69942331e-03f, + 1.65761761e-04f, 4.02077827e-03f, 7.83220364e-03f, 1.15671267e-02f, + 1.51932941e-02f, 1.86795257e-02f, 2.19958878e-02f, 2.51139844e-02f, + 2.80072326e-02f, 3.06510161e-02f, 3.30229521e-02f, 3.51030581e-02f, + 3.68739519e-02f, 3.83209200e-02f, 3.94321479e-02f, 4.01987038e-02f, + 4.06147205e-02f, 4.06773379e-02f, 4.03868010e-02f, 3.97463905e-02f, + 3.87624502e-02f, 3.74442613e-02f, 3.58040217e-02f, 3.38566274e-02f, + 3.16197104e-02f, 2.91132913e-02f, 2.63597535e-02f, 2.33834951e-02f, + 2.02109048e-02f, 1.68699197e-02f, 1.33899369e-02f, 9.80149436e-03f, + 6.13596978e-03f, 2.42540672e-03f, -1.29791566e-03f, -5.00159520e-03f, + -8.65350380e-03f, -1.22220366e-02f, -1.56763640e-02f, -1.89867381e-02f, + -2.21247040e-02f, -2.50633577e-02f, -2.77776392e-02f, -3.02443926e-02f, + -3.24427741e-02f, -3.43542347e-02f, -3.59627985e-02f, -3.72551425e-02f, + -3.82207294e-02f, -3.88518854e-02f, -3.91438407e-02f, -3.90947976e-02f, + -3.87059356e-02f, -3.79813919e-02f, -3.69281514e-02f, -3.55561727e-02f, + -3.38780082e-02f, -3.19089538e-02f, -2.96667755e-02f, -2.71715529e-02f, + -2.44455713e-02f, -2.15130067e-02f, -1.83998712e-02f, -1.51336016e-02f, + -1.17429607e-02f, -8.25773631e-03f, -4.70843717e-03f, -1.12611141e-03f, + 2.45802887e-03f, 6.01275162e-03f, 9.50720826e-03f, 1.29111118e-02f, + 1.61950221e-02f, 1.93306375e-02f, 2.22909758e-02f, 2.50506401e-02f, + 2.75860615e-02f, 2.98756376e-02f, 3.18999914e-02f, 3.36420581e-02f, + 3.50872846e-02f, 3.62237568e-02f, 3.70422138e-02f, 3.75362558e-02f, + 3.77022507e-02f, 3.75395252e-02f, 3.70501413e-02f, 3.62391403e-02f, + 3.51142523e-02f, 3.36859539e-02f, 3.19673834e-02f, 2.99741015e-02f, + 2.77241155e-02f, 2.52375586e-02f, 2.25366390e-02f, 1.96453479e-02f, + 1.65892764e-02f, 1.33954509e-02f, 1.00919807e-02f, 6.70792095e-03f, + 3.27291818e-03f, -1.82973294e-04f, -3.62955472e-03f, -7.03682264e-03f, + -1.03751742e-02f, -1.36156254e-02f, -1.67301419e-02f, -1.96918703e-02f, + -2.24752801e-02f, -2.50565117e-02f, -2.74134928e-02f, -2.95261426e-02f, + -3.13765953e-02f, -3.29492690e-02f, -3.42310321e-02f, -3.52113800e-02f, + -3.58823726e-02f, -3.62388683e-02f, -3.62784198e-02f, -3.60013804e-02f, + -3.54108451e-02f, -3.45126668e-02f, -3.33153418e-02f, -3.18299584e-02f, + -3.00701260e-02f, -2.80517892e-02f, -2.57931374e-02f, -2.33143863e-02f, + -2.06376424e-02f, -1.77867508e-02f, -1.47868963e-02f, -1.16646556e-02f, + -8.44750940e-03f, -5.16378838e-03f, -1.84226820e-03f, 1.48794674e-03f, + 4.79778745e-03f, 8.05836223e-03f, 1.12413416e-02f, 1.43190753e-02f, + 1.72649338e-02f, 2.00534760e-02f, 2.26606759e-02f, 2.50641770e-02f, + 2.72434093e-02f, 2.91798405e-02f, 3.08570869e-02f, 3.22610538e-02f, + 3.33800775e-02f, 3.42050023e-02f, 3.47292410e-02f, 3.49489094e-02f, + 3.48627120e-02f, 3.44720896e-02f, 3.37811019e-02f, 3.27964643e-02f, + 3.15273982e-02f, 2.99856201e-02f, 2.81852138e-02f, 2.61424498e-02f, + 2.38757347e-02f, 2.14053475e-02f, 1.87533476e-02f, 1.59433058e-02f, + 1.30001119e-02f, 9.94981546e-03f, 6.81929403e-03f, 3.63610142e-03f, + 4.28194737e-04f, -2.77634627e-03f, -5.94948890e-03f, -9.06356635e-03f, + -1.20914729e-02f, -1.50068986e-02f, -1.77846186e-02f, -2.04006305e-02f, + -2.28323995e-02f, -2.50590554e-02f, -2.70615857e-02f, -2.88229378e-02f, + -3.03282395e-02f, -3.15648981e-02f, -3.25226545e-02f, -3.31937480e-02f, + -3.35729235e-02f, -3.36575141e-02f, -3.34474213e-02f, -3.29451007e-02f, + -3.21556137e-02f, -3.10864769e-02f, -2.97476700e-02f, -2.81514985e-02f, + -2.63124688e-02f, -2.42472323e-02f, -2.19743519e-02f, -1.95141654e-02f, + -1.68886307e-02f, -1.41210735e-02f, -1.12360633e-02f, -8.25907766e-03f, + -5.21640280e-03f, -2.13480364e-03f, 9.58636625e-04f, 4.03679668e-03f, + 7.07279449e-03f, 1.00401133e-02f, 1.29129091e-02f, 1.56662400e-02f, + 1.82762701e-02f, 2.07204296e-02f, 2.29776718e-02f, 2.50286615e-02f, + 2.68558574e-02f, 2.84437511e-02f, 2.97789600e-02f, 3.08503332e-02f, + 3.16490674e-02f, 3.21687669e-02f, 3.24054635e-02f, 3.23577221e-02f, + 3.20265687e-02f, 3.14155519e-02f, 3.05305894e-02f, 2.93800783e-02f, + 2.79746552e-02f, 2.63272236e-02f, 2.44527049e-02f, 2.23680618e-02f, + 2.00919975e-02f, 1.76449192e-02f, 1.50485988e-02f, 1.23261499e-02f, + 9.50171139e-03f, 6.60026650e-03f, 3.64740367e-03f, 6.69130719e-04f, + -2.30836384e-03f, -5.25901398e-03f, -8.15698453e-03f, -1.09769803e-02f, + -1.36944604e-02f, -1.62858144e-02f, -1.87285884e-02f, -2.10016919e-02f, + -2.30855512e-02f, -2.49623180e-02f, -2.66159983e-02f, -2.80325244e-02f, + -2.92000656e-02f, -3.01088544e-02f, -3.07515454e-02f, -3.11230425e-02f, + -3.12207134e-02f, -3.10442642e-02f, -3.05958739e-02f, -2.98800667e-02f, + -2.89037056e-02f, -2.76759017e-02f, -2.62080378e-02f, -2.45134730e-02f, + -2.26075902e-02f, -2.05075661e-02f, -1.82322785e-02f, -1.58020520e-02f, + -1.32385765e-02f, -1.05646147e-02f, -7.80391393e-03f, -4.98087115e-03f, + -2.12039991e-03f, 7.52293928e-04f, 3.61198449e-03f, 6.43358111e-03f, + 9.19241842e-03f, 1.18644039e-02f, 1.44262640e-02f, 1.68557443e-02f, + 1.91318358e-02f, 2.12348654e-02f, 2.31467521e-02f, 2.48511451e-02f, + 2.63335090e-02f, 2.75813428e-02f, 2.85841744e-02f, 2.93337504e-02f, + 2.98240644e-02f, 3.00513466e-02f, 3.00142022e-02f, 2.97135260e-02f, + 2.91525594e-02f, 2.83367887e-02f, 2.72739123e-02f, 2.59738760e-02f, + 2.44485621e-02f, 2.27119106e-02f, 2.07795919e-02f, 1.86690808e-02f, + 1.63992592e-02f, 1.39904260e-02f, 1.14640690e-02f, 8.84264758e-03f, + 6.14939507e-03f, 3.40812526e-03f, 6.43054198e-04f, -2.12146966e-03f, + -4.86117838e-03f, -7.55201827e-03f, -1.01704721e-02f, -1.26936743e-02f, + -1.50996598e-02f, -1.73675327e-02f, -1.94776873e-02f, -2.14119119e-02f, + -2.31535849e-02f, -2.46878274e-02f, -2.60016123e-02f, -2.70838807e-02f, + -2.79256152e-02f, -2.85199737e-02f, -2.88622578e-02f, -2.89500170e-02f, + -2.87830391e-02f, -2.83633782e-02f, -2.76952574e-02f, -2.67851241e-02f, + -2.56414895e-02f, -2.42749591e-02f, -2.26980211e-02f, -2.09250117e-02f, + -1.89719767e-02f, -1.68564622e-02f, -1.45974790e-02f, -1.22151551e-02f, + -9.73077489e-03f, -7.16636971e-03f, -4.54471089e-03f, -1.88895556e-03f, + 7.77421152e-04f, 3.43097465e-03f, 6.04836823e-03f, 8.60665740e-03f, + 1.10834931e-02f, 1.34572307e-02f, 1.57072091e-02f, 1.78139437e-02f, + 1.97591848e-02f, 2.15261896e-02f, 2.30998051e-02f, 2.44666019e-02f, + 2.56150329e-02f, 2.65354493e-02f, 2.72202868e-02f, 2.76640272e-02f, + 2.78632920e-02f, 2.78168878e-02f, 2.75257711e-02f, 2.69930474e-02f, + 2.62239398e-02f, 2.52257420e-02f, 2.40077502e-02f, 2.25811821e-02f, + 2.09590651e-02f, 1.91560968e-02f, 1.71885831e-02f, 1.50741718e-02f, + 1.28318640e-02f, 1.04816157e-02f, 8.04442313e-03f, 5.54190051e-03f, + 2.99625439e-03f, 4.29964517e-04f, -2.13427934e-03f, -4.67394770e-03f, + -7.16668999e-03f, -9.59070046e-03f, -1.19247527e-02f, -1.41485167e-02f, + -1.62426221e-02f, -1.81889386e-02f, -1.99706517e-02f, -2.15724419e-02f, + -2.29805917e-02f, -2.41831321e-02f, -2.51699460e-02f, -2.59328014e-02f, + -2.64654988e-02f, -2.67638473e-02f, -2.68257457e-02f, -2.66511933e-02f, + -2.62422396e-02f, -2.56030378e-02f, -2.47397026e-02f, -2.36603803e-02f, + -2.23750178e-02f, -2.08954572e-02f, -1.92351291e-02f, -1.74091057e-02f, + -1.54338252e-02f, -1.33270371e-02f, -1.11076012e-02f, -8.79532116e-03f, + -6.41081337e-03f, -3.97522070e-03f, -1.51016205e-03f, 9.62627810e-04f, + 3.42126524e-03f, 5.84416900e-03f, 8.21003607e-03f, 1.04981418e-02f, + 1.26884979e-02f, 1.47620168e-02f, 1.67006743e-02f, 1.84877071e-02f, + 2.01076631e-02f, 2.15466723e-02f, 2.27924559e-02f, 2.38344391e-02f, + 2.46639202e-02f, 2.52740508e-02f, 2.56599603e-02f, 2.58187383e-02f, + 2.57495061e-02f, 2.54533889e-02f, 2.49335063e-02f, 2.41949679e-02f, + 2.32447443e-02f, 2.20917330e-02f, 2.07465369e-02f, 1.92214548e-02f, + 1.75303581e-02f, 1.56885248e-02f, 1.37125582e-02f, 1.16201731e-02f, + 9.43010247e-03f, 7.16186261e-03f, 4.83567361e-03f, 2.47215188e-03f, + 9.22473038e-05f, -2.28300123e-03f, -4.63266587e-03f, -6.93607706e-03f, + -9.17298978e-03f, -1.13238544e-02f, -1.33698760e-02f, -1.52932430e-02f, + -1.70772680e-02f, -1.87065128e-02f, -2.01669821e-02f, -2.14461452e-02f, + -2.25331284e-02f, -2.34188021e-02f, -2.40957864e-02f, -2.45585844e-02f, + -2.48036252e-02f, -2.48292044e-02f, -2.46356329e-02f, -2.42250842e-02f, + -2.36017013e-02f, -2.27714652e-02f, -2.17421665e-02f, -2.05233690e-02f, + -1.91262271e-02f, -1.75635217e-02f, -1.58493869e-02f, -1.39993165e-02f, + -1.20299498e-02f, -9.95894696e-03f, -7.80483551e-03f, -5.58678574e-03f, + -3.32457334e-03f, -1.03821621e-03f, 1.25199524e-03f, 3.52587224e-03f, + 5.76334794e-03f, 7.94474634e-03f, 1.00509704e-02f, 1.20635647e-02f, + 1.39649989e-02f, 1.57386972e-02f, 1.73693364e-02f, 1.88428192e-02f, + 2.01465043e-02f, 2.12692517e-02f, 2.22015935e-02f, 2.29357192e-02f, + 2.34655795e-02f, 2.37870006e-02f, 2.38975984e-02f, 2.37969007e-02f, + 2.34862414e-02f, 2.29689158e-02f, 2.22499000e-02f, 2.13360637e-02f, + 2.02358982e-02f, 1.89595527e-02f, 1.75187187e-02f, 1.59264857e-02f, + 1.41973055e-02f, 1.23467171e-02f, 1.03913879e-02f, 8.34881663e-03f, + 6.23727188e-03f, 4.07554898e-03f, 1.88285221e-03f, -3.21348688e-04f, + -2.51757106e-03f, -4.68641464e-03f, -6.80879709e-03f, -8.86604146e-03f, + -1.08401587e-02f, -1.27138656e-02f, -1.44708646e-02f, -1.60958743e-02f, + -1.75748613e-02f, -1.88950773e-02f, -2.00452523e-02f, -2.10155784e-02f, + -2.17979173e-02f, -2.23857954e-02f, -2.27744512e-02f, -2.29609363e-02f, + -2.29440496e-02f, -2.27244286e-02f, -2.23044963e-02f, -2.16883864e-02f, + -2.08820436e-02f, -1.98930467e-02f, -1.87304962e-02f, -1.74051536e-02f, + -1.59290505e-02f, -1.43156476e-02f, -1.25794710e-02f, -1.07361870e-02f, + -8.80234289e-03f, -6.79523707e-03f, -4.73276287e-03f, -2.63333832e-03f, + -5.15560833e-04f, 1.60177115e-03f, 3.69994169e-03f, 5.76046713e-03f, + 7.76518011e-03f, 9.69651950e-03f, 1.15375588e-02f, 1.32722403e-02f, + 1.48854641e-02f, 1.63632477e-02f, 1.76928345e-02f, 1.88628539e-02f, + 1.98632836e-02f, 2.06857078e-02f, 2.13232370e-02f, 2.17707035e-02f, + 2.20245710e-02f, 2.20830689e-02f, 2.19461035e-02f, 2.16153999e-02f, + 2.10942875e-02f, 2.03878531e-02f, 1.95027602e-02f, 1.84472879e-02f, + 1.72311466e-02f, 1.58654896e-02f, 1.43627402e-02f, 1.27365006e-02f, + 1.10014956e-02f, 9.17326248e-03f, 7.26826777e-03f, 5.30349659e-03f, + 3.29647574e-03f, 1.26509823e-03f, -7.72666021e-04f, -2.79870526e-03f, + -4.79517508e-03f, -6.74446397e-03f, -8.62941284e-03f, -1.04335350e-02f, + -1.21410266e-02f, -1.37370298e-02f, -1.52076630e-02f, -1.65402345e-02f, + -1.77232647e-02f, -1.87466345e-02f, -1.96016877e-02f, -2.02812328e-02f, + -2.07796995e-02f, -2.10930662e-02f, -2.12190342e-02f, -2.11569310e-02f, + -2.09077424e-02f, -2.04741102e-02f, -1.98603315e-02f, -1.90722574e-02f, + -1.81172824e-02f, -1.70042570e-02f, -1.57433882e-02f, -1.43462276e-02f, + -1.28254403e-02f, -1.11947772e-02f, -9.46895912e-03f, -7.66347070e-03f, + -5.79450864e-03f, -3.87873939e-03f, -1.93323810e-03f, 2.46644329e-05f, + 1.97765804e-03f, 3.90841509e-03f, 5.79993335e-03f, 7.63553849e-03f, + 9.39912349e-03f, 1.10752347e-02f, 1.26492655e-02f, 1.41074757e-02f, + 1.54373004e-02f, 1.66272192e-02f, 1.76670438e-02f, 1.85479358e-02f, + 1.92624646e-02f, 1.98047255e-02f, 2.01703008e-02f, 2.03564026e-02f, + 2.03618038e-02f, 2.01868907e-02f, 1.98336400e-02f, 1.93056297e-02f, + 1.86079226e-02f, 1.77471409e-02f, 1.67312987e-02f, 1.55697372e-02f, + 1.42731251e-02f, 1.28532513e-02f, 1.13230121e-02f, 9.69619751e-03f, + 7.98747524e-03f, 6.21217610e-03f, 4.38617553e-03f, 2.52576378e-03f, + 6.47513831e-04f, -1.23187933e-03f, -3.09578918e-03f, -4.92771934e-03f, + -6.71155785e-03f, -8.43156989e-03f, -1.00726980e-02f, -1.16206095e-02f, + -1.30617878e-02f, -1.43837508e-02f, -1.55750228e-02f, -1.66254535e-02f, + -1.75260165e-02f, -1.82691095e-02f, -1.88485262e-02f, -1.92595629e-02f, + -1.94989485e-02f, -1.95649969e-02f, -1.94575478e-02f, -1.91779692e-02f, + -1.87291588e-02f, -1.81155240e-02f, -1.73428622e-02f, -1.64184497e-02f, + -1.53508419e-02f, -1.41498176e-02f, -1.28263924e-02f, -1.13925480e-02f, + -9.86129571e-03f, -8.24642962e-03f, -6.56245985e-03f, -4.82449803e-03f, + -3.04804665e-03f, -1.24899186e-03f, 5.56712448e-04f, 2.35302268e-03f, + 4.12404102e-03f, 5.85413883e-03f, 7.52808495e-03f, 9.13118114e-03f, + 1.06493773e-02f, 1.20694285e-02f, 1.33790131e-02f, 1.45667599e-02f, + 1.56224667e-02f, 1.65370727e-02f, 1.73028687e-02f, 1.79133659e-02f, + 1.83636049e-02f, 1.86499152e-02f, 1.87702262e-02f, 1.87238401e-02f, + 1.85116123e-02f, 1.81358025e-02f, 1.76001712e-02f, 1.69098379e-02f, + 1.60713269e-02f, 1.50924404e-02f, 1.39821625e-02f, 1.27507290e-02f, + 1.14093145e-02f, 9.97008458e-03f, 8.44606386e-03f, 6.85095093e-03f, + 5.19909294e-03f, 3.50524621e-03f, 1.78455190e-03f, 5.23496112e-05f, + -1.67599378e-03f, -3.38516088e-03f, -5.06003030e-03f, -6.68585576e-03f, + -8.24832082e-03f, -9.73374187e-03f, -1.11291228e-02f, -1.24223220e-02f, + -1.36021312e-02f, -1.46583598e-02f, -1.55819659e-02f, -1.63650578e-02f, + -1.70010772e-02f, -1.74847152e-02f, -1.78120952e-02f, -1.79806821e-02f, + -1.79893879e-02f, -1.78385292e-02f, -1.75298522e-02f, -1.70664857e-02f, + -1.64529222e-02f, -1.56949858e-02f, -1.47997517e-02f, -1.37755159e-02f, + -1.26316843e-02f, -1.13787184e-02f, -1.00279612e-02f, -8.59169598e-03f, + -7.08284107e-03f, -5.51497784e-03f, -3.90215899e-03f, -2.25882049e-03f, + -5.99578638e-04f, 1.06075048e-03f, 2.70749024e-03f, 4.32601367e-03f, + 5.90209087e-03f, 7.42179142e-03f, 8.87180147e-03f, 1.02394295e-02f, + 1.15127513e-02f, 1.26806976e-02f, 1.37331768e-02f, 1.46611542e-02f, + 1.54567137e-02f, 1.61130908e-02f, 1.66248644e-02f, 1.69878247e-02f, + 1.71991520e-02f, 1.72573319e-02f, 1.71622504e-02f, 1.69151533e-02f, + 1.65185918e-02f, 1.59764761e-02f, 1.52940069e-02f, 1.44775476e-02f, + 1.35347439e-02f, 1.24742225e-02f, 1.13057588e-02f, 1.00399437e-02f, + 8.68828286e-03f, 7.26299632e-03f, 5.77689293e-03f, 4.24335301e-03f, + 2.67608005e-03f, 1.08909445e-03f, -5.03504096e-04f, -2.08753398e-03f, + -3.64897310e-03f, -5.17400648e-03f, -6.64921573e-03f, -8.06161447e-03f, + -9.39882273e-03f, -1.06491934e-02f, -1.18018231e-02f, -1.28467447e-02f, + -1.37749479e-02f, -1.45785068e-02f, -1.52505941e-02f, -1.57855954e-02f, + -1.61790941e-02f, -1.64279942e-02f, -1.65304126e-02f, -1.64858700e-02f, + -1.62950933e-02f, -1.59601657e-02f, -1.54844596e-02f, -1.48725186e-02f, + -1.41301516e-02f, -1.32642739e-02f, -1.22829116e-02f, -1.11950413e-02f, + -1.00106329e-02f, -8.74042472e-03f, -7.39593243e-03f, -5.98926755e-03f, + -4.53309924e-03f, -3.04046635e-03f, -1.52472960e-03f, 6.00928259e-07f, + 1.52194755e-03f, 3.02583848e-03f, 4.49892816e-03f, 5.92824223e-03f, + 7.30119202e-03f, 8.60573140e-03f, 9.83048696e-03f, 1.09647391e-02f, + 1.19986831e-02f, 1.29234016e-02f, 1.37309351e-02f, 1.44144670e-02f, + 1.49682002e-02f, 1.53875953e-02f, 1.56692643e-02f, 1.58110475e-02f, + 1.58120917e-02f, 1.56726915e-02f, 1.53945244e-02f, 1.49803712e-02f, + 1.44342905e-02f, 1.37614814e-02f, 1.29682165e-02f, 1.20618964e-02f, + 1.10508386e-02f, 9.94431276e-03f, 8.75238815e-03f, 7.48587143e-03f, + 6.15624020e-03f, 4.77541534e-03f, 3.35583886e-03f, 1.91020436e-03f, + 4.51421254e-04f, -1.00751978e-03f, -2.45365701e-03f, -3.87418456e-03f, + -5.25654068e-03f, -6.58854736e-03f, -7.85852969e-03f, -9.05533440e-03f, + -1.01685578e-02f, -1.11885037e-02f, -1.21063745e-02f, -1.29142921e-02f, + -1.36053355e-02f, -1.41737244e-02f, -1.46146963e-02f, -1.49246914e-02f, + -1.51012942e-02f, -1.51432786e-02f, -1.50506566e-02f, -1.48245834e-02f, + -1.44674302e-02f, -1.39827154e-02f, -1.33750864e-02f, -1.26502768e-02f, + -1.18150098e-02f, -1.08770275e-02f, -9.84492957e-03f, -8.72812658e-03f, + -7.53678810e-03f, -6.28165584e-03f, -4.97410178e-03f, -3.62585109e-03f, + -2.24899664e-03f, -8.55861568e-04f, 5.41158727e-04f, 1.92960471e-03f, + 3.29720069e-03f, 4.63182847e-03f, 5.92171512e-03f, 7.15552772e-03f, + 8.32245104e-03f, 9.41229766e-03f, 1.04155873e-02f, 1.13236403e-02f, + 1.21286239e-02f, 1.28237067e-02f, 1.34029530e-02f, 1.38615561e-02f, + 1.41957640e-02f, 1.44029303e-02f, 1.44815754e-02f, 1.44313144e-02f, + 1.42529830e-02f, 1.39484805e-02f, 1.35208513e-02f, 1.29742567e-02f, + 1.23138195e-02f, 1.15457887e-02f, 1.06772196e-02f, 9.71614290e-03f, + 8.67133403e-03f, 7.55228769e-03f, 6.36919353e-03f, 5.13268639e-03f, + 3.85394864e-03f, 2.54443240e-03f, 1.21585789e-03f, -1.19878996e-04f, + -1.45093919e-03f, -2.76547718e-03f, -4.05185149e-03f, -5.29871877e-03f, + -6.49507635e-03f, -7.63046174e-03f, -8.69492807e-03f, -9.67921631e-03f, + -1.05747852e-02f, -1.13739326e-02f, -1.20697971e-02f, -1.26564969e-02f, + -1.31290964e-02f, -1.34837229e-02f, -1.37175190e-02f, -1.38287732e-02f, + -1.38167986e-02f, -1.36820574e-02f, -1.34260714e-02f, -1.30514744e-02f, + -1.25618820e-02f, -1.19619814e-02f, -1.12574303e-02f, -1.04547257e-02f, + -9.56133206e-03f, -8.58538654e-03f, -7.53583412e-03f, -6.42217872e-03f, + -5.25452181e-03f, -4.04336901e-03f, -2.79962633e-03f, -1.53443002e-03f, + -2.59098789e-04f, 1.01502741e-03f, 2.27659752e-03f, 3.51443317e-03f, + 4.71761329e-03f, 5.87551504e-03f, 6.97798012e-03f, 8.01534309e-03f, + 8.97859709e-03f, 9.85934513e-03f, 1.06500123e-02f, 1.13437857e-02f, + 1.19348028e-02f, 1.24180671e-02f, 1.27895845e-02f, 1.30463879e-02f, + 1.31864572e-02f, 1.32089360e-02f, 1.31138884e-02f, 1.29025483e-02f, + 1.25770694e-02f, 1.21406815e-02f, 1.15975855e-02f, 1.09528847e-02f, + 1.02125997e-02f, 9.38359994e-03f, 8.47347906e-03f, 7.49058151e-03f, + 6.44381432e-03f, 5.34271678e-03f, 4.19719189e-03f, 3.01755365e-03f, + 1.81439519e-03f, 5.98487206e-04f, -6.19328778e-04f, -1.82821924e-03f, + -3.01745871e-03f, -4.17650106e-03f, -5.29517520e-03f, -6.36359283e-03f, + -7.37244199e-03f, -8.31286411e-03f, -9.17674098e-03f, -9.95657112e-03f, + -1.06456657e-02f, -1.12381503e-02f, -1.17290432e-02f, -1.21142223e-02f, + -1.23906200e-02f, -1.25560393e-02f, -1.26093357e-02f, -1.25503481e-02f, + -1.23799279e-02f, -1.20999009e-02f, -1.17130697e-02f, -1.12231760e-02f, + -1.06348586e-02f, -9.95364282e-03f, -9.18584158e-03f, -8.33854107e-03f, + -7.41947999e-03f, -6.43706907e-03f, -5.40019213e-03f, -4.31826103e-03f, + -3.20099965e-03f, -2.05845707e-03f, -9.00865349e-04f, 2.61441292e-04f, + 1.41809108e-03f, 2.55883321e-03f, 3.67354889e-03f, 4.75241753e-03f, + 5.78592427e-03f, 6.76501988e-03f, 7.68115315e-03f, 8.52633033e-03f, + 9.29324902e-03f, 9.97532856e-03f, 1.05667189e-02f, 1.10624375e-02f, + 1.14583325e-02f, 1.17511845e-02f, 1.19386891e-02f, 1.20194307e-02f, + 1.19930645e-02f, 1.18600946e-02f, 1.16220073e-02f, 1.12812470e-02f, + 1.08411159e-02f, 1.03058419e-02f, 9.68042693e-03f, 8.97072807e-03f, + 8.18325916e-03f, 7.32531818e-03f, 6.40466057e-03f, 5.42973409e-03f, + 4.40930059e-03f, 3.35261692e-03f, 2.26916087e-03f, 1.16867964e-03f, + 6.09764122e-05f, -1.04405158e-03f, -2.13657269e-03f, -3.20691908e-03f, + -4.24563108e-03f, -5.24355860e-03f, -6.19193381e-03f, -7.08246398e-03f, + -7.90741664e-03f, -8.65959818e-03f, -9.33257195e-03f, -9.92055171e-03f, + -1.04185689e-02f, -1.08224536e-02f, -1.11288669e-02f, -1.13354045e-02f, + -1.14404744e-02f, -1.14434825e-02f, -1.13446735e-02f, -1.11452105e-02f, + -1.08472074e-02f, -1.04535649e-02f, -9.96807850e-03f, -9.39533832e-03f, + -8.74069245e-03f, -8.01020524e-03f, -7.21061702e-03f, -6.34920826e-03f, + -5.43385500e-03f, -4.47285238e-03f, -3.47490761e-03f, -2.44893717e-03f, + -1.40424120e-03f, -3.50117524e-04f, 7.03996367e-04f, 1.74874948e-03f, + 2.77486316e-03f, 3.77323529e-03f, 4.73510696e-03f, 5.65199516e-03f, + 6.51589535e-03f, 7.31925060e-03f, 8.05510111e-03f, 8.71711178e-03f, + 9.29960810e-03f, 9.79762661e-03f, 1.02069851e-02f, 1.05243267e-02f, + 1.07470734e-02f, 1.08735440e-02f, 1.09028797e-02f, 1.08351360e-02f, + 1.06711793e-02f, 1.04127447e-02f, 1.00624520e-02f, 9.62365700e-03f, + 9.10054672e-03f, 8.49802459e-03f, 7.82168998e-03f, 7.07776632e-03f, + 6.27310545e-03f, 5.41501721e-03f, 4.51131894e-03f, 3.57017926e-03f, + 2.60009355e-03f, 1.60979968e-03f, 6.08111937e-04f, -3.95951416e-04f, + -1.39349410e-03f, -2.37563261e-03f, -3.33368651e-03f, -4.25918973e-03f, + -5.14402447e-03f, -5.98039909e-03f, -6.76106610e-03f, -7.47919350e-03f, + -8.12860686e-03f, -8.70374621e-03f, -9.19968280e-03f, -9.61226566e-03f, + -9.93805155e-03f, -1.01744272e-02f, -1.03195556e-02f, -1.03723935e-02f, + -1.03327824e-02f, -1.02013418e-02f, -9.97951235e-03f, -9.66955161e-03f, + -9.27446962e-03f, -8.79806255e-03f, -8.24481164e-03f, -7.61986353e-03f, + -6.92898907e-03f, -6.17857782e-03f, -5.37544863e-03f, -4.52693679e-03f, + -3.64070939e-03f, -2.72478903e-03f, -1.78741480e-03f, -8.36980429e-04f, + 1.17989664e-04f, 1.06900538e-03f, 2.00758934e-03f, 2.92546046e-03f, + 3.81446851e-03f, 4.66681294e-03f, 5.47503707e-03f, 6.23202642e-03f, + 6.93126676e-03f, 7.56665434e-03f, 8.13275794e-03f, 8.62476397e-03f, + 9.03846441e-03f, 9.37048808e-03f, 9.61806678e-03f, 9.77929762e-03f, + 9.85300343e-03f, 9.83878137e-03f, 9.73705126e-03f, 9.54895686e-03f, + 9.27646789e-03f, 8.92224731e-03f, 8.48973525e-03f, 7.98296668e-03f, + 7.40673818e-03f, 6.76635688e-03f, 6.06774523e-03f, 5.31727151e-03f, + 4.52181520e-03f, 3.68856414e-03f, 2.82508906e-03f, 1.93911771e-03f, + 1.03864165e-03f, 1.31724000e-04f, -7.73548104e-04f, -1.66912498e-03f, + -2.54705639e-03f, -3.39960239e-03f, -4.21921926e-03f, -4.99874677e-03f, + -5.73135156e-03f, -6.41064787e-03f, -7.03078893e-03f, -7.58638515e-03f, + -8.07274239e-03f, -8.48573382e-03f, -8.82189842e-03f, -9.07847965e-03f, + -9.25348106e-03f, -9.34553774e-03f, -9.35415328e-03f, -9.27944192e-03f, + -9.12241341e-03f, -8.88466411e-03f, -8.56856089e-03f, -8.17721225e-03f, + -7.71429404e-03f, -7.18416104e-03f, -6.59175983e-03f, -5.94253368e-03f, + -5.24247041e-03f, -4.49794039e-03f, -3.71571847e-03f, -2.90290602e-03f, + -2.06680152e-03f, -1.21494821e-03f, -3.54960536e-04f, 5.05456753e-04f, + 1.35866818e-03f, 2.19707251e-03f, 3.01328571e-03f, 3.80007591e-03f, + 4.55055509e-03f, 5.25814438e-03f, 5.91669192e-03f, 6.52047200e-03f, + 7.06430640e-03f, 7.54353794e-03f, 7.95408283e-03f, 8.29252663e-03f, + 8.55606304e-03f, 8.74262824e-03f, 8.85075236e-03f, 8.87975906e-03f, + 8.82962202e-03f, 8.70105451e-03f, 8.49545293e-03f, 8.21486139e-03f, + 7.86207328e-03f, 7.44038974e-03f, 6.95385180e-03f, 6.40695971e-03f, + 5.80479502e-03f, 5.15286870e-03f, 4.45718246e-03f, 3.72404945e-03f, + 2.96010221e-03f, 2.17226938e-03f, 1.36762733e-03f, 5.53405381e-04f, + -2.63146484e-04f, -1.07470419e-03f, -1.87409443e-03f, -2.65421924e-03f, + -3.40822243e-03f, -4.12944508e-03f, -4.81156866e-03f, -5.44867726e-03f, + -6.03520323e-03f, -6.56613683e-03f, -7.03688002e-03f, -7.44347988e-03f, + -7.78249135e-03f, -8.05112205e-03f, -8.24721385e-03f, -8.36925002e-03f, + -8.41640683e-03f, -8.38846392e-03f, -8.28595912e-03f, -8.11001020e-03f, + -7.86244307e-03f, -7.54569095e-03f, -7.16278595e-03f, -6.71738300e-03f, + -6.21361469e-03f, -5.65619404e-03f, -5.05022152e-03f, -4.40127236e-03f, + -3.71526815e-03f, -2.99841577e-03f, -2.25722112e-03f, -1.49836763e-03f, + -7.28642071e-04f, 4.50434477e-05f, 8.15794780e-04f, 1.57675983e-03f, + 2.32119303e-03f, 3.04253373e-03f, 3.73440374e-03f, 4.39076109e-03f, + 5.00588458e-03f, 5.57441293e-03f, 6.09144353e-03f, 6.55256260e-03f, + 6.95381103e-03f, 7.29184996e-03f, 7.56385409e-03f, 7.76763312e-03f, + 7.90157987e-03f, 7.96474929e-03f, 7.95679715e-03f, 7.87804369e-03f, + 7.72942509e-03f, 7.51247337e-03f, 7.22938137e-03f, 6.88285636e-03f, + 6.47620933e-03f, 6.01325566e-03f, 5.49830484e-03f, 4.93612206e-03f, + 4.33185515e-03f, 3.69104049e-03f, 3.01948602e-03f, 2.32330330e-03f, + 1.60874544e-03f, 8.82231714e-04f, 1.50308827e-04f, -5.80528317e-04f, + -1.30375322e-03f, -2.01296135e-03f, -2.70188199e-03f, -3.36444701e-03f, + -3.99483762e-03f, -4.58758013e-03f, -5.13746453e-03f, -5.63980390e-03f, + -6.09023428e-03f, -6.48495361e-03f, -6.82059867e-03f, -7.09440613e-03f, + -7.30414154e-03f, -7.44814383e-03f, -7.52537074e-03f, -7.53532231e-03f, + -7.47817928e-03f, -7.35462729e-03f, -7.16604653e-03f, -6.91428861e-03f, + -6.60184485e-03f, -6.23166749e-03f, -5.80727376e-03f, -5.33260451e-03f, + -4.81206874e-03f, -4.25046030e-03f, -3.65290532e-03f, -3.02486716e-03f, + -2.37201464e-03f, -1.70026266e-03f, -1.01565670e-03f, -3.24326012e-04f, + 3.67549442e-04f, 1.05380850e-03f, 1.72833677e-03f, 2.38519657e-03f, + 3.01857597e-03f, 3.62294858e-03f, 4.19299252e-03f, 4.72378606e-03f, + 5.21069555e-03f, 5.64957232e-03f, 6.03664967e-03f, 6.36867744e-03f, + 6.64284990e-03f, 6.85698418e-03f, 7.00931443e-03f, 7.09874441e-03f, + 7.12465840e-03f, 7.08706888e-03f, 6.98651094e-03f, 6.82410178e-03f, + 6.60150306e-03f, 6.32090521e-03f, 5.98500306e-03f, 5.59697420e-03f, + 5.16044998e-03f, 4.67947995e-03f, 4.15852195e-03f, 3.60234011e-03f, + 3.01599436e-03f, 2.40483676e-03f, 1.77436911e-03f, 1.13028995e-03f, + 4.78371791e-04f, -1.75557439e-04f, -8.25650621e-04f, -1.46616696e-03f, + -2.09139965e-03f, -2.69583389e-03f, -3.27419516e-03f, -3.82140068e-03f, + -4.33268241e-03f, -4.80363658e-03f, -5.23017010e-03f, -5.60867917e-03f, + -5.93592714e-03f, -6.20921825e-03f, -6.42625503e-03f, -6.58536384e-03f, + -6.68526899e-03f, -6.72533025e-03f, -6.70537034e-03f, -6.62579421e-03f, + -6.48752153e-03f, -6.29196633e-03f, -6.04110074e-03f, -5.73732501e-03f, + -5.38355191e-03f, -4.98307960e-03f, -4.53965639e-03f, -4.05738219e-03f, + -3.54068617e-03f, -2.99427320e-03f, -2.42313171e-03f, -1.83242705e-03f, + -1.22748267e-03f, -6.13742401e-04f, 3.29459964e-06f, 6.18128643e-04f, + 1.22530705e-03f, 1.81942982e-03f, 2.39525993e-03f, 2.94773242e-03f, + 3.47202200e-03f, 3.96353949e-03f, 4.41805557e-03f, 4.83162874e-03f, + 5.20072081e-03f, 5.52222100e-03f, 5.79342983e-03f, 6.01209834e-03f, + 6.17646715e-03f, 6.28528285e-03f, 6.33778155e-03f, 6.33364321e-03f, + 6.27317175e-03f, 6.15705477e-03f, 5.98657179e-03f, 5.76336894e-03f, + 5.48968624e-03f, 5.16811657e-03f, 4.80167923e-03f, 4.39381474e-03f, + 3.94828981e-03f, 3.46922951e-03f, 2.96098404e-03f, 2.42819955e-03f, + 1.87572404e-03f, 1.30851770e-03f, 7.31708692e-04f, 1.50445075e-04f, + -4.30070196e-04f, -1.00466524e-03f, -1.56827393e-03f, -2.11588940e-03f, + -2.64269440e-03f, -3.14408074e-03f, -3.61568802e-03f, -4.05340575e-03f, + -4.45347457e-03f, -4.81245326e-03f, -5.12730726e-03f, -5.39539697e-03f, + -5.61447256e-03f, -5.78280084e-03f, -5.89903878e-03f, -5.96236515e-03f, + -5.97237627e-03f, -5.92920270e-03f, -5.83340844e-03f, -5.68605666e-03f, + -5.48859720e-03f, -5.24302551e-03f, -4.95168147e-03f, -4.61731774e-03f, + -4.24306545e-03f, -3.83242787e-03f, -3.38915992e-03f, -2.91735676e-03f, + -2.42128273e-03f, -1.90549963e-03f, -1.37460030e-03f, -8.33438466e-04f, + -2.86823004e-04f, 2.60354880e-04f, 8.03207784e-04f, 1.33692447e-03f, + 1.85678689e-03f, 2.35821923e-03f, 2.83683539e-03f, 3.28844174e-03f, + 3.70911927e-03f, 4.09525832e-03f, 4.44351520e-03f, 4.75094096e-03f, + 5.01496724e-03f, 5.23338423e-03f, 5.40441842e-03f, 5.52671520e-03f, + 5.59940305e-03f, 5.62196231e-03f, 5.59441894e-03f, 5.51717260e-03f, + 5.39113480e-03f, 5.21756593e-03f, 4.99821309e-03f, 4.73517171e-03f, + 4.43098991e-03f, 4.08848316e-03f, 3.71086536e-03f, 3.30161941e-03f, + 2.86452112e-03f, 2.40355058e-03f, 1.92289472e-03f, 1.42691829e-03f, + 9.20082667e-04f, 4.06947402e-04f, -1.07889236e-04f, -6.19866009e-04f, + -1.12440714e-03f, -1.61705134e-03f, -2.09347995e-03f, -2.54950186e-03f, + -2.98112018e-03f, -3.38459298e-03f, -3.75646023e-03f, -4.09349417e-03f, + -4.39283025e-03f, -4.65196264e-03f, -4.86872185e-03f, -5.04135627e-03f, + -5.16846121e-03f, -5.24911038e-03f, -5.28273546e-03f, -5.26923483e-03f, + -5.20889346e-03f, -5.10241936e-03f, -4.95095616e-03f, -4.75600604e-03f, + -4.51946213e-03f, -4.24360018e-03f, -3.93100678e-03f, -3.58463172e-03f, + -3.20766423e-03f, -2.80356963e-03f, -2.37606413e-03f, -1.92903332e-03f, + -1.46654246e-03f, -9.92737520e-04f, -5.11899001e-04f, -2.83386680e-05f, + 4.53646722e-04f, 9.29762308e-04f, 1.39578833e-03f, 1.84762733e-03f, + 2.28131825e-03f, 2.69304631e-03f, 3.07924546e-03f, 3.43657876e-03f, + 3.76196637e-03f, 4.05264982e-03f, 4.30615625e-03f, 4.52037923e-03f, + 4.69357270e-03f, 4.82432688e-03f, 4.91167463e-03f, 4.95498330e-03f, + 4.95403020e-03f, 4.90901290e-03f, 4.82048355e-03f, 4.68941823e-03f, + 4.51712335e-03f, 4.30530103e-03f, 4.05599901e-03f, 3.77155674e-03f, + 3.45466234e-03f, 3.10824592e-03f, 2.73552763e-03f, 2.33988646e-03f, + 1.92496688e-03f, 1.49452387e-03f, 1.05244323e-03f, 6.02711670e-04f, + 1.49351638e-04f, -3.03587769e-04f, -7.52061949e-04f, -1.19211664e-03f, + -1.61985030e-03f, -2.03151752e-03f, -2.42352388e-03f, -2.79243508e-03f, + -3.13508754e-03f, -3.44849890e-03f, -3.73002846e-03f, -3.97727137e-03f, + -4.18818648e-03f, -4.36099952e-03f, -4.49435996e-03f, -4.58719167e-03f, + -4.63889710e-03f, -4.64910091e-03f, -4.61791828e-03f, -4.54579073e-03f, + -4.43349692e-03f, -4.28220742e-03f, -4.09340522e-03f, -3.86894313e-03f, + -3.61094458e-03f, -3.32182985e-03f, -3.00430811e-03f, -2.66130866e-03f, + -2.29599013e-03f, -1.91166816e-03f, -1.51187551e-03f, -1.10018574e-03f, + -6.80346354e-04f, -2.56113849e-04f, 1.68739284e-04f, 5.90407275e-04f, + 1.00515855e-03f, 1.40933774e-03f, 1.79936566e-03f, 2.17186782e-03f, + 2.52357035e-03f, 2.85142336e-03f, 3.15262986e-03f, 3.42461005e-03f, + 3.66503089e-03f, 3.87190507e-03f, 4.04353460e-03f, 4.17850434e-03f, + 4.27578294e-03f, 4.33462985e-03f, 4.35470557e-03f, 4.33597602e-03f, + 4.27874072e-03f, 4.18369422e-03f, 4.05179951e-03f, 3.88441838e-03f, + 3.68314007e-03f, 3.44992044e-03f, 3.18696299e-03f, 2.89668435e-03f, + 2.58184280e-03f, 2.24526719e-03f, 1.89009368e-03f, 1.51951951e-03f, + 1.13694082e-03f, 7.45769400e-04f, 3.49541752e-04f, -4.81936756e-05f, + -4.43913653e-04f, -8.34083461e-04f, -1.21527427e-03f, -1.58411419e-03f, + -1.93737804e-03f, -2.27200055e-03f, -2.58505953e-03f, -2.87386789e-03f, + -3.13596261e-03f, -3.36909158e-03f, -3.57133916e-03f, -3.74099847e-03f, + -3.87671687e-03f, -3.97740006e-03f, -4.04231029e-03f, -4.07103135e-03f, + -4.06340767e-03f, -4.01970877e-03f, -3.94042889e-03f, -3.82644559e-03f, + -3.67889403e-03f, -3.49922796e-03f, -3.28921214e-03f, -3.05076130e-03f, + -2.78618777e-03f, -2.49791205e-03f, -2.18859644e-03f, -1.86109553e-03f, + -1.51839073e-03f, -1.16358358e-03f, -7.99877062e-04f, -4.30553251e-04f, + -5.89097879e-05f, 3.11723769e-04f, 6.78086247e-04f, 1.03688551e-03f, + 1.38501625e-03f, 1.71939231e-03f, 2.03710542e-03f, 2.33541156e-03f, + 2.61171872e-03f, 2.86367519e-03f, 3.08912751e-03f, 3.28618123e-03f, + 3.45323225e-03f, 3.58887288e-03f, 3.69206164e-03f, 3.76199260e-03f, + 3.79818934e-03f, 3.80048902e-03f, 3.76896561e-03f, 3.70409972e-03f, + 3.60655769e-03f, 3.47737266e-03f, 3.31778682e-03f, 3.12940296e-03f, + 2.91396911e-03f, 2.67353140e-03f, 2.41032316e-03f, 2.12679252e-03f, + 1.82553429e-03f, 1.50931293e-03f, 1.18097998e-03f, 8.43534445e-04f, + 4.99989066e-04f, 1.53447611e-04f, -1.93022636e-04f, -5.36321933e-04f, + -8.73416474e-04f, -1.20133088e-03f, -1.51720912e-03f, -1.81826798e-03f, + -2.10190341e-03f, -2.36567388e-03f, -2.60729607e-03f, -2.82474796e-03f, + -3.01616460e-03f, -3.17997100e-03f, -3.31484550e-03f, -3.41966897e-03f, + -3.49367342e-03f, -3.53630851e-03f, -3.54733556e-03f, -3.52679670e-03f, + -3.47499990e-03f, -3.39254703e-03f, -3.28028198e-03f, -3.13934064e-03f, + -2.97110336e-03f, -2.77718173e-03f, -2.55939692e-03f, -2.31979112e-03f, + -2.06058943e-03f, -1.78418388e-03f, -1.49308601e-03f, -1.18996918e-03f, + -8.77562261e-04f, -5.58694884e-04f, -2.36201878e-04f, 8.69917292e-05f, + 4.08040872e-04f, 7.24100044e-04f, 1.03237391e-03f, 1.33015630e-03f, + 1.61484341e-03f, 1.88395774e-03f, 2.13520669e-03f, 2.36638885e-03f, + 2.57554947e-03f, 2.76095026e-03f, 2.92101166e-03f, 3.05442850e-03f, + 3.16014757e-03f, 3.23732893e-03f, 3.28541223e-03f, 3.30412034e-03f, + 3.29335572e-03f, 3.25342718e-03f, 3.18472643e-03f, 3.08805315e-03f, + 2.96434808e-03f, 2.81484617e-03f, 2.64098428e-03f, 2.44441964e-03f, + 2.22698580e-03f, 1.99071671e-03f, 1.73777144e-03f, 1.47049147e-03f, + 1.19131101e-03f, 9.02757052e-04f, 6.07440484e-04f, 3.08007319e-04f, + 7.13829517e-06f, -2.92474511e-04f, -5.88197665e-04f, -8.77399733e-04f, + -1.15755209e-03f, -1.42622267e-03f, -1.68101236e-03f, -1.91978505e-03f, + -2.14045006e-03f, -2.34115054e-03f, -2.52017578e-03f, -2.67602742e-03f, + -2.80743844e-03f, -2.91334720e-03f, -2.99289427e-03f, -3.04551982e-03f, + -3.07087625e-03f, -3.06883997e-03f, -3.03955162e-03f, -2.98340720e-03f, + -2.90101796e-03f, -2.79322818e-03f, -2.66108957e-03f, -2.50591158e-03f, + -2.32917402e-03f, -2.13252284e-03f, -1.91779817e-03f, -1.68700449e-03f, + -1.44223748e-03f, -1.18575491e-03f, -9.19861684e-04f, -6.47001914e-04f, + -3.69593810e-04f, -9.01276712e-05f, 1.88886030e-04f, 4.64997876e-04f, + 7.35743579e-04f, 9.98752535e-04f, 1.25173547e-03f, 1.49246727e-03f, + 1.71888893e-03f, 1.92901822e-03f, 2.12110128e-03f, 2.29349620e-03f, + 2.44475112e-03f, 2.57362114e-03f, 2.67908845e-03f, 2.76026445e-03f, + 2.81660118e-03f, 2.84766807e-03f, 2.85331403e-03f, 2.83360095e-03f, + 2.78882988e-03f, 2.71949307e-03f, 2.62633897e-03f, 2.51027194e-03f, + 2.37244667e-03f, 2.21418592e-03f, 2.03698052e-03f, 1.84248796e-03f, + 1.63251333e-03f, 1.40901166e-03f, 1.17398423e-03f, 9.29629511e-04f, + 6.78093274e-04f, 4.21697134e-04f, 1.62708629e-04f, -9.65532846e-05f, + -3.53786150e-04f, -6.06698280e-04f, -8.53097342e-04f, -1.09078811e-03f, + -1.31773933e-03f, -1.53194961e-03f, -1.73160040e-03f, -1.91496699e-03f, + -2.08051810e-03f, -2.22683099e-03f, -2.35272161e-03f, -2.45716809e-03f, + -2.53932263e-03f, -2.59857031e-03f, -2.63448385e-03f, -2.64686900e-03f, + -2.63568789e-03f, -2.60117512e-03f, -2.54374911e-03f, -2.46399878e-03f, + -2.36276068e-03f, -2.24101080e-03f, -2.09994110e-03f, -1.94088932e-03f, + -1.76533620e-03f, -1.57492545e-03f, -1.37143402e-03f, -1.15668344e-03f, + -9.32686687e-04f, -7.01423675e-04f, -4.65019034e-04f, -2.25575437e-04f, + 1.47791969e-05f, 2.53869081e-04f, 4.89608281e-04f, 7.19930839e-04f, + 9.42775487e-04f, 1.15624415e-03f, 1.35846050e-03f, 1.54767351e-03f, + 1.72231203e-03f, 1.88082576e-03f, 2.02194509e-03f, 2.14444684e-03f, + 2.24738140e-03f, 2.32988384e-03f, 2.39134751e-03f, 2.43130004e-03f, + 2.44952080e-03f, 2.44590293e-03f, 2.42061909e-03f, 2.37398935e-03f, + 2.30650850e-03f, 2.21889198e-03f, 2.11201559e-03f, 1.98687753e-03f, + 1.84474091e-03f, 1.68687111e-03f, 1.51481261e-03f, 1.33010545e-03f, + 1.13447001e-03f, 9.29723262e-04f, 7.17657881e-04f, 5.00235175e-04f, + 2.79406846e-04f, 5.71322995e-05f, -1.64594545e-04f, -3.83823629e-04f, + -5.98614360e-04f, -8.07065822e-04f, -1.00739680e-03f, -1.19784818e-03f, + -1.37675697e-03f, -1.54263099e-03f, -1.69403924e-03f, -1.82971049e-03f, + -1.94850212e-03f, -2.04945536e-03f, -2.13177374e-03f, -2.19478544e-03f, + -2.23804061e-03f, -2.26125546e-03f, -2.26431492e-03f, -2.24728584e-03f, + -2.21038704e-03f, -2.15409950e-03f, -2.07896974e-03f, -1.98576777e-03f, + -1.87541310e-03f, -1.74895112e-03f, -1.60761319e-03f, -1.45267246e-03f, + -1.28561050e-03f, -1.10794701e-03f, -9.21319188e-04f, -7.27412747e-04f, + -5.28001974e-04f, -3.24849174e-04f, -1.19822475e-04f, 8.53071910e-05f, + 2.88686843e-04f, 4.88528049e-04f, 6.83070989e-04f, 8.70634797e-04f, + 1.04957014e-03f, 1.21833395e-03f, 1.37549860e-03f, 1.51969890e-03f, + 1.64975621e-03f, 1.76454677e-03f, 1.86314685e-03f, 1.94476478e-03f, + 2.00874558e-03f, 2.05462014e-03f, 2.08206459e-03f, 2.09092884e-03f, + 2.08122572e-03f, 2.05311829e-03f, 2.00696285e-03f, 1.94325570e-03f, + 1.86261554e-03f, 1.76589405e-03f, 1.65398613e-03f, 1.52796111e-03f, + 1.38902998e-03f, 1.23845862e-03f, 1.07765357e-03f, 9.08101811e-04f, + 7.31312372e-04f, 5.48925579e-04f, 3.62559457e-04f, 1.73908753e-04f, + -1.53640653e-05f, -2.03576240e-04f, -3.89073746e-04f, -5.70178373e-04f, + -7.45367890e-04f, -9.13091163e-04f, -1.07188249e-03f, -1.22041361e-03f, + -1.35740687e-03f, -1.48168528e-03f, -1.59225154e-03f, -1.68814122e-03f, + -1.76862655e-03f, -1.83303385e-03f, -1.88089694e-03f, -1.91186980e-03f, + -1.92575160e-03f, -1.92251421e-03f, -1.90226422e-03f, -1.86526958e-03f, + -1.81193893e-03f, -1.74283351e-03f, -1.65863573e-03f, -1.56017209e-03f, + -1.44838006e-03f, -1.32431143e-03f, -1.18916080e-03f, -1.04412159e-03f, + -8.90592600e-04f, -7.29926142e-04f, -5.63607604e-04f, -3.93123732e-04f, + -2.20015398e-04f, -4.58265535e-05f, 1.27888591e-04f, 2.99622014e-04f, + 4.67812693e-04f, 6.31045212e-04f, 7.87850352e-04f, 9.36918188e-04f, + 1.07691014e-03f, 1.20669763e-03f, 1.32512406e-03f, 1.43124633e-03f, + 1.52414726e-03f, 1.60310171e-03f, 1.66746035e-03f, 1.71674068e-03f, + 1.75057975e-03f, 1.76874501e-03f, 1.77117438e-03f, 1.75790455e-03f, + 1.72916148e-03f, 1.68523050e-03f, 1.62665041e-03f, 1.55393961e-03f, + 1.46788028e-03f, 1.36921441e-03f, 1.25897234e-03f, 1.13815311e-03f, + 1.00785213e-03f, 8.69331636e-04f, 7.23787860e-04f, 5.72621147e-04f, + 4.17123806e-04f, 2.58743469e-04f, 9.88837891e-05f, -6.10350755e-05f, + -2.19596167e-04f, -3.75409622e-04f, -5.27082337e-04f, -6.73344125e-04f, + -8.12882243e-04f, -9.44509077e-04f, -1.06712919e-03f, -1.17964492e-03f, + -1.28116892e-03f, -1.37084083e-03f, -1.44791745e-03f, -1.51179651e-03f, + -1.56196842e-03f, -1.59806342e-03f, -1.61984890e-03f, -1.62719129e-03f, + -1.62009091e-03f, -1.59871731e-03f, -1.56330213e-03f, -1.51425912e-03f, + -1.45207397e-03f, -1.37735224e-03f, -1.29085967e-03f, -1.19339166e-03f, + -1.08587214e-03f, -9.69313282e-04f, -8.44784217e-04f, -7.13452738e-04f, + -5.76480508e-04f, -4.35102909e-04f, -2.90654344e-04f, -1.44369163e-04f, + 2.42120883e-06f, 1.48447430e-04f, 2.92370866e-04f, 4.32973514e-04f, + 5.68999279e-04f, 6.99308513e-04f, 8.22732435e-04f, 9.38238883e-04f, + 1.04484914e-03f, 1.14166499e-03f, 1.22788622e-03f, 1.30279910e-03f, + 1.36578274e-03f, 1.41640462e-03f, 1.45420013e-03f, 1.47896611e-03f, + 1.49053972e-03f, 1.48884571e-03f, 1.47404145e-03f, 1.44625672e-03f, + 1.40585004e-03f, 1.35323924e-03f, 1.28894075e-03f, 1.21358722e-03f, + 1.12792237e-03f, 1.03272450e-03f, 9.28938538e-04f, 8.17461092e-04f, + 6.99372886e-04f, 5.75717626e-04f, 4.47641236e-04f, 3.16292734e-04f, + 1.82841969e-04f, 4.84802779e-05f, -8.55739910e-05f, -2.18169080e-04f, + -3.48142521e-04f, -4.74327437e-04f, -5.95661172e-04f, -7.11080885e-04f, + -8.19593186e-04f, -9.20314282e-04f, -1.01233129e-03f, -1.09493574e-03f, + -1.16743147e-03f, -1.22920752e-03f, -1.27981998e-03f, -1.31882788e-03f, + -1.34600511e-03f, -1.36114508e-03f, -1.36417035e-03f, -1.35514680e-03f, + -1.33419233e-03f, -1.30158504e-03f, -1.25764422e-03f, -1.20284876e-03f, + -1.13771466e-03f, -1.06289654e-03f, -9.79088444e-04f, -8.87105266e-04f, + -7.87774055e-04f, -6.82024906e-04f, -5.70838905e-04f, -4.55202296e-04f, + -3.36181145e-04f, -2.14839486e-04f, -9.22503227e-05f, 3.04698838e-05f, + 1.52270414e-04f, 2.72050892e-04f, 3.88799677e-04f, 5.01453830e-04f, + 6.09100748e-04f, 7.10751675e-04f, 8.05583159e-04f, 8.92801542e-04f, + 9.71649359e-04f, 1.04151118e-03f, 1.10177598e-03f, 1.15200217e-03f, + 1.19178629e-03f, 1.22083114e-03f, 1.23895814e-03f, 1.24603381e-03f, + 1.24209314e-03f, 1.22718978e-03f, 1.20157123e-03f, 1.16547661e-03f, + 1.11929301e-03f, 1.06350264e-03f, 9.98630850e-04f, 9.25308342e-04f, + 8.44227193e-04f, 7.56141297e-04f, 6.61877038e-04f, 5.62284265e-04f, + 4.58306144e-04f, 3.50831771e-04f, 2.40873321e-04f, 1.29410208e-04f, + 1.74046420e-05f, -9.41372015e-05f, -2.04212769e-04f, -3.11884379e-04f, + -4.16198692e-04f, -5.16283854e-04f, -6.11247752e-04f, -7.00285651e-04f, + -7.82656708e-04f, -8.57682384e-04f, -9.24710993e-04f, -9.83216422e-04f, + -1.03273072e-03f, -1.07286281e-03f, -1.10331228e-03f, -1.12385081e-03f, + -1.13439398e-03f, -1.13485267e-03f, -1.12532108e-03f, -1.10590886e-03f, + -1.07686330e-03f, -1.03849711e-03f, -9.91189076e-04f, -9.35388727e-04f, + -8.71690018e-04f, -8.00651347e-04f, -7.22956626e-04f, -6.39322842e-04f, + -5.50528037e-04f, -4.57398663e-04f, -3.60742819e-04f, -2.61456827e-04f, + -1.60433252e-04f, -5.85672438e-05f, 4.32388885e-05f, 1.44099976e-04f, + 2.43118717e-04f, 3.39409985e-04f, 4.32194068e-04f, 5.20625265e-04f, + 6.03978397e-04f, 6.81539342e-04f, 7.52654738e-04f, 8.16724875e-04f, + 8.73252328e-04f, 9.21758980e-04f, 9.61871797e-04f, 9.93285392e-04f, + 1.01577077e-03f, 1.02918084e-03f, 1.03343231e-03f, 1.02859389e-03f, + 1.01469668e-03f, 9.91956413e-04f, 9.60609395e-04f, 9.20977714e-04f, + 8.73477570e-04f, 8.18534765e-04f, 7.56717188e-04f, 6.88582017e-04f, + 6.14781378e-04f, 5.35992969e-04f, 4.52932280e-04f, 3.66367367e-04f, + 2.77067953e-04f, 1.85846068e-04f, 9.35202666e-05f, 8.95800378e-07f, + -9.12013687e-05f, -1.81968879e-04f, -2.70612753e-04f, -3.56346876e-04f, + -4.38458579e-04f, -5.16248792e-04f, -5.89021803e-04f, -6.56215711e-04f, + -7.17226849e-04f, -7.71587683e-04f, -8.18834525e-04f, -8.58598909e-04f, + -8.90593018e-04f, -9.14550231e-04f, -9.30330737e-04f, -9.37818621e-04f, + -9.37042342e-04f, -9.28000423e-04f, -9.10856704e-04f, -8.85795548e-04f, + -8.53094084e-04f, -8.13072428e-04f, -7.66143829e-04f, -7.12744419e-04f, + -6.53375169e-04f, -5.88633731e-04f, -5.19070071e-04f, -4.45359640e-04f, + -3.68152539e-04f, -2.88173584e-04f, -2.06121828e-04f, -1.22733383e-04f, + -3.87658539e-05f, 4.50501871e-05f, 1.27995593e-04f, 2.09307286e-04f, + 2.88316517e-04f, 3.64302877e-04f, 4.36661583e-04f, 5.04755088e-04f, + 5.68007944e-04f, 6.25887844e-04f, 6.77933133e-04f, 7.23703420e-04f, + 7.62852083e-04f, 7.95064070e-04f, 8.20080681e-04f, 8.37764175e-04f, + 8.47969128e-04f, 8.50664693e-04f, 8.45873351e-04f, 8.33674258e-04f, + 8.14207332e-04f, 7.87719338e-04f, 7.54456412e-04f, 7.14767940e-04f, + 6.69041313e-04f, 6.17697028e-04f, 5.61235426e-04f, 5.00173623e-04f, + 4.35079832e-04f, 3.66567870e-04f, 2.95227094e-04f, 2.21715344e-04f, + 1.46718993e-04f, 7.08530739e-05f, -5.15577364e-06f, -8.07004736e-05f, + -1.55066524e-04f, -2.27613536e-04f, -2.97740553e-04f, -3.64841340e-04f, + -4.28317869e-04f, -4.87670380e-04f, -5.42375883e-04f, -5.92004229e-04f, + -6.36123948e-04f, -6.74394124e-04f, -7.06529242e-04f, -7.32237235e-04f, + -7.51412988e-04f, -7.63820757e-04f, -7.69492035e-04f, -7.68344591e-04f, + -7.60492235e-04f, -7.45985791e-04f, -7.25024598e-04f, -6.97830116e-04f, + -6.64676731e-04f, -6.25892548e-04f, -5.81842478e-04f, -5.32951282e-04f, + -4.79696107e-04f, -4.22525795e-04f, -3.62014271e-04f, -2.98672472e-04f, + -2.33120510e-04f, -1.65900121e-04f, -9.76362200e-05f, -2.89275243e-05f, + 3.96176674e-05f, 1.07395474e-04f, 1.73835271e-04f, 2.38325404e-04f, + 3.00347969e-04f, 3.59354345e-04f, 4.14862825e-04f, 4.66386521e-04f, + 5.13515409e-04f, 5.55829029e-04f, 5.93042831e-04f, 6.24819812e-04f, + 6.50905442e-04f, 6.71146467e-04f, 6.85375692e-04f, 6.93516598e-04f, + 6.95509528e-04f, 6.91405413e-04f, 6.81262048e-04f, 6.65220278e-04f, + 6.43420145e-04f, 6.16125905e-04f, 5.83606917e-04f, 5.46156953e-04f, + 5.04157409e-04f, 4.58002510e-04f, 4.08099631e-04f, 3.54942755e-04f, + 2.98984828e-04f, 2.40759234e-04f, 1.80768051e-04f, 1.19575974e-04f, + 5.76957438e-05f, -4.28880367e-06f, -6.58721960e-05f, -1.26493174e-04f, + -1.85639859e-04f, -2.42807383e-04f, -2.97481187e-04f, -3.49221122e-04f, + -3.97578847e-04f, -4.42159263e-04f, -4.82605142e-04f, -5.18557036e-04f, + -5.49760019e-04f, -5.75954068e-04f, -5.96944106e-04f, -6.12574988e-04f, + -6.22745551e-04f, -6.27396115e-04f, -6.26534322e-04f, -6.20176804e-04f, + -6.08460174e-04f, -5.91464522e-04f, -5.69410030e-04f, -5.42484457e-04f, + -5.10992484e-04f, -4.75225134e-04f, -4.35505918e-04f, -3.92225972e-04f, + -3.45771246e-04f, -2.96593648e-04f, -2.45116844e-04f, -1.91808621e-04f, + -1.37165970e-04f, -8.16356339e-05f, -2.57445926e-05f, 3.00322984e-05f, + 8.51783200e-05f, 1.39260137e-04f, 1.91796337e-04f, 2.42317102e-04f, + 2.90405255e-04f, 3.35659286e-04f, 3.77691701e-04f, 4.16174018e-04f, + 4.50785161e-04f, 4.81219250e-04f, 5.07259738e-04f, 5.28714262e-04f, + 5.45399813e-04f, 5.57228370e-04f, 5.64092051e-04f, 5.66000580e-04f, + 5.62944333e-04f, 5.54966288e-04f, 5.42209838e-04f, 5.24790563e-04f, + 5.02873830e-04f, 4.76708610e-04f, 4.46521001e-04f, 4.12650276e-04f, + 3.75358170e-04f, 3.35013907e-04f, 2.91997251e-04f, 2.46695035e-04f, + 1.99507857e-04f, 1.50889553e-04f, 1.01231956e-04f, 5.10047602e-05f, + 6.33587302e-07f, -4.94259002e-05f, -9.87418814e-05f, -1.46889490e-04f, + -1.93470217e-04f, -2.38065458e-04f, -2.80305060e-04f, -3.19836003e-04f, + -3.56327452e-04f, -3.89502317e-04f, -4.19041782e-04f, -4.44759447e-04f, + -4.66423158e-04f, -4.83886447e-04f, -4.97011387e-04f, -5.05730787e-04f, + -5.09963720e-04f, -5.09717728e-04f, -5.05043322e-04f, -4.95963142e-04f, + -4.82639446e-04f, -4.65152875e-04f, -4.43733866e-04f, -4.18567153e-04f, + -3.89890304e-04f, -3.57996097e-04f, -3.23170997e-04f, -2.85736681e-04f, + -2.46055002e-04f, -2.04445682e-04f, -1.61320963e-04f, -1.17042529e-04f, + -7.20188676e-05f, -2.66396241e-05f, 1.86912290e-05f, 6.36017948e-05f, + 1.07665176e-04f, 1.50514890e-04f, 1.91812013e-04f, 2.31174729e-04f, + 2.68275281e-04f, 3.02820173e-04f, 3.34517215e-04f, 3.63107193e-04f, + 3.88350390e-04f, 4.10051248e-04f, 4.28044624e-04f, 4.42214263e-04f, + 4.52417167e-04f, 4.58637799e-04f, 4.60811960e-04f, 4.58959019e-04f, + 4.53110813e-04f, 4.43353615e-04f, 4.29788076e-04f, 4.12573604e-04f, + 3.91837458e-04f, 3.67827680e-04f, 3.40742776e-04f, 3.10878990e-04f, + 2.78464465e-04f, 2.43800376e-04f, 2.07253019e-04f, 1.69099894e-04f, + 1.29702525e-04f, 8.93909104e-05f, 4.85743353e-05f, 7.55415142e-06f, + -3.32831786e-05f, -7.36072164e-05f, -1.13043176e-04f, -1.51234384e-04f, + -1.87933888e-04f, -2.22751408e-04f, -2.55424048e-04f, -2.85707115e-04f, + -3.13301151e-04f, -3.38018882e-04f, -3.59650405e-04f, -3.78009094e-04f, + -3.93005971e-04f, -4.04467563e-04f, -4.12352296e-04f, -4.16625213e-04f, + -4.17213487e-04f, -4.14188932e-04f, -4.07570766e-04f, -3.97428285e-04f, + -3.83928030e-04f, -3.67135876e-04f, -3.47267227e-04f, -3.24463119e-04f, + -2.98994961e-04f, -2.71043298e-04f, -2.40926487e-04f, -2.08843709e-04f, + -1.75152575e-04f, -1.40107049e-04f, -1.04080472e-04f, -6.73135829e-05f, + -3.01948541e-05f, 7.00871665e-06f, 4.39240599e-05f, 8.02943349e-05f, + 1.15726815e-04f, 1.49966848e-04f, 1.82710879e-04f, 2.13703334e-04f, + 2.42653106e-04f, 2.69351388e-04f, 2.93549873e-04f, 3.15102924e-04f, + 3.33768751e-04f, 3.49447907e-04f, 3.62019988e-04f, 3.71363176e-04f, + 3.77445082e-04f, 3.80209433e-04f, 3.79656986e-04f, 3.75829456e-04f, + 3.68740564e-04f, 3.58494796e-04f, 3.45203622e-04f, 3.28976671e-04f, + 3.09977680e-04f, 2.88380075e-04f, 2.64390378e-04f, 2.38244599e-04f, + 2.10143730e-04f, 1.80382945e-04f, 1.49191334e-04f, 1.16874433e-04f, + 8.36939641e-05f, 4.99545687e-05f, 1.59419682e-05f, -1.80487956e-05f, + -5.17116932e-05f, -8.47731419e-05f, -1.16937183e-04f, -1.47911960e-04f, + -1.77490349e-04f, -2.05400357e-04f, -2.31387402e-04f, -2.55252058e-04f, + -2.76804796e-04f, -2.95856747e-04f, -3.12270197e-04f, -3.25902725e-04f, + -3.36672987e-04f, -3.44466047e-04f, -3.49280604e-04f, -3.51026811e-04f, + -3.49738846e-04f, -3.45444097e-04f, -3.38175670e-04f, -3.28025304e-04f, + -3.15107780e-04f, -2.99483932e-04f, -2.81389775e-04f, -2.60907140e-04f, + -2.38309091e-04f, -2.13697965e-04f, -1.87369701e-04f, -1.59547138e-04f, + -1.30435290e-04f, -1.00336342e-04f, -6.95075165e-05f, -3.81891347e-05f, + -6.66316342e-06f, 2.48005967e-05f, 5.59093608e-05f, 8.64381970e-05f, + 1.16084478e-04f, 1.44629782e-04f, 1.71817136e-04f, 1.97430379e-04f, + 2.21249248e-04f, 2.43067483e-04f, 2.62714805e-04f, 2.80037512e-04f, + 2.94912411e-04f, 3.07154450e-04f, 3.16770812e-04f, 3.23565757e-04f, + 3.27598190e-04f, 3.28781884e-04f, 3.27137017e-04f, 3.22671504e-04f, + 3.15478482e-04f, 3.05561179e-04f, 2.93071243e-04f, 2.78072214e-04f, + 2.60752421e-04f, 2.41204026e-04f, 2.19657587e-04f, 1.96274763e-04f, + 1.71259618e-04f, 1.44829587e-04f, 1.17207736e-04f, 8.86396900e-05f, + 5.93848595e-05f, 2.96756971e-05f, -2.55974213e-07f, -3.01364040e-05f, + -5.97095986e-05f, -8.87016361e-05f, -1.16938981e-04f, -1.44121097e-04f, + -1.70042060e-04f, -1.94504049e-04f, -2.17285275e-04f, -2.38190653e-04f, + -2.57074514e-04f, -2.73769854e-04f, -2.88131929e-04f, -3.00067778e-04f, + -3.09445066e-04f, -3.16238251e-04f, -3.20350572e-04f, -3.21779258e-04f, + -3.20521292e-04f, -3.16575228e-04f, -3.09991670e-04f, -3.00802605e-04f, + -2.89147820e-04f, -2.75068336e-04f, -2.58722607e-04f, -2.40234254e-04f, + -2.19762234e-04f, -1.97484144e-04f, -1.73586378e-04f, -1.48283187e-04f, + -1.21767035e-04f, -9.42546438e-05f, -6.59849449e-05f, -3.72382910e-05f, + -8.18708029e-06f, 2.09018928e-05f, 4.97856427e-05f, 7.82043260e-05f, + 1.05945996e-04f, 1.32796055e-04f, 1.58498048e-04f, 1.82852505e-04f, + 2.05652703e-04f, 2.26725402e-04f, 2.45875363e-04f, 2.62957031e-04f, + 2.77828957e-04f, 2.90361412e-04f, 3.00450626e-04f, 3.07979930e-04f, + 3.12961709e-04f, 3.15260735e-04f, 3.14925301e-04f, 3.11915939e-04f, + 3.06269493e-04f, 2.97998031e-04f, 2.87169055e-04f, 2.73915388e-04f, + 2.58278031e-04f, 2.40399328e-04f, 2.20409753e-04f, 1.98478187e-04f, + 1.74739784e-04f, 1.49434544e-04f, 1.22685308e-04f, 9.47715675e-05f, + 6.58537103e-05f, 3.61749072e-05f, 5.97746474e-06f, -2.45162183e-05f, + -5.50707960e-05f, -8.54546076e-05f, -1.15437977e-04f, -1.44752181e-04f, + -1.73217230e-04f, -2.00586450e-04f, -2.26666528e-04f, -2.51224767e-04f, + -2.74099533e-04f, -2.95109231e-04f, -3.14079824e-04f, -3.30869553e-04f, + -3.45369337e-04f, -3.57422987e-04f, -3.66953155e-04f, -3.73911775e-04f, + -3.78220230e-04f, -3.79850587e-04f, -3.78754989e-04f, -3.74987358e-04f, + -3.68568872e-04f, -3.59511330e-04f, -3.47888205e-04f, -3.33821666e-04f, + -3.17386918e-04f, -2.98721551e-04f, -2.77916847e-04f, -2.55178573e-04f, + -2.30688519e-04f, -2.04596855e-04f, -1.77101686e-04f, -1.48416144e-04f, + -1.18791903e-04f, -8.83978597e-05f, -5.74821966e-05f, -2.62868833e-05f, + 4.92815189e-06f, 3.59504591e-05f, 6.65434569e-05f, 9.64155022e-05f, + 1.25420450e-04f, 1.53244448e-04f, 1.79726500e-04f, 2.04644719e-04f, + 2.27732573e-04f, 2.48876343e-04f, 2.67856021e-04f, 2.84480504e-04f, + 2.98654254e-04f, 3.10195638e-04f, 3.18997467e-04f, 3.24986682e-04f, + 3.28010014e-04f, 3.28075276e-04f, 3.25081644e-04f, 3.18989006e-04f, + 3.09862065e-04f, 2.97618732e-04f, 2.82358452e-04f, 2.64078432e-04f, + 2.42867446e-04f, 2.18818442e-04f, 1.92028457e-04f, 1.62575887e-04f, + 1.30632731e-04f, 9.63241083e-05f, 5.98270577e-05f, 2.13018198e-05f, + -1.90650636e-05f, -6.10476572e-05f, -1.04504009e-04f, -1.49141212e-04f, + -1.94818380e-04f, -2.41269356e-04f, -2.88284124e-04f, -3.35636339e-04f, + -3.83078353e-04f, -4.30375313e-04f, -4.77325331e-04f, -5.23684425e-04f, + -5.69213416e-04f, -6.13776473e-04f, -6.57085679e-04f, -6.98973293e-04f, + -7.39240301e-04f, -7.77721569e-04f, -8.14237506e-04f, -8.48630685e-04f, + -8.80760950e-04f, -9.10481345e-04f, -9.37672148e-04f, -9.62299433e-04f, + -9.84157343e-04f, -1.00327325e-03f, -1.01946138e-03f, -1.03289356e-03f, + -1.04338135e-03f, -1.05093085e-03f, -1.05547810e-03f, -1.05718776e-03f, + -1.05596199e-03f, -1.05199493e-03f, -1.04512596e-03f, -1.03564457e-03f, + -1.02347745e-03f, -1.00881749e-03f, -9.91651851e-04f, -9.72214477e-04f, + -9.50589764e-04f, -9.26823063e-04f, -9.01169667e-04f, -8.73693053e-04f, + -8.44651495e-04f, -8.14015623e-04f, -7.82096657e-04f, -7.48978970e-04f, + -7.14918806e-04f, -6.79915120e-04f, -6.44197160e-04f, -6.07973052e-04f, + -5.71340571e-04f, -5.34494985e-04f, -4.97625899e-04f, -4.60763150e-04f, + -4.24154408e-04f, -3.87781959e-04f, -3.51977273e-04f, -3.16708720e-04f, + -2.82209173e-04f, -2.48453876e-04f, -2.15649909e-04f, -1.83919068e-04f, + -1.53211006e-04f, -1.23682813e-04f, -9.53777996e-05f, -6.83598308e-05f, + -4.27405102e-05f, -1.84644678e-05f, 4.40560628e-06f, 2.57526124e-05f, + 4.57070844e-05f, 6.42470547e-05f, 8.12438667e-05f, 9.67784543e-05f, + 1.10883532e-04f, 1.23557473e-04f, 1.34866254e-04f, 1.44751012e-04f, + 1.53323607e-04f, 1.60602540e-04f, 1.66572422e-04f, 1.71555319e-04f, + 1.75228695e-04f, 1.77785942e-04f, 1.79287868e-04f, 1.80066421e-04f, + 1.79856554e-04f, 1.78600745e-04f, 1.76640402e-04f, 1.74149693e-04f, + 1.70998956e-04f, 1.67101361e-04f, 1.62736605e-04f, 1.57955365e-04f, + 1.52783832e-04f, 1.47235562e-04f, 1.41440247e-04f, 1.35411748e-04f, + 1.29174003e-04f, 1.22883471e-04f, 1.16507352e-04f, 1.10062224e-04f, + 1.03554135e-04f, 9.72109292e-05f, 9.09057438e-05f, 8.47069282e-05f, + 7.86275439e-05f, 7.27295443e-05f, 6.70460602e-05f, 6.14870567e-05f, + 5.62358268e-05f, 5.11680423e-05f, 4.63971100e-05f, 4.18604282e-05f, + 3.75581730e-05f, 3.34956971e-05f, 2.95613583e-05f, 2.62367667e-05f, + 2.30173784e-05f, 1.99216834e-05f, 1.73717570e-05f, 1.47187235e-05f, + 1.23944987e-05f, 1.06041654e-05f, 8.75464375e-06f, 1.94324625e-05f, }; // Minimum-phase equiripple FIR lowpass diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 65145d4c88..048b8b1633 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -221,8 +221,12 @@ void Avatar::updateAvatarEntities() { return; } - if (getID() == QUuid() || getID() == AVATAR_SELF_ID) { - return; // wait until MyAvatar gets an ID before doing this. + if (getID().isNull() || + getID() == AVATAR_SELF_ID || + DependencyManager::get()->getSessionUUID() == QUuid()) { + // wait until MyAvatar and this Node gets an ID before doing this. Otherwise, various things go wrong -- + // things get their parent fixed up from AVATAR_SELF_ID to a null uuid which means "no parent". + return; } auto treeRenderer = DependencyManager::get(); @@ -1806,4 +1810,4 @@ scriptable::ScriptableModelBase Avatar::getScriptableModel() { result.appendMaterials(_materials); } return result; -} \ No newline at end of file +} diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index a765e66bbc..0407c8508c 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -33,65 +33,8 @@ #include "FBXBaker.h" -#ifdef _WIN32 -#pragma warning( push ) -#pragma warning( disable : 4267 ) -#endif - -#include -#include - -#ifdef _WIN32 -#pragma warning( pop ) -#endif - - -FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, - const QString& bakedOutputDir, const QString& originalOutputDir) : - _fbxURL(fbxURL), - _bakedOutputDir(bakedOutputDir), - _originalOutputDir(originalOutputDir), - _textureThreadGetter(textureThreadGetter) -{ - -} - -FBXBaker::~FBXBaker() { - if (_tempDir.exists()) { - if (!_tempDir.remove(_originalFBXFilePath)) { - qCWarning(model_baking) << "Failed to remove temporary copy of fbx file:" << _originalFBXFilePath; - } - if (!_tempDir.rmdir(".")) { - qCWarning(model_baking) << "Failed to remove temporary directory:" << _tempDir; - } - } -} - -void FBXBaker::abort() { - Baker::abort(); - - // tell our underlying TextureBaker instances to abort - // the FBXBaker will wait until all are aborted before emitting its own abort signal - for (auto& textureBaker : _bakingTextures) { - textureBaker->abort(); - } -} - -void FBXBaker::bake() { - qDebug() << "FBXBaker" << _fbxURL << "bake starting"; - - auto tempDir = PathUtils::generateTemporaryDir(); - - if (tempDir.isEmpty()) { - handleError("Failed to create a temporary directory."); - return; - } - - _tempDir = tempDir; - - _originalFBXFilePath = _tempDir.filePath(_fbxURL.fileName()); - qDebug() << "Made temporary dir " << _tempDir; - qDebug() << "Origin file path: " << _originalFBXFilePath; +void FBXBaker::bake() { + qDebug() << "FBXBaker" << _modelURL << "bake starting"; // setup the output folder for the results of this bake setupOutputFolder(); @@ -152,7 +95,7 @@ void FBXBaker::setupOutputFolder() { } // attempt to make the output folder if (!QDir().mkpath(_originalOutputDir)) { - handleError("Failed to create FBX output folder " + _bakedOutputDir); + handleError("Failed to create FBX output folder " + _originalOutputDir); return; } } @@ -160,25 +103,25 @@ void FBXBaker::setupOutputFolder() { void FBXBaker::loadSourceFBX() { // check if the FBX is local or first needs to be downloaded - if (_fbxURL.isLocalFile()) { + if (_modelURL.isLocalFile()) { // load up the local file - QFile localFBX { _fbxURL.toLocalFile() }; + QFile localFBX { _modelURL.toLocalFile() }; - qDebug() << "Local file url: " << _fbxURL << _fbxURL.toString() << _fbxURL.toLocalFile() << ", copying to: " << _originalFBXFilePath; + qDebug() << "Local file url: " << _modelURL << _modelURL.toString() << _modelURL.toLocalFile() << ", copying to: " << _originalModelFilePath; if (!localFBX.exists()) { //QMessageBox::warning(this, "Could not find " + _fbxURL.toString(), ""); - handleError("Could not find " + _fbxURL.toString()); + handleError("Could not find " + _modelURL.toString()); return; } // make a copy in the output folder if (!_originalOutputDir.isEmpty()) { - qDebug() << "Copying to: " << _originalOutputDir << "/" << _fbxURL.fileName(); - localFBX.copy(_originalOutputDir + "/" + _fbxURL.fileName()); + qDebug() << "Copying to: " << _originalOutputDir << "/" << _modelURL.fileName(); + localFBX.copy(_originalOutputDir + "/" + _modelURL.fileName()); } - localFBX.copy(_originalFBXFilePath); + localFBX.copy(_originalModelFilePath); // emit our signal to start the import of the FBX source copy emit sourceCopyReadyToLoad(); @@ -193,9 +136,9 @@ void FBXBaker::loadSourceFBX() { networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - networkRequest.setUrl(_fbxURL); + networkRequest.setUrl(_modelURL); - qCDebug(model_baking) << "Downloading" << _fbxURL; + qCDebug(model_baking) << "Downloading" << _modelURL; auto networkReply = networkAccessManager.get(networkRequest); connect(networkReply, &QNetworkReply::finished, this, &FBXBaker::handleFBXNetworkReply); @@ -206,20 +149,20 @@ void FBXBaker::handleFBXNetworkReply() { auto requestReply = qobject_cast(sender()); if (requestReply->error() == QNetworkReply::NoError) { - qCDebug(model_baking) << "Downloaded" << _fbxURL; + qCDebug(model_baking) << "Downloaded" << _modelURL; // grab the contents of the reply and make a copy in the output folder - QFile copyOfOriginal(_originalFBXFilePath); + QFile copyOfOriginal(_originalModelFilePath); - qDebug(model_baking) << "Writing copy of original FBX to" << _originalFBXFilePath << copyOfOriginal.fileName(); + qDebug(model_baking) << "Writing copy of original FBX to" << _originalModelFilePath << copyOfOriginal.fileName(); if (!copyOfOriginal.open(QIODevice::WriteOnly)) { // add an error to the error list for this FBX stating that a duplicate of the original FBX could not be made - handleError("Could not create copy of " + _fbxURL.toString() + " (Failed to open " + _originalFBXFilePath + ")"); + handleError("Could not create copy of " + _modelURL.toString() + " (Failed to open " + _originalModelFilePath + ")"); return; } if (copyOfOriginal.write(requestReply->readAll()) == -1) { - handleError("Could not create copy of " + _fbxURL.toString() + " (Failed to write)"); + handleError("Could not create copy of " + _modelURL.toString() + " (Failed to write)"); return; } @@ -227,98 +170,32 @@ void FBXBaker::handleFBXNetworkReply() { copyOfOriginal.close(); if (!_originalOutputDir.isEmpty()) { - copyOfOriginal.copy(_originalOutputDir + "/" + _fbxURL.fileName()); + copyOfOriginal.copy(_originalOutputDir + "/" + _modelURL.fileName()); } // emit our signal to start the import of the FBX source copy emit sourceCopyReadyToLoad(); } else { // add an error to our list stating that the FBX could not be downloaded - handleError("Failed to download " + _fbxURL.toString()); + handleError("Failed to download " + _modelURL.toString()); } } void FBXBaker::importScene() { - qDebug() << "file path: " << _originalFBXFilePath.toLocal8Bit().data() << QDir(_originalFBXFilePath).exists(); + qDebug() << "file path: " << _originalModelFilePath.toLocal8Bit().data() << QDir(_originalModelFilePath).exists(); - QFile fbxFile(_originalFBXFilePath); + QFile fbxFile(_originalModelFilePath); if (!fbxFile.open(QIODevice::ReadOnly)) { - handleError("Error opening " + _originalFBXFilePath + " for reading"); + handleError("Error opening " + _originalModelFilePath + " for reading"); return; } FBXReader reader; - qCDebug(model_baking) << "Parsing" << _fbxURL; + qCDebug(model_baking) << "Parsing" << _modelURL; _rootNode = reader._rootNode = reader.parseFBX(&fbxFile); - _geometry = reader.extractFBXGeometry({}, _fbxURL.toString()); - _textureContent = reader._textureContent; -} - -QString texturePathRelativeToFBX(QUrl fbxURL, QUrl textureURL) { - auto fbxPath = fbxURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment); - auto texturePath = textureURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment); - - if (texturePath.startsWith(fbxPath)) { - // texture path is a child of the FBX path, return the texture path without the fbx path - return texturePath.mid(fbxPath.length()); - } else { - // the texture path was not a child of the FBX path, return the empty string - return ""; - } -} - -QString FBXBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) { - // first make sure we have a unique base name for this texture - // in case another texture referenced by this model has the same base name - auto& nameMatches = _textureNameMatchCount[textureFileInfo.baseName()]; - - QString bakedTextureFileName { textureFileInfo.completeBaseName() }; - - if (nameMatches > 0) { - // there are already nameMatches texture with this name - // append - and that number to our baked texture file name so that it is unique - bakedTextureFileName += "-" + QString::number(nameMatches); - } - - bakedTextureFileName += BAKED_TEXTURE_EXT; - - // increment the number of name matches - ++nameMatches; - - return bakedTextureFileName; -} - -QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded) { - - QUrl urlToTexture; - - auto apparentRelativePath = QFileInfo(relativeFileName.replace("\\", "/")); - - if (isEmbedded) { - urlToTexture = _fbxURL.toString() + "/" + apparentRelativePath.filePath(); - } else { - if (textureFileInfo.exists() && textureFileInfo.isFile()) { - // set the texture URL to the local texture that we have confirmed exists - urlToTexture = QUrl::fromLocalFile(textureFileInfo.absoluteFilePath()); - } else { - // external texture that we'll need to download or find - - // this is a relative file path which will require different handling - // depending on the location of the original FBX - if (_fbxURL.isLocalFile() && apparentRelativePath.exists() && apparentRelativePath.isFile()) { - // the absolute path we ran into for the texture in the FBX exists on this machine - // so use that file - urlToTexture = QUrl::fromLocalFile(apparentRelativePath.absoluteFilePath()); - } else { - // we didn't find the texture on this machine at the absolute path - // so assume that it is right beside the FBX to match the behaviour of interface - urlToTexture = _fbxURL.resolved(apparentRelativePath.fileName()); - } - } - } - - return urlToTexture; + _geometry = reader.extractFBXGeometry({}, _modelURL.toString()); + _textureContentMap = reader._textureContent; } void FBXBaker::rewriteAndBakeSceneModels() { @@ -344,176 +221,25 @@ void FBXBaker::rewriteAndBakeSceneModels() { // TODO Pull this out of _geometry instead so we don't have to reprocess it auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex, false); - auto& mesh = extractedMesh.mesh; - - if (mesh.wasCompressed) { - handleError("Cannot re-bake a file that contains compressed mesh"); - return; - } - - Q_ASSERT(mesh.normals.size() == 0 || mesh.normals.size() == mesh.vertices.size()); - Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); - Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size()); - - int64_t numTriangles { 0 }; - for (auto& part : mesh.parts) { - if ((part.quadTrianglesIndices.size() % 3) != 0 || (part.triangleIndices.size() % 3) != 0) { - handleWarning("Found a mesh part with invalid index data, skipping"); + + // Callback to get MaterialID + GetMaterialIDCallback materialIDcallback = [&extractedMesh](int partIndex) { + return extractedMesh.partMaterialTextures[partIndex].first; + }; + + // Compress mesh information and store in dracoMeshNode + FBXNode dracoMeshNode; + bool success = compressMesh(extractedMesh.mesh, hasDeformers, dracoMeshNode, materialIDcallback); + + // if bake fails - return, if there were errors and continue, if there were warnings. + if (!success) { + if (hasErrors()) { + return; + } else if (hasWarnings()) { continue; } - numTriangles += part.quadTrianglesIndices.size() / 3; - numTriangles += part.triangleIndices.size() / 3; } - - if (numTriangles == 0) { - continue; - } - - draco::TriangleSoupMeshBuilder meshBuilder; - - meshBuilder.Start(numTriangles); - - bool hasNormals { mesh.normals.size() > 0 }; - bool hasColors { mesh.colors.size() > 0 }; - bool hasTexCoords { mesh.texCoords.size() > 0 }; - bool hasTexCoords1 { mesh.texCoords1.size() > 0 }; - bool hasPerFaceMaterials { mesh.parts.size() > 1 - || extractedMesh.partMaterialTextures[0].first != 0 }; - bool needsOriginalIndices { hasDeformers }; - - int normalsAttributeID { -1 }; - int colorsAttributeID { -1 }; - int texCoordsAttributeID { -1 }; - int texCoords1AttributeID { -1 }; - int faceMaterialAttributeID { -1 }; - int originalIndexAttributeID { -1 }; - - const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION, - 3, draco::DT_FLOAT32); - if (needsOriginalIndices) { - originalIndexAttributeID = meshBuilder.AddAttribute( - (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_ORIGINAL_INDEX, - 1, draco::DT_INT32); - } - - if (hasNormals) { - normalsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::NORMAL, - 3, draco::DT_FLOAT32); - } - if (hasColors) { - colorsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::COLOR, - 3, draco::DT_FLOAT32); - } - if (hasTexCoords) { - texCoordsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::TEX_COORD, - 2, draco::DT_FLOAT32); - } - if (hasTexCoords1) { - texCoords1AttributeID = meshBuilder.AddAttribute( - (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_TEX_COORD_1, - 2, draco::DT_FLOAT32); - } - if (hasPerFaceMaterials) { - faceMaterialAttributeID = meshBuilder.AddAttribute( - (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, - 1, draco::DT_UINT16); - } - - - auto partIndex = 0; - draco::FaceIndex face; - for (auto& part : mesh.parts) { - const auto& matTex = extractedMesh.partMaterialTextures[partIndex]; - uint16_t materialID = matTex.first; - - auto addFace = [&](QVector& indices, int index, draco::FaceIndex face) { - int32_t idx0 = indices[index]; - int32_t idx1 = indices[index + 1]; - int32_t idx2 = indices[index + 2]; - - if (hasPerFaceMaterials) { - meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID); - } - - meshBuilder.SetAttributeValuesForFace(positionAttributeID, face, - &mesh.vertices[idx0], &mesh.vertices[idx1], - &mesh.vertices[idx2]); - - if (needsOriginalIndices) { - meshBuilder.SetAttributeValuesForFace(originalIndexAttributeID, face, - &mesh.originalIndices[idx0], - &mesh.originalIndices[idx1], - &mesh.originalIndices[idx2]); - } - if (hasNormals) { - meshBuilder.SetAttributeValuesForFace(normalsAttributeID, face, - &mesh.normals[idx0], &mesh.normals[idx1], - &mesh.normals[idx2]); - } - if (hasColors) { - meshBuilder.SetAttributeValuesForFace(colorsAttributeID, face, - &mesh.colors[idx0], &mesh.colors[idx1], - &mesh.colors[idx2]); - } - if (hasTexCoords) { - meshBuilder.SetAttributeValuesForFace(texCoordsAttributeID, face, - &mesh.texCoords[idx0], &mesh.texCoords[idx1], - &mesh.texCoords[idx2]); - } - if (hasTexCoords1) { - meshBuilder.SetAttributeValuesForFace(texCoords1AttributeID, face, - &mesh.texCoords1[idx0], &mesh.texCoords1[idx1], - &mesh.texCoords1[idx2]); - } - }; - - for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { - addFace(part.quadTrianglesIndices, i, face++); - } - - for (int i = 0; (i + 2) < part.triangleIndices.size(); i += 3) { - addFace(part.triangleIndices, i, face++); - } - - partIndex++; - } - - auto dracoMesh = meshBuilder.Finalize(); - - if (!dracoMesh) { - handleWarning("Failed to finalize the baking of a draco Geometry node"); - continue; - } - - // we need to modify unique attribute IDs for custom attributes - // so the attributes are easily retrievable on the other side - if (hasPerFaceMaterials) { - dracoMesh->attribute(faceMaterialAttributeID)->set_unique_id(DRACO_ATTRIBUTE_MATERIAL_ID); - } - - if (hasTexCoords1) { - dracoMesh->attribute(texCoords1AttributeID)->set_unique_id(DRACO_ATTRIBUTE_TEX_COORD_1); - } - - if (needsOriginalIndices) { - dracoMesh->attribute(originalIndexAttributeID)->set_unique_id(DRACO_ATTRIBUTE_ORIGINAL_INDEX); - } - - draco::Encoder encoder; - - encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 14); - encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, 12); - encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 10); - encoder.SetSpeedOptions(0, 5); - - draco::EncoderBuffer buffer; - encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); - - FBXNode dracoMeshNode; - dracoMeshNode.name = "DracoMesh"; - auto value = QVariant::fromValue(QByteArray(buffer.data(), (int) buffer.size())); - dracoMeshNode.properties.append(value); - + objectChild.children.push_back(dracoMeshNode); static const std::vector nodeNamesToDelete { @@ -590,69 +316,25 @@ void FBXBaker::rewriteAndBakeSceneTextures() { for (FBXNode& textureChild : object->children) { if (textureChild.name == "RelativeFilename") { + QString fbxTextureFileName { textureChild.properties.at(0).toString() }; + + // grab the ID for this texture so we can figure out the + // texture type from the loaded materials + auto textureID { object->properties[0].toString() }; + auto textureType = textureTypes[textureID]; - // use QFileInfo to easily split up the existing texture filename into its components - QString fbxTextureFileName { textureChild.properties.at(0).toByteArray() }; - QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") }; + // Compress the texture information and return the new filename to be added into the FBX scene + auto bakedTextureFile = compressTexture(fbxTextureFileName, textureType); - if (hasErrors()) { - return; - } - - if (textureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) { - // re-baking an FBX that already references baked textures is a fail - // so we add an error and return from here - handleError("Cannot re-bake a file that references compressed textures"); - - return; - } - - if (!image::getSupportedFormats().contains(textureFileInfo.suffix())) { - // this is a texture format we don't bake, skip it - handleWarning(fbxTextureFileName + " is not a bakeable texture format"); - continue; - } - - // make sure this texture points to something and isn't one we've already re-mapped - if (!textureFileInfo.filePath().isEmpty()) { - // check if this was an embedded texture we have already have in-memory content for - auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit()); - - // figure out the URL to this texture, embedded or external - auto urlToTexture = getTextureURL(textureFileInfo, fbxTextureFileName, - !textureContent.isNull()); - - QString bakedTextureFileName; - if (_remappedTexturePaths.contains(urlToTexture)) { - bakedTextureFileName = _remappedTexturePaths[urlToTexture]; - } else { - // construct the new baked texture file name and file path - // ensuring that the baked texture will have a unique name - // even if there was another texture with the same name at a different path - bakedTextureFileName = createBakedTextureFileName(textureFileInfo); - _remappedTexturePaths[urlToTexture] = bakedTextureFileName; - } - - qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName - << "to" << bakedTextureFileName; - - QString bakedTextureFilePath { - _bakedOutputDir + "/" + bakedTextureFileName - }; - - // write the new filename into the FBX scene - textureChild.properties[0] = bakedTextureFileName.toLocal8Bit(); - - if (!_bakingTextures.contains(urlToTexture)) { - _outputFiles.push_back(bakedTextureFilePath); - - // grab the ID for this texture so we can figure out the - // texture type from the loaded materials - QString textureID { object->properties[0].toByteArray() }; - auto textureType = textureTypes[textureID]; - - // bake this texture asynchronously - bakeTexture(urlToTexture, textureType, _bakedOutputDir, bakedTextureFileName, textureContent); + // If no errors or warnings have occurred during texture compression add the filename to the FBX scene + if (!bakedTextureFile.isNull()) { + textureChild.properties[0] = bakedTextureFile; + } else { + // if bake fails - return, if there were errors and continue, if there were warnings. + if (hasErrors()) { + return; + } else if (hasWarnings()) { + continue; } } } @@ -671,172 +353,26 @@ void FBXBaker::rewriteAndBakeSceneTextures() { } } -void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, - const QDir& outputDir, const QString& bakedFilename, const QByteArray& textureContent) { - // start a bake for this texture and add it to our list to keep track of - QSharedPointer bakingTexture { - new TextureBaker(textureURL, textureType, outputDir, bakedFilename, textureContent), - &TextureBaker::deleteLater - }; - - // make sure we hear when the baking texture is done or aborted - connect(bakingTexture.data(), &Baker::finished, this, &FBXBaker::handleBakedTexture); - connect(bakingTexture.data(), &TextureBaker::aborted, this, &FBXBaker::handleAbortedTexture); - - // keep a shared pointer to the baking texture - _bakingTextures.insert(textureURL, bakingTexture); - - // start baking the texture on one of our available worker threads - bakingTexture->moveToThread(_textureThreadGetter()); - QMetaObject::invokeMethod(bakingTexture.data(), "bake"); -} - -void FBXBaker::handleBakedTexture() { - TextureBaker* bakedTexture = qobject_cast(sender()); - - // make sure we haven't already run into errors, and that this is a valid texture - if (bakedTexture) { - if (!shouldStop()) { - if (!bakedTexture->hasErrors()) { - if (!_originalOutputDir.isEmpty()) { - // we've been asked to make copies of the originals, so we need to make copies of this if it is a linked texture - - // use the path to the texture being baked to determine if this was an embedded or a linked texture - - // it is embeddded if the texure being baked was inside a folder with the name of the FBX - // since that is the fake URL we provide when baking external textures - - if (!_fbxURL.isParentOf(bakedTexture->getTextureURL())) { - // for linked textures we want to save a copy of original texture beside the original FBX - - qCDebug(model_baking) << "Saving original texture for" << bakedTexture->getTextureURL(); - - // check if we have a relative path to use for the texture - auto relativeTexturePath = texturePathRelativeToFBX(_fbxURL, bakedTexture->getTextureURL()); - - QFile originalTextureFile { - _originalOutputDir + "/" + relativeTexturePath + bakedTexture->getTextureURL().fileName() - }; - - if (relativeTexturePath.length() > 0) { - // make the folders needed by the relative path - } - - if (originalTextureFile.open(QIODevice::WriteOnly) && originalTextureFile.write(bakedTexture->getOriginalTexture()) != -1) { - qCDebug(model_baking) << "Saved original texture file" << originalTextureFile.fileName() - << "for" << _fbxURL; - } else { - handleError("Could not save original external texture " + originalTextureFile.fileName() - + " for " + _fbxURL.toString()); - return; - } - } - } - - - // now that this texture has been baked and handled, we can remove that TextureBaker from our hash - _bakingTextures.remove(bakedTexture->getTextureURL()); - - checkIfTexturesFinished(); - } else { - // there was an error baking this texture - add it to our list of errors - _errorList.append(bakedTexture->getErrors()); - - // we don't emit finished yet so that the other textures can finish baking first - _pendingErrorEmission = true; - - // now that this texture has been baked, even though it failed, we can remove that TextureBaker from our list - _bakingTextures.remove(bakedTexture->getTextureURL()); - - // abort any other ongoing texture bakes since we know we'll end up failing - for (auto& bakingTexture : _bakingTextures) { - bakingTexture->abort(); - } - - checkIfTexturesFinished(); - } - } else { - // we have errors to attend to, so we don't do extra processing for this texture - // but we do need to remove that TextureBaker from our list - // and then check if we're done with all textures - _bakingTextures.remove(bakedTexture->getTextureURL()); - - checkIfTexturesFinished(); - } - } -} - -void FBXBaker::handleAbortedTexture() { - // grab the texture bake that was aborted and remove it from our hash since we don't need to track it anymore - TextureBaker* bakedTexture = qobject_cast(sender()); - - if (bakedTexture) { - _bakingTextures.remove(bakedTexture->getTextureURL()); - } - - // since a texture we were baking aborted, our status is also aborted - _shouldAbort.store(true); - - // abort any other ongoing texture bakes since we know we'll end up failing - for (auto& bakingTexture : _bakingTextures) { - bakingTexture->abort(); - } - - checkIfTexturesFinished(); -} - void FBXBaker::exportScene() { // save the relative path to this FBX inside our passed output folder - auto fileName = _fbxURL.fileName(); + auto fileName = _modelURL.fileName(); auto baseName = fileName.left(fileName.lastIndexOf('.')); auto bakedFilename = baseName + BAKED_FBX_EXTENSION; - _bakedFBXFilePath = _bakedOutputDir + "/" + bakedFilename; + _bakedModelFilePath = _bakedOutputDir + "/" + bakedFilename; auto fbxData = FBXWriter::encodeFBX(_rootNode); - QFile bakedFile(_bakedFBXFilePath); + QFile bakedFile(_bakedModelFilePath); if (!bakedFile.open(QIODevice::WriteOnly)) { - handleError("Error opening " + _bakedFBXFilePath + " for writing"); + handleError("Error opening " + _bakedModelFilePath + " for writing"); return; } bakedFile.write(fbxData); - _outputFiles.push_back(_bakedFBXFilePath); + _outputFiles.push_back(_bakedModelFilePath); - qCDebug(model_baking) << "Exported" << _fbxURL << "with re-written paths to" << _bakedFBXFilePath; -} - -void FBXBaker::checkIfTexturesFinished() { - // check if we're done everything we need to do for this FBX - // and emit our finished signal if we're done - - if (_bakingTextures.isEmpty()) { - if (shouldStop()) { - // if we're checking for completion but we have errors - // that means one or more of our texture baking operations failed - - if (_pendingErrorEmission) { - setIsFinished(true); - } - - return; - } else { - qCDebug(model_baking) << "Finished baking, emitting finsihed" << _fbxURL; - - setIsFinished(true); - } - } -} - -void FBXBaker::setWasAborted(bool wasAborted) { - if (wasAborted != _wasAborted.load()) { - Baker::setWasAborted(wasAborted); - - if (wasAborted) { - qCDebug(model_baking) << "Aborted baking" << _fbxURL; - } - } + qCDebug(model_baking) << "Exported" << _modelURL << "with re-written paths to" << _bakedModelFilePath; } diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index a6034ee2b7..2888a60f73 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -19,7 +19,7 @@ #include "Baker.h" #include "TextureBaker.h" - +#include "ModelBaker.h" #include "ModelBakingLoggingCategory.h" #include @@ -30,21 +30,13 @@ static const QString BAKED_FBX_EXTENSION = ".baked.fbx"; using TextureBakerThreadGetter = std::function; -class FBXBaker : public Baker { +class FBXBaker : public ModelBaker { Q_OBJECT public: - FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, - const QString& bakedOutputDir, const QString& originalOutputDir = ""); - ~FBXBaker() override; - - QUrl getFBXUrl() const { return _fbxURL; } - QString getBakedFBXFilePath() const { return _bakedFBXFilePath; } - - virtual void setWasAborted(bool wasAborted) override; + using ModelBaker::ModelBaker; public slots: virtual void bake() override; - virtual void abort() override; signals: void sourceCopyReadyToLoad(); @@ -52,8 +44,6 @@ signals: private slots: void bakeSourceCopy(); void handleFBXNetworkReply(); - void handleBakedTexture(); - void handleAbortedTexture(); private: void setupOutputFolder(); @@ -64,38 +54,12 @@ private: void rewriteAndBakeSceneModels(); void rewriteAndBakeSceneTextures(); void exportScene(); - void removeEmbeddedMediaFolder(); - - void checkIfTexturesFinished(); - - QString createBakedTextureFileName(const QFileInfo& textureFileInfo); - QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded = false); - - void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir, - const QString& bakedFilename, const QByteArray& textureContent = QByteArray()); - - QUrl _fbxURL; FBXNode _rootNode; FBXGeometry* _geometry; - QHash _textureContent; - - QString _bakedFBXFilePath; - - QString _bakedOutputDir; - - // If set, the original FBX and textures will also be copied here - QString _originalOutputDir; - - QDir _tempDir; - QString _originalFBXFilePath; - - QMultiHash> _bakingTextures; QHash _textureNameMatchCount; QHash _remappedTexturePaths; - TextureBakerThreadGetter _textureThreadGetter; - bool _pendingErrorEmission { false }; }; diff --git a/libraries/baking/src/JSBaker.cpp b/libraries/baking/src/JSBaker.cpp index a97a7fe5b3..9932ad633e 100644 --- a/libraries/baking/src/JSBaker.cpp +++ b/libraries/baking/src/JSBaker.cpp @@ -190,7 +190,7 @@ bool JSBaker::handleMultiLineComments(QTextStream& in) { while (!in.atEnd()) { in >> character; if (character == '*') { - if (in.read(1) == '/') { + if (in.read(1) == "/") { return true; } } @@ -228,7 +228,7 @@ bool JSBaker::isSpecialCharacter(QChar c) { // If previous character is a special character, maybe don't omit new line (depends on next character as well) bool JSBaker::isSpecialCharacterPrevious(QChar c) { return (c == '\'' || c == '$' || c == '_' || c == '}' || c == ']' || c == ')' || c == '+' || c == '-' - || c == '"' || c == "'"); + || c == '"' || c == '\''); } // If next character is a special character, maybe don't omit new line (depends on previous character as well) @@ -243,5 +243,5 @@ bool JSBaker::isSpaceOrTab(QChar c) { // Check If the currentCharacter is " or ' or ` bool JSBaker::isQuote(QChar c) { - return (c == '"' || c == "'" || c == '`'); + return (c == '"' || c == '\'' || c == '`'); } diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp new file mode 100644 index 0000000000..16a0c89c7f --- /dev/null +++ b/libraries/baking/src/ModelBaker.cpp @@ -0,0 +1,521 @@ +// +// ModelBaker.cpp +// libraries/baking/src +// +// Created by Utkarsh Gautam on 9/29/17. +// 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 "ModelBaker.h" + +#include + +#include +#include + +#ifdef _WIN32 +#pragma warning( push ) +#pragma warning( disable : 4267 ) +#endif + +#include +#include + +#ifdef _WIN32 +#pragma warning( pop ) +#endif + +ModelBaker::ModelBaker(const QUrl& inputModelURL, TextureBakerThreadGetter inputTextureThreadGetter, + const QString& bakedOutputDirectory, const QString& originalOutputDirectory) : + _modelURL(inputModelURL), + _bakedOutputDir(bakedOutputDirectory), + _originalOutputDir(originalOutputDirectory), + _textureThreadGetter(inputTextureThreadGetter) +{ + auto tempDir = PathUtils::generateTemporaryDir(); + + if (tempDir.isEmpty()) { + handleError("Failed to create a temporary directory."); + return; + } + + _modelTempDir = tempDir; + + _originalModelFilePath = _modelTempDir.filePath(_modelURL.fileName()); + qDebug() << "Made temporary dir " << _modelTempDir; + qDebug() << "Origin file path: " << _originalModelFilePath; + +} + +ModelBaker::~ModelBaker() { + if (_modelTempDir.exists()) { + if (!_modelTempDir.remove(_originalModelFilePath)) { + qCWarning(model_baking) << "Failed to remove temporary copy of fbx file:" << _originalModelFilePath; + } + if (!_modelTempDir.rmdir(".")) { + qCWarning(model_baking) << "Failed to remove temporary directory:" << _modelTempDir; + } + } +} + +void ModelBaker::abort() { + Baker::abort(); + + // tell our underlying TextureBaker instances to abort + // the ModelBaker will wait until all are aborted before emitting its own abort signal + for (auto& textureBaker : _bakingTextures) { + textureBaker->abort(); + } +} + +bool ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback) { + if (mesh.wasCompressed) { + handleError("Cannot re-bake a file that contains compressed mesh"); + return false; + } + + Q_ASSERT(mesh.normals.size() == 0 || mesh.normals.size() == mesh.vertices.size()); + Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); + Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size()); + + int64_t numTriangles{ 0 }; + for (auto& part : mesh.parts) { + if ((part.quadTrianglesIndices.size() % 3) != 0 || (part.triangleIndices.size() % 3) != 0) { + handleWarning("Found a mesh part with invalid index data, skipping"); + continue; + } + numTriangles += part.quadTrianglesIndices.size() / 3; + numTriangles += part.triangleIndices.size() / 3; + } + + if (numTriangles == 0) { + return false; + } + + draco::TriangleSoupMeshBuilder meshBuilder; + + meshBuilder.Start(numTriangles); + + bool hasNormals{ mesh.normals.size() > 0 }; + bool hasColors{ mesh.colors.size() > 0 }; + bool hasTexCoords{ mesh.texCoords.size() > 0 }; + bool hasTexCoords1{ mesh.texCoords1.size() > 0 }; + bool hasPerFaceMaterials = (materialIDCallback) ? (mesh.parts.size() > 1 || materialIDCallback(0) != 0 ) : true; + bool needsOriginalIndices{ hasDeformers }; + + int normalsAttributeID { -1 }; + int colorsAttributeID { -1 }; + int texCoordsAttributeID { -1 }; + int texCoords1AttributeID { -1 }; + int faceMaterialAttributeID { -1 }; + int originalIndexAttributeID { -1 }; + + const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION, + 3, draco::DT_FLOAT32); + if (needsOriginalIndices) { + originalIndexAttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_ORIGINAL_INDEX, + 1, draco::DT_INT32); + } + + if (hasNormals) { + normalsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::NORMAL, + 3, draco::DT_FLOAT32); + } + if (hasColors) { + colorsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::COLOR, + 3, draco::DT_FLOAT32); + } + if (hasTexCoords) { + texCoordsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::TEX_COORD, + 2, draco::DT_FLOAT32); + } + if (hasTexCoords1) { + texCoords1AttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_TEX_COORD_1, + 2, draco::DT_FLOAT32); + } + if (hasPerFaceMaterials) { + faceMaterialAttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, + 1, draco::DT_UINT16); + } + + auto partIndex = 0; + draco::FaceIndex face; + uint16_t materialID; + + for (auto& part : mesh.parts) { + materialID = (materialIDCallback) ? materialIDCallback(partIndex) : partIndex; + + auto addFace = [&](QVector& indices, int index, draco::FaceIndex face) { + int32_t idx0 = indices[index]; + int32_t idx1 = indices[index + 1]; + int32_t idx2 = indices[index + 2]; + + if (hasPerFaceMaterials) { + meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID); + } + + meshBuilder.SetAttributeValuesForFace(positionAttributeID, face, + &mesh.vertices[idx0], &mesh.vertices[idx1], + &mesh.vertices[idx2]); + + if (needsOriginalIndices) { + meshBuilder.SetAttributeValuesForFace(originalIndexAttributeID, face, + &mesh.originalIndices[idx0], + &mesh.originalIndices[idx1], + &mesh.originalIndices[idx2]); + } + if (hasNormals) { + meshBuilder.SetAttributeValuesForFace(normalsAttributeID, face, + &mesh.normals[idx0], &mesh.normals[idx1], + &mesh.normals[idx2]); + } + if (hasColors) { + meshBuilder.SetAttributeValuesForFace(colorsAttributeID, face, + &mesh.colors[idx0], &mesh.colors[idx1], + &mesh.colors[idx2]); + } + if (hasTexCoords) { + meshBuilder.SetAttributeValuesForFace(texCoordsAttributeID, face, + &mesh.texCoords[idx0], &mesh.texCoords[idx1], + &mesh.texCoords[idx2]); + } + if (hasTexCoords1) { + meshBuilder.SetAttributeValuesForFace(texCoords1AttributeID, face, + &mesh.texCoords1[idx0], &mesh.texCoords1[idx1], + &mesh.texCoords1[idx2]); + } + }; + + for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { + addFace(part.quadTrianglesIndices, i, face++); + } + + for (int i = 0; (i + 2) < part.triangleIndices.size(); i += 3) { + addFace(part.triangleIndices, i, face++); + } + + partIndex++; + } + + auto dracoMesh = meshBuilder.Finalize(); + + if (!dracoMesh) { + handleWarning("Failed to finalize the baking of a draco Geometry node"); + return false; + } + + // we need to modify unique attribute IDs for custom attributes + // so the attributes are easily retrievable on the other side + if (hasPerFaceMaterials) { + dracoMesh->attribute(faceMaterialAttributeID)->set_unique_id(DRACO_ATTRIBUTE_MATERIAL_ID); + } + + if (hasTexCoords1) { + dracoMesh->attribute(texCoords1AttributeID)->set_unique_id(DRACO_ATTRIBUTE_TEX_COORD_1); + } + + if (needsOriginalIndices) { + dracoMesh->attribute(originalIndexAttributeID)->set_unique_id(DRACO_ATTRIBUTE_ORIGINAL_INDEX); + } + + draco::Encoder encoder; + + encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 14); + encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, 12); + encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 10); + encoder.SetSpeedOptions(0, 5); + + draco::EncoderBuffer buffer; + encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); + + FBXNode dracoNode; + dracoNode.name = "DracoMesh"; + auto value = QVariant::fromValue(QByteArray(buffer.data(), (int)buffer.size())); + dracoNode.properties.append(value); + + dracoMeshNode = dracoNode; + // Mesh compression successful return true + return true; +} + +QString ModelBaker::compressTexture(QString modelTextureFileName, image::TextureUsage::Type textureType) { + + QFileInfo modelTextureFileInfo{ modelTextureFileName.replace("\\", "/") }; + + if (modelTextureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) { + // re-baking a model that already references baked textures + // this is an error - return from here + handleError("Cannot re-bake a file that already references compressed textures"); + return QString::null; + } + + if (!image::getSupportedFormats().contains(modelTextureFileInfo.suffix())) { + // this is a texture format we don't bake, skip it + handleWarning(modelTextureFileName + " is not a bakeable texture format"); + return QString::null; + } + + // make sure this texture points to something and isn't one we've already re-mapped + QString textureChild { QString::null }; + if (!modelTextureFileInfo.filePath().isEmpty()) { + // check if this was an embedded texture that we already have in-memory content for + QByteArray textureContent; + + // figure out the URL to this texture, embedded or external + if (!modelTextureFileInfo.filePath().isEmpty()) { + textureContent = _textureContentMap.value(modelTextureFileName.toLocal8Bit()); + } + auto urlToTexture = getTextureURL(modelTextureFileInfo, modelTextureFileName, !textureContent.isNull()); + + QString bakedTextureFileName; + if (_remappedTexturePaths.contains(urlToTexture)) { + bakedTextureFileName = _remappedTexturePaths[urlToTexture]; + } else { + // construct the new baked texture file name and file path + // ensuring that the baked texture will have a unique name + // even if there was another texture with the same name at a different path + bakedTextureFileName = createBakedTextureFileName(modelTextureFileInfo); + _remappedTexturePaths[urlToTexture] = bakedTextureFileName; + } + + qCDebug(model_baking).noquote() << "Re-mapping" << modelTextureFileName + << "to" << bakedTextureFileName; + + QString bakedTextureFilePath{ + _bakedOutputDir + "/" + bakedTextureFileName + }; + + textureChild = bakedTextureFileName; + + if (!_bakingTextures.contains(urlToTexture)) { + _outputFiles.push_back(bakedTextureFilePath); + + // bake this texture asynchronously + bakeTexture(urlToTexture, textureType, _bakedOutputDir, bakedTextureFileName, textureContent); + } + } + + return textureChild; +} + +void ModelBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, + const QDir& outputDir, const QString& bakedFilename, const QByteArray& textureContent) { + + // start a bake for this texture and add it to our list to keep track of + QSharedPointer bakingTexture{ + new TextureBaker(textureURL, textureType, outputDir, bakedFilename, textureContent), + &TextureBaker::deleteLater + }; + + // make sure we hear when the baking texture is done or aborted + connect(bakingTexture.data(), &Baker::finished, this, &ModelBaker::handleBakedTexture); + connect(bakingTexture.data(), &TextureBaker::aborted, this, &ModelBaker::handleAbortedTexture); + + // keep a shared pointer to the baking texture + _bakingTextures.insert(textureURL, bakingTexture); + + // start baking the texture on one of our available worker threads + bakingTexture->moveToThread(_textureThreadGetter()); + QMetaObject::invokeMethod(bakingTexture.data(), "bake"); +} + +void ModelBaker::handleBakedTexture() { + TextureBaker* bakedTexture = qobject_cast(sender()); + qDebug() << "Handling baked texture" << bakedTexture->getTextureURL(); + + // make sure we haven't already run into errors, and that this is a valid texture + if (bakedTexture) { + if (!shouldStop()) { + if (!bakedTexture->hasErrors()) { + if (!_originalOutputDir.isEmpty()) { + // we've been asked to make copies of the originals, so we need to make copies of this if it is a linked texture + + // use the path to the texture being baked to determine if this was an embedded or a linked texture + + // it is embeddded if the texure being baked was inside a folder with the name of the model + // since that is the fake URL we provide when baking external textures + + if (!_modelURL.isParentOf(bakedTexture->getTextureURL())) { + // for linked textures we want to save a copy of original texture beside the original model + + qCDebug(model_baking) << "Saving original texture for" << bakedTexture->getTextureURL(); + + // check if we have a relative path to use for the texture + auto relativeTexturePath = texturePathRelativeToModel(_modelURL, bakedTexture->getTextureURL()); + + QFile originalTextureFile{ + _originalOutputDir + "/" + relativeTexturePath + bakedTexture->getTextureURL().fileName() + }; + + if (relativeTexturePath.length() > 0) { + // make the folders needed by the relative path + } + + if (originalTextureFile.open(QIODevice::WriteOnly) && originalTextureFile.write(bakedTexture->getOriginalTexture()) != -1) { + qCDebug(model_baking) << "Saved original texture file" << originalTextureFile.fileName() + << "for" << _modelURL; + } else { + handleError("Could not save original external texture " + originalTextureFile.fileName() + + " for " + _modelURL.toString()); + return; + } + } + } + + + // now that this texture has been baked and handled, we can remove that TextureBaker from our hash + _bakingTextures.remove(bakedTexture->getTextureURL()); + + checkIfTexturesFinished(); + } else { + // there was an error baking this texture - add it to our list of errors + _errorList.append(bakedTexture->getErrors()); + + // we don't emit finished yet so that the other textures can finish baking first + _pendingErrorEmission = true; + + // now that this texture has been baked, even though it failed, we can remove that TextureBaker from our list + _bakingTextures.remove(bakedTexture->getTextureURL()); + + // abort any other ongoing texture bakes since we know we'll end up failing + for (auto& bakingTexture : _bakingTextures) { + bakingTexture->abort(); + } + + checkIfTexturesFinished(); + } + } else { + // we have errors to attend to, so we don't do extra processing for this texture + // but we do need to remove that TextureBaker from our list + // and then check if we're done with all textures + _bakingTextures.remove(bakedTexture->getTextureURL()); + + checkIfTexturesFinished(); + } + } +} + +void ModelBaker::handleAbortedTexture() { + // grab the texture bake that was aborted and remove it from our hash since we don't need to track it anymore + TextureBaker* bakedTexture = qobject_cast(sender()); + + qDebug() << "Texture aborted: " << bakedTexture->getTextureURL(); + + if (bakedTexture) { + _bakingTextures.remove(bakedTexture->getTextureURL()); + } + + // since a texture we were baking aborted, our status is also aborted + _shouldAbort.store(true); + + // abort any other ongoing texture bakes since we know we'll end up failing + for (auto& bakingTexture : _bakingTextures) { + bakingTexture->abort(); + } + + checkIfTexturesFinished(); +} + +QUrl ModelBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded) { + QUrl urlToTexture; + + // use QFileInfo to easily split up the existing texture filename into its components + auto apparentRelativePath = QFileInfo(relativeFileName.replace("\\", "/")); + + if (isEmbedded) { + urlToTexture = _modelURL.toString() + "/" + apparentRelativePath.filePath(); + } else { + if (textureFileInfo.exists() && textureFileInfo.isFile()) { + // set the texture URL to the local texture that we have confirmed exists + urlToTexture = QUrl::fromLocalFile(textureFileInfo.absoluteFilePath()); + } else { + // external texture that we'll need to download or find + + // this is a relative file path which will require different handling + // depending on the location of the original model + if (_modelURL.isLocalFile() && apparentRelativePath.exists() && apparentRelativePath.isFile()) { + // the absolute path we ran into for the texture in the model exists on this machine + // so use that file + urlToTexture = QUrl::fromLocalFile(apparentRelativePath.absoluteFilePath()); + } else { + // we didn't find the texture on this machine at the absolute path + // so assume that it is right beside the model to match the behaviour of interface + urlToTexture = _modelURL.resolved(apparentRelativePath.fileName()); + } + } + } + + return urlToTexture; +} + +QString ModelBaker::texturePathRelativeToModel(QUrl modelURL, QUrl textureURL) { + auto modelPath = modelURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment); + auto texturePath = textureURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment); + + if (texturePath.startsWith(modelPath)) { + // texture path is a child of the model path, return the texture path without the model path + return texturePath.mid(modelPath.length()); + } else { + // the texture path was not a child of the model path, return the empty string + return ""; + } +} + +void ModelBaker::checkIfTexturesFinished() { + // check if we're done everything we need to do for this model + // and emit our finished signal if we're done + + if (_bakingTextures.isEmpty()) { + if (shouldStop()) { + // if we're checking for completion but we have errors + // that means one or more of our texture baking operations failed + + if (_pendingErrorEmission) { + setIsFinished(true); + } + + return; + } else { + qCDebug(model_baking) << "Finished baking, emitting finished" << _modelURL; + + setIsFinished(true); + } + } +} + +QString ModelBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) { + // first make sure we have a unique base name for this texture + // in case another texture referenced by this model has the same base name + auto& nameMatches = _textureNameMatchCount[textureFileInfo.baseName()]; + + QString bakedTextureFileName{ textureFileInfo.completeBaseName() }; + + if (nameMatches > 0) { + // there are already nameMatches texture with this name + // append - and that number to our baked texture file name so that it is unique + bakedTextureFileName += "-" + QString::number(nameMatches); + } + + bakedTextureFileName += BAKED_TEXTURE_EXT; + + // increment the number of name matches + ++nameMatches; + + return bakedTextureFileName; +} + +void ModelBaker::setWasAborted(bool wasAborted) { + if (wasAborted != _wasAborted.load()) { + Baker::setWasAborted(wasAborted); + + if (wasAborted) { + qCDebug(model_baking) << "Aborted baking" << _modelURL; + } + } +} diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h new file mode 100644 index 0000000000..6fd529af92 --- /dev/null +++ b/libraries/baking/src/ModelBaker.h @@ -0,0 +1,79 @@ +// +// ModelBaker.h +// libraries/baking/src +// +// Created by Utkarsh Gautam on 9/29/17. +// 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_ModelBaker_h +#define hifi_ModelBaker_h + +#include +#include +#include +#include + +#include "Baker.h" +#include "TextureBaker.h" + +#include "ModelBakingLoggingCategory.h" + +#include + +#include + +using TextureBakerThreadGetter = std::function; +using GetMaterialIDCallback = std::function ; + +class ModelBaker : public Baker { + Q_OBJECT + +public: + ModelBaker(const QUrl& inputModelURL, TextureBakerThreadGetter inputTextureThreadGetter, + const QString& bakedOutputDirectory, const QString& originalOutputDirectory = ""); + virtual ~ModelBaker(); + + bool compressMesh(FBXMesh& 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; + + QUrl getModelURL() const { return _modelURL; } + QString getBakedModelFilePath() const { return _bakedModelFilePath; } + +public slots: + virtual void abort() override; + +protected: + void checkIfTexturesFinished(); + + QHash _textureContentMap; + QUrl _modelURL; + QString _bakedOutputDir; + QString _originalOutputDir; + QString _bakedModelFilePath; + QDir _modelTempDir; + QString _originalModelFilePath; + +private slots: + void handleBakedTexture(); + void handleAbortedTexture(); + +private: + QString createBakedTextureFileName(const QFileInfo & textureFileInfo); + QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded = false); + void bakeTexture(const QUrl & textureURL, image::TextureUsage::Type textureType, const QDir & outputDir, + const QString & bakedFilename, const QByteArray & textureContent); + QString texturePathRelativeToModel(QUrl modelURL, QUrl textureURL); + + TextureBakerThreadGetter _textureThreadGetter; + QMultiHash> _bakingTextures; + QHash _textureNameMatchCount; + QHash _remappedTexturePaths; + bool _pendingErrorEmission{ false }; +}; + +#endif // hifi_ModelBaker_h diff --git a/libraries/baking/src/OBJBaker.cpp b/libraries/baking/src/OBJBaker.cpp new file mode 100644 index 0000000000..85771ff2e3 --- /dev/null +++ b/libraries/baking/src/OBJBaker.cpp @@ -0,0 +1,404 @@ +// +// OBJBaker.cpp +// libraries/baking/src +// +// Created by Utkarsh Gautam on 9/29/17. +// 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 +#include + +#include "OBJBaker.h" +#include "OBJReader.h" +#include "FBXWriter.h" + +const double UNIT_SCALE_FACTOR = 100.0; +const QByteArray PROPERTIES70_NODE_NAME = "Properties70"; +const QByteArray P_NODE_NAME = "P"; +const QByteArray C_NODE_NAME = "C"; +const QByteArray FBX_HEADER_EXTENSION = "FBXHeaderExtension"; +const QByteArray GLOBAL_SETTINGS_NODE_NAME = "GlobalSettings"; +const QByteArray OBJECTS_NODE_NAME = "Objects"; +const QByteArray GEOMETRY_NODE_NAME = "Geometry"; +const QByteArray MODEL_NODE_NAME = "Model"; +const QByteArray MATERIAL_NODE_NAME = "Material"; +const QByteArray TEXTURE_NODE_NAME = "Texture"; +const QByteArray TEXTURENAME_NODE_NAME = "TextureName"; +const QByteArray RELATIVEFILENAME_NODE_NAME = "RelativeFilename"; +const QByteArray CONNECTIONS_NODE_NAME = "Connections"; +const QByteArray CONNECTIONS_NODE_PROPERTY = "OO"; +const QByteArray CONNECTIONS_NODE_PROPERTY_1 = "OP"; +const QByteArray MESH = "Mesh"; + +void OBJBaker::bake() { + qDebug() << "OBJBaker" << _modelURL << "bake starting"; + + // trigger bakeOBJ once OBJ is loaded + connect(this, &OBJBaker::OBJLoaded, this, &OBJBaker::bakeOBJ); + + // make a local copy of the OBJ + loadOBJ(); +} + +void OBJBaker::loadOBJ() { + if (!QDir().mkpath(_bakedOutputDir)) { + handleError("Failed to create baked OBJ output folder " + _bakedOutputDir); + return; + } + + if (!QDir().mkpath(_originalOutputDir)) { + handleError("Failed to create original OBJ output folder " + _originalOutputDir); + return; + } + + // check if the OBJ is local or it needs to be downloaded + if (_modelURL.isLocalFile()) { + // loading the local OBJ + QFile localOBJ { _modelURL.toLocalFile() }; + + qDebug() << "Local file url: " << _modelURL << _modelURL.toString() << _modelURL.toLocalFile() << ", copying to: " << _originalModelFilePath; + + if (!localOBJ.exists()) { + handleError("Could not find " + _modelURL.toString()); + return; + } + + // make a copy in the output folder + if (!_originalOutputDir.isEmpty()) { + qDebug() << "Copying to: " << _originalOutputDir << "/" << _modelURL.fileName(); + localOBJ.copy(_originalOutputDir + "/" + _modelURL.fileName()); + } + + localOBJ.copy(_originalModelFilePath); + + // local OBJ is loaded emit signal to trigger its baking + emit OBJLoaded(); + } else { + // OBJ is remote, start download + auto& networkAccessManager = NetworkAccessManager::getInstance(); + + QNetworkRequest networkRequest; + + // setup the request to follow re-directs and always hit the network + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + networkRequest.setUrl(_modelURL); + + qCDebug(model_baking) << "Downloading" << _modelURL; + auto networkReply = networkAccessManager.get(networkRequest); + + connect(networkReply, &QNetworkReply::finished, this, &OBJBaker::handleOBJNetworkReply); + } +} + +void OBJBaker::handleOBJNetworkReply() { + auto requestReply = qobject_cast(sender()); + + if (requestReply->error() == QNetworkReply::NoError) { + qCDebug(model_baking) << "Downloaded" << _modelURL; + + // grab the contents of the reply and make a copy in the output folder + QFile copyOfOriginal(_originalModelFilePath); + + qDebug(model_baking) << "Writing copy of original obj to" << _originalModelFilePath << copyOfOriginal.fileName(); + + if (!copyOfOriginal.open(QIODevice::WriteOnly)) { + // add an error to the error list for this obj stating that a duplicate of the original obj could not be made + handleError("Could not create copy of " + _modelURL.toString() + " (Failed to open " + _originalModelFilePath + ")"); + return; + } + if (copyOfOriginal.write(requestReply->readAll()) == -1) { + handleError("Could not create copy of " + _modelURL.toString() + " (Failed to write)"); + return; + } + + // close that file now that we are done writing to it + copyOfOriginal.close(); + + if (!_originalOutputDir.isEmpty()) { + copyOfOriginal.copy(_originalOutputDir + "/" + _modelURL.fileName()); + } + + // remote OBJ is loaded emit signal to trigger its baking + emit OBJLoaded(); + } else { + // add an error to our list stating that the OBJ could not be downloaded + handleError("Failed to download " + _modelURL.toString()); + } +} + +void OBJBaker::bakeOBJ() { + // Read the OBJ file + QFile objFile(_originalModelFilePath); + if (!objFile.open(QIODevice::ReadOnly)) { + handleError("Error opening " + _originalModelFilePath + " for reading"); + return; + } + + QByteArray objData = objFile.readAll(); + + bool combineParts = true; // set true so that OBJReader reads material info from material library + OBJReader reader; + auto geometry = reader.readOBJ(objData, QVariantHash(), combineParts, _modelURL); + + // Write OBJ Data as FBX tree nodes + FBXNode rootNode; + createFBXNodeTree(rootNode, *geometry); + + // Serialize the resultant FBX tree + auto encodedFBX = FBXWriter::encodeFBX(rootNode); + + // Export as baked FBX + auto fileName = _modelURL.fileName(); + auto baseName = fileName.left(fileName.lastIndexOf('.')); + auto bakedFilename = baseName + ".baked.fbx"; + + _bakedModelFilePath = _bakedOutputDir + "/" + bakedFilename; + + QFile bakedFile; + bakedFile.setFileName(_bakedModelFilePath); + if (!bakedFile.open(QIODevice::WriteOnly)) { + handleError("Error opening " + _bakedModelFilePath + " for writing"); + return; + } + + bakedFile.write(encodedFBX); + + // Export successful + _outputFiles.push_back(_bakedModelFilePath); + qCDebug(model_baking) << "Exported" << _modelURL << "to" << _bakedModelFilePath; + + checkIfTexturesFinished(); +} + +void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) { + // Generating FBX Header Node + FBXNode headerNode; + headerNode.name = FBX_HEADER_EXTENSION; + + // Generating global settings node + // Required for Unit Scale Factor + FBXNode globalSettingsNode; + globalSettingsNode.name = GLOBAL_SETTINGS_NODE_NAME; + + // Setting the tree hierarchy: GlobalSettings -> Properties70 -> P -> Properties + FBXNode properties70Node; + properties70Node.name = PROPERTIES70_NODE_NAME; + + FBXNode pNode; + { + pNode.name = P_NODE_NAME; + pNode.properties.append({ + "UnitScaleFactor", "double", "Number", "", + UNIT_SCALE_FACTOR + }); + } + + properties70Node.children = { pNode }; + globalSettingsNode.children = { properties70Node }; + + // Generating Object node + _objectNode.name = OBJECTS_NODE_NAME; + + // Generating Object node's child - Geometry node + FBXNode geometryNode; + geometryNode.name = GEOMETRY_NODE_NAME; + { + _geometryID = nextNodeID(); + geometryNode.properties = { + _geometryID, + GEOMETRY_NODE_NAME, + MESH + }; + } + + // 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); + geometryNode.children.append(dracoNode); + + // Generating Object node's child - Model node + FBXNode modelNode; + modelNode.name = MODEL_NODE_NAME; + { + _modelID = nextNodeID(); + modelNode.properties = { _modelID, MODEL_NODE_NAME, MESH }; + } + + _objectNode.children = { geometryNode, modelNode }; + + // Generating Objects node's child - Material node + auto& meshParts = geometry.meshes[0].parts; + for (auto& meshPart : meshParts) { + FBXNode materialNode; + materialNode.name = MATERIAL_NODE_NAME; + if (geometry.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); + } + } else { + setMaterialNodeProperties(materialNode, meshPart.materialID, geometry); + } + + _objectNode.children.append(materialNode); + } + + // Generating Texture Node + // iterate through mesh parts and process the associated textures + auto size = meshParts.size(); + for (int i = 0; i < size; i++) { + QString material = meshParts[i].materialID; + FBXMaterial currentMaterial = geometry.materials[material]; + if (!currentMaterial.albedoTexture.filename.isEmpty() || !currentMaterial.specularTexture.filename.isEmpty()) { + _textureID = nextNodeID(); + _mapTextureMaterial.emplace_back(_textureID, i); + + FBXNode textureNode; + { + textureNode.name = TEXTURE_NODE_NAME; + textureNode.properties = { _textureID }; + } + + // Texture node child - TextureName node + FBXNode textureNameNode; + { + textureNameNode.name = TEXTURENAME_NODE_NAME; + QByteArray propertyString = (!currentMaterial.albedoTexture.filename.isEmpty()) ? "Kd" : "Ka"; + textureNameNode.properties = { propertyString }; + } + + // Texture node child - Relative Filename node + FBXNode relativeFilenameNode; + { + relativeFilenameNode.name = RELATIVEFILENAME_NODE_NAME; + } + + QByteArray textureFileName = (!currentMaterial.albedoTexture.filename.isEmpty()) ? currentMaterial.albedoTexture.filename : currentMaterial.specularTexture.filename; + + auto textureType = (!currentMaterial.albedoTexture.filename.isEmpty()) ? image::TextureUsage::Type::ALBEDO_TEXTURE : image::TextureUsage::Type::SPECULAR_TEXTURE; + + // Compress the texture using ModelBaker::compressTexture() and store compressed file's name in the node + auto textureFile = compressTexture(textureFileName, textureType); + if (textureFile.isNull()) { + // Baking failed return + handleError("Failed to compress texture: " + textureFileName); + return; + } + relativeFilenameNode.properties = { textureFile }; + + textureNode.children = { textureNameNode, relativeFilenameNode }; + + _objectNode.children.append(textureNode); + } + } + + // Generating Connections node + FBXNode connectionsNode; + connectionsNode.name = CONNECTIONS_NODE_NAME; + + // connect Geometry to Model + FBXNode cNode; + cNode.name = C_NODE_NAME; + cNode.properties = { CONNECTIONS_NODE_PROPERTY, _geometryID, _modelID }; + connectionsNode.children = { cNode }; + + // connect all materials to model + for (auto& materialID : _materialIDs) { + FBXNode cNode; + cNode.name = C_NODE_NAME; + cNode.properties = { CONNECTIONS_NODE_PROPERTY, materialID, _modelID }; + connectionsNode.children.append(cNode); + } + + // Connect textures to materials + for (const auto& texMat : _mapTextureMaterial) { + FBXNode cAmbientNode; + cAmbientNode.name = C_NODE_NAME; + cAmbientNode.properties = { + CONNECTIONS_NODE_PROPERTY_1, + texMat.first, + _materialIDs[texMat.second], + "AmbientFactor" + }; + connectionsNode.children.append(cAmbientNode); + + FBXNode cDiffuseNode; + cDiffuseNode.name = C_NODE_NAME; + cDiffuseNode.properties = { + CONNECTIONS_NODE_PROPERTY_1, + texMat.first, + _materialIDs[texMat.second], + "DiffuseColor" + }; + connectionsNode.children.append(cDiffuseNode); + } + + // Make all generated nodes children of rootNode + rootNode.children = { globalSettingsNode, _objectNode, connectionsNode }; +} + +// Set properties for material nodes +void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry) { + auto materialID = nextNodeID(); + _materialIDs.push_back(materialID); + materialNode.properties = { materialID, material, MESH }; + + FBXMaterial currentMaterial = geometry.materials[material]; + + // Setting the hierarchy: Material -> Properties70 -> P -> Properties + FBXNode properties70Node; + properties70Node.name = PROPERTIES70_NODE_NAME; + + // Set diffuseColor + FBXNode pNodeDiffuseColor; + { + pNodeDiffuseColor.name = P_NODE_NAME; + pNodeDiffuseColor.properties.append({ + "DiffuseColor", "Color", "", "A", + currentMaterial.diffuseColor[0], currentMaterial.diffuseColor[1], currentMaterial.diffuseColor[2] + }); + } + properties70Node.children.append(pNodeDiffuseColor); + + // Set specularColor + FBXNode pNodeSpecularColor; + { + pNodeSpecularColor.name = P_NODE_NAME; + pNodeSpecularColor.properties.append({ + "SpecularColor", "Color", "", "A", + currentMaterial.specularColor[0], currentMaterial.specularColor[1], currentMaterial.specularColor[2] + }); + } + properties70Node.children.append(pNodeSpecularColor); + + // Set Shininess + FBXNode pNodeShininess; + { + pNodeShininess.name = P_NODE_NAME; + pNodeShininess.properties.append({ + "Shininess", "Number", "", "A", + currentMaterial.shininess + }); + } + properties70Node.children.append(pNodeShininess); + + // Set Opacity + FBXNode pNodeOpacity; + { + pNodeOpacity.name = P_NODE_NAME; + pNodeOpacity.properties.append({ + "Opacity", "Number", "", "A", + currentMaterial.opacity + }); + } + properties70Node.children.append(pNodeOpacity); + + materialNode.children.append(properties70Node); +} diff --git a/libraries/baking/src/OBJBaker.h b/libraries/baking/src/OBJBaker.h new file mode 100644 index 0000000000..e888c7b1d8 --- /dev/null +++ b/libraries/baking/src/OBJBaker.h @@ -0,0 +1,54 @@ +// +// OBJBaker.h +// libraries/baking/src +// +// Created by Utkarsh Gautam on 9/29/17. +// 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_OBJBaker_h +#define hifi_OBJBaker_h + +#include "Baker.h" +#include "TextureBaker.h" +#include "ModelBaker.h" + +#include "ModelBakingLoggingCategory.h" + +using TextureBakerThreadGetter = std::function; + +using NodeID = qlonglong; + +class OBJBaker : public ModelBaker { + Q_OBJECT +public: + using ModelBaker::ModelBaker; + +public slots: + virtual void bake() override; + +signals: + void OBJLoaded(); + +private slots: + void bakeOBJ(); + void handleOBJNetworkReply(); + +private: + void loadOBJ(); + void createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry); + void setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry); + NodeID nextNodeID() { return _nodeID++; } + + NodeID _nodeID { 0 }; + NodeID _geometryID; + NodeID _modelID; + std::vector _materialIDs; + NodeID _textureID; + std::vector> _mapTextureMaterial; + FBXNode _objectNode; +}; +#endif // hifi_OBJBaker_h diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index d8dd7f5e35..359ff6b33a 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -183,6 +183,7 @@ namespace controller { makeButtonPair(Action::ACTION2, "ACTION2"), makeButtonPair(Action::CONTEXT_MENU, "CONTEXT_MENU"), makeButtonPair(Action::TOGGLE_MUTE, "TOGGLE_MUTE"), + makeButtonPair(Action::SPRINT, "SPRINT") }; return availableInputs; } diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index a133d62c9f..0c77d63863 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -174,6 +174,7 @@ enum class Action { TRACKED_OBJECT_13, TRACKED_OBJECT_14, TRACKED_OBJECT_15, + SPRINT, NUM_ACTIONS }; diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 29f011fba2..371deec7d5 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -73,7 +73,8 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) { qCDebug(controllers) << "Registered input device <" << device->getName() << "> deviceID = " << deviceID; - for (const auto& inputMapping : device->getAvailableInputs()) { + auto inputs = device->getAvailableInputs(); + for (const auto& inputMapping : inputs) { const auto& input = inputMapping.first; // Ignore aliases if (_endpointsByInput.count(input)) { @@ -126,7 +127,8 @@ void UserInputMapper::removeDevice(int deviceID) { _mappingsByDevice.erase(mappingsEntry); } - for (const auto& inputMapping : device->getAvailableInputs()) { + auto inputs = device->getAvailableInputs(); + for (const auto& inputMapping : inputs) { const auto& input = inputMapping.first; auto endpoint = _endpointsByInput.find(input); if (endpoint != _endpointsByInput.end()) { @@ -171,7 +173,7 @@ InputDevice::Pointer UserInputMapper::getDevice(const Input& input) { } } -QString UserInputMapper::getDeviceName(uint16 deviceID) { +QString UserInputMapper::getDeviceName(uint16 deviceID) { Locker locker(_lock); if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { return _registeredDevices[deviceID]->_name; @@ -181,7 +183,7 @@ QString UserInputMapper::getDeviceName(uint16 deviceID) { int UserInputMapper::findDevice(QString name) const { Locker locker(_lock); - for (auto device : _registeredDevices) { + for (const auto& device : _registeredDevices) { if (device.second->_name == name) { return device.first; } @@ -192,7 +194,7 @@ int UserInputMapper::findDevice(QString name) const { QVector UserInputMapper::getDeviceNames() { Locker locker(_lock); QVector result; - for (auto device : _registeredDevices) { + for (const auto& device : _registeredDevices) { QString deviceName = device.second->_name.split(" (")[0]; result << deviceName; } @@ -218,7 +220,7 @@ Input UserInputMapper::findDeviceInput(const QString& inputName) const { const auto& device = _registeredDevices.at(deviceID); auto deviceInputs = device->getAvailableInputs(); - for (auto input : deviceInputs) { + for (const auto& input : deviceInputs) { if (input.second == inputName) { return input.first; } @@ -321,7 +323,8 @@ QVector UserInputMapper::getAllActions() const { QString UserInputMapper::getActionName(Action action) const { Locker locker(_lock); - for (auto actionPair : getActionInputs()) { + auto inputs = getActionInputs(); + for (const auto& actionPair : inputs) { if (actionPair.first.channel == toInt(action)) { return actionPair.second; } @@ -331,18 +334,20 @@ QString UserInputMapper::getActionName(Action action) const { QString UserInputMapper::getStandardPoseName(uint16_t pose) { Locker locker(_lock); - for (auto posePair : getStandardInputs()) { + auto inputs = getStandardInputs(); + for (const auto& posePair : inputs) { if (posePair.first.channel == pose && posePair.first.getType() == ChannelType::POSE) { return posePair.second; } } return QString(); -} +} QVector UserInputMapper::getActionNames() const { Locker locker(_lock); QVector result; - for (auto actionPair : getActionInputs()) { + auto inputs = getActionInputs(); + for (const auto& actionPair : inputs) { result << actionPair.second; } return result; @@ -357,7 +362,7 @@ Pose UserInputMapper::getPoseState(Action action) const { bool UserInputMapper::triggerHapticPulse(float strength, float duration, controller::Hand hand) { Locker locker(_lock); bool toReturn = false; - for (auto device : _registeredDevices) { + for (const auto& device : _registeredDevices) { toReturn = toReturn || device.second->triggerHapticPulse(strength, duration, hand); } return toReturn; @@ -469,7 +474,7 @@ void UserInputMapper::runMappings() { if (debugRoutes) { qCDebug(controllers) << "Beginning mapping frame"; } - for (auto endpointEntry : this->_endpointsByInput) { + for (const auto& endpointEntry : _endpointsByInput) { endpointEntry.second->reset(); } @@ -542,9 +547,9 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) { } - // Most endpoints can only be read once (though a given mapping can route them to + // Most endpoints can only be read once (though a given mapping can route them to // multiple places). Consider... If the default is to wire the A button to JUMP - // and someone else wires it to CONTEXT_MENU, I don't want both to occur when + // and someone else wires it to CONTEXT_MENU, I don't want both to occur when // I press the button. The exception is if I'm wiring a control back to itself // in order to adjust my interface, like inverting the Y axis on an analog stick if (!route->peek && !source->readable()) { @@ -897,7 +902,8 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) if (value.isArray()) { // Support "when" : [ "GamePad.RB", "GamePad.LB" ] Conditional::List children; - for (auto arrayItem : value.toArray()) { + auto array = value.toArray(); + for (const auto& arrayItem : array) { Conditional::Pointer childConditional = parseConditional(arrayItem); if (!childConditional) { return Conditional::Pointer(); @@ -908,7 +914,7 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) } else if (value.isString()) { // Support "when" : "GamePad.RB" auto conditionalToken = value.toString(); - + // Detect for modifier case (Not...) QString conditionalModifier; const QString JSON_CONDITIONAL_MODIFIER_NOT("!"); @@ -943,12 +949,12 @@ Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) { result = Filter::getFactory().create(value.toString()); } else if (value.isObject()) { result = Filter::parse(value.toObject()); - } + } if (!result) { qWarning() << "Invalid filter definition " << value; } - + return result; } @@ -960,7 +966,7 @@ Filter::List UserInputMapper::parseFilters(const QJsonValue& value) { if (value.isArray()) { Filter::List result; auto filtersArray = value.toArray(); - for (auto filterValue : filtersArray) { + for (const auto& filterValue : filtersArray) { Filter::Pointer filter = parseFilter(filterValue); if (!filter) { return Filter::List(); @@ -968,7 +974,7 @@ Filter::List UserInputMapper::parseFilters(const QJsonValue& value) { result.push_back(filter); } return result; - } + } Filter::Pointer filter = parseFilter(value); if (!filter) { @@ -980,7 +986,8 @@ Filter::List UserInputMapper::parseFilters(const QJsonValue& value) { Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) { if (value.isArray()) { ArrayEndpoint::Pointer result = std::make_shared(); - for (auto arrayItem : value.toArray()) { + auto array = value.toArray(); + for (const auto& arrayItem : array) { Endpoint::Pointer destination = parseEndpoint(arrayItem); if (!destination) { return Endpoint::Pointer(); @@ -988,14 +995,14 @@ Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) { result->_children.push_back(destination); } return result; - } - + } + return parseEndpoint(value); } Endpoint::Pointer UserInputMapper::parseAxis(const QJsonValue& value) { if (value.isObject()) { - auto object = value.toObject(); + auto object = value.toObject(); if (object.contains("makeAxis")) { auto axisValue = object.value("makeAxis"); if (axisValue.isArray()) { @@ -1017,7 +1024,8 @@ Endpoint::Pointer UserInputMapper::parseAxis(const QJsonValue& value) { Endpoint::Pointer UserInputMapper::parseAny(const QJsonValue& value) { if (value.isArray()) { Endpoint::List children; - for (auto arrayItem : value.toArray()) { + auto array = value.toArray(); + for (const auto& arrayItem : array) { Endpoint::Pointer destination = parseEndpoint(arrayItem); if (!destination) { return Endpoint::Pointer(); @@ -1162,7 +1170,7 @@ Mapping::Pointer UserInputMapper::parseMapping(const QString& json) { template bool hasDebuggableRoute(const T& routes) { - for (auto route : routes) { + for (const auto& route : routes) { if (route->debug) { return true; } @@ -1174,7 +1182,7 @@ bool hasDebuggableRoute(const T& routes) { void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) { Locker locker(_lock); // New routes for a device get injected IN FRONT of existing routes. Routes - // are processed in order so this ensures that the standard -> action processing + // are processed in order so this ensures that the standard -> action processing // takes place after all of the hardware -> standard or hardware -> action processing // because standard -> action is the first set of routes added. Route::List standardRoutes = mapping->routes; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp index ef9f04402b..f73268b41f 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp @@ -16,11 +16,14 @@ using namespace controller; void ActionEndpoint::apply(float newValue, const Pointer& source) { - InputRecorder* inputRecorder = InputRecorder::getInstance(); auto userInputMapper = DependencyManager::get(); - QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); - if(inputRecorder->isPlayingback()) { - newValue = inputRecorder->getActionState(actionName); + InputRecorder* inputRecorder = InputRecorder::getInstance(); + QString actionName; + if (inputRecorder->isPlayingback() || inputRecorder->isRecording()) { + actionName = userInputMapper->getActionName(Action(_input.getChannel())); + if (inputRecorder->isPlayingback()) { + newValue = inputRecorder->getActionState(actionName); + } } _currentValue += newValue; @@ -32,10 +35,12 @@ void ActionEndpoint::apply(float newValue, const Pointer& source) { void ActionEndpoint::apply(const Pose& value, const Pointer& source) { _currentPose = value; - InputRecorder* inputRecorder = InputRecorder::getInstance(); auto userInputMapper = DependencyManager::get(); - QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); - inputRecorder->setActionState(actionName, _currentPose); + InputRecorder* inputRecorder = InputRecorder::getInstance(); + if (inputRecorder->isRecording()) { + QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); + inputRecorder->setActionState(actionName, _currentPose); + } if (!_currentPose.isValid()) { return; diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index 6a19a34727..47a213cf71 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -21,10 +21,6 @@ glm::uvec2 NullDisplayPlugin::getRecommendedRenderSize() const { return glm::uvec2(100, 100); } -bool NullDisplayPlugin::hasFocus() const { - return false; -} - void NullDisplayPlugin::submitFrame(const gpu::FramePointer& frame) { if (frame) { _gpuContext->consumeFrameUpdates(frame); diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index 97b71b5780..11563b3798 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -16,7 +16,6 @@ public: grouping getGrouping() const override { return DEVELOPER; } glm::uvec2 getRecommendedRenderSize() const override; - bool hasFocus() const override; void submitFrame(const gpu::FramePointer& newFrame) override; QImage getScreenshot(float aspectRatio = 0.0f) const override; QImage getSecondaryCameraScreenshot() const override; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index db9b86b9dd..4b33565bfd 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -530,7 +530,11 @@ void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::Textur batch.setStateScissorRect(scissor); batch.setViewportTransform(viewport); batch.setResourceTexture(0, texture); +#ifndef USE_GLES batch.setPipeline(_presentPipeline); +#else + batch.setPipeline(_simplePipeline); +#endif batch.draw(gpu::TRIANGLE_STRIP, 4); if (copyFbo) { gpu::Vec4i copyFboRect(0, 0, copyFbo->getWidth(), copyFbo->getHeight()); @@ -831,11 +835,6 @@ glm::uvec2 OpenGLDisplayPlugin::getSurfaceSize() const { return result; } -bool OpenGLDisplayPlugin::hasFocus() const { - auto window = _container->getPrimaryWidget(); - return window ? window->hasFocus() : false; -} - void OpenGLDisplayPlugin::assertNotPresentThread() const { Q_ASSERT(QThread::currentThread() != _presentThread); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index bde7984ec0..1cc060161b 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -98,8 +98,6 @@ protected: virtual void compositePointer(); virtual void compositeExtra() {}; - virtual bool hasFocus() const override; - // These functions must only be called on the presentation thread virtual void customizeContext(); virtual void uncustomizeContext(); diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp index 00879e1380..280b44cec0 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPConnection.cpp @@ -55,7 +55,7 @@ HTTPConnection::~HTTPConnection() { QHash HTTPConnection::parseUrlEncodedForm() { // make sure we have the correct MIME type - QList elements = _requestHeaders.value("Content-Type").split(';'); + QList elements = requestHeader("Content-Type").split(';'); QString contentType = elements.at(0).trimmed(); if (contentType != "application/x-www-form-urlencoded") { @@ -75,7 +75,7 @@ QHash HTTPConnection::parseUrlEncodedForm() { QList HTTPConnection::parseFormData() const { // make sure we have the correct MIME type - QList elements = _requestHeaders.value("Content-Type").split(';'); + QList elements = requestHeader("Content-Type").split(';'); QString contentType = elements.at(0).trimmed(); @@ -251,7 +251,7 @@ void HTTPConnection::readHeaders() { if (trimmed.isEmpty()) { _socket->disconnect(this, SLOT(readHeaders())); - QByteArray clength = _requestHeaders.value("Content-Length"); + QByteArray clength = requestHeader("Content-Length"); if (clength.isEmpty()) { _parentManager->handleHTTPRequest(this, _requestUrl); @@ -275,7 +275,7 @@ void HTTPConnection::readHeaders() { respond("400 Bad Request", "The header was malformed."); return; } - _lastRequestHeader = trimmed.left(idx); + _lastRequestHeader = trimmed.left(idx).toLower(); QByteArray& value = _requestHeaders[_lastRequestHeader]; if (!value.isEmpty()) { value.append(", "); diff --git a/libraries/embedded-webserver/src/HTTPConnection.h b/libraries/embedded-webserver/src/HTTPConnection.h index 60408d4325..e4d23e3c90 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.h +++ b/libraries/embedded-webserver/src/HTTPConnection.h @@ -72,8 +72,8 @@ public: /// Returns a reference to the request URL. const QUrl& requestUrl () const { return _requestUrl; } - /// Returns a reference to the request headers. - const Headers& requestHeaders () const { return _requestHeaders; } + /// Returns a copy of the request header value. If it does not exist, it will return a default constructed QByteArray. + QByteArray requestHeader(const QString& key) const { return _requestHeaders.value(key.toLower().toLocal8Bit()); } /// Returns a reference to the request content. const QByteArray& requestContent () const { return _requestContent; } diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index 119a46b68f..ea8edb8a08 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -9,6 +9,7 @@ #include "RenderableMaterialEntityItem.h" #include "RenderPipelines.h" +#include "GeometryCache.h" using namespace render; using namespace render::entities; @@ -90,138 +91,6 @@ ShapeKey MaterialEntityRenderer::getShapeKey() { return builder.build(); } -glm::vec3 MaterialEntityRenderer::getVertexPos(float phi, float theta) { - return glm::vec3(glm::sin(theta) * glm::cos(phi), glm::cos(theta), glm::sin(theta) * glm::sin(phi)); -} - -glm::vec3 MaterialEntityRenderer::getTangent(float phi, float theta) { - return glm::vec3(-glm::cos(theta) * glm::cos(phi), glm::sin(theta), -glm::cos(theta) * glm::sin(phi)); -} - -void MaterialEntityRenderer::addVertex(std::vector& buffer, const glm::vec3& pos, const glm::vec3& tan, const glm::vec2 uv) { - buffer.push_back(pos.x); buffer.push_back(pos.y); buffer.push_back(pos.z); - buffer.push_back(tan.x); buffer.push_back(tan.y); buffer.push_back(tan.z); - buffer.push_back(uv.x); buffer.push_back(uv.y); -} - -void MaterialEntityRenderer::addTriangleFan(std::vector& buffer, int stack, int step) { - float v1 = ((float)stack) / STACKS; - float theta1 = v1 * (float)M_PI; - glm::vec3 tip = getVertexPos(0, theta1); - float v2 = ((float)(stack + step)) / STACKS; - float theta2 = v2 * (float)M_PI; - for (int i = 0; i < SLICES; i++) { - float u1 = ((float)i) / SLICES; - float u2 = ((float)(i + step)) / SLICES; - float phi1 = u1 * M_PI_TIMES_2; - float phi2 = u2 * M_PI_TIMES_2; - /* (flipped for negative step) - p1 - / \ - / \ - / \ - p3 ------ p2 - */ - - glm::vec3 pos2 = getVertexPos(phi2, theta2); - glm::vec3 pos3 = getVertexPos(phi1, theta2); - - glm::vec3 tan1 = getTangent(0, theta1); - glm::vec3 tan2 = getTangent(phi2, theta2); - glm::vec3 tan3 = getTangent(phi1, theta2); - - glm::vec2 uv1 = glm::vec2((u1 + u2) / 2.0f, v1); - glm::vec2 uv2 = glm::vec2(u2, v2); - glm::vec2 uv3 = glm::vec2(u1, v2); - - addVertex(buffer, tip, tan1, uv1); - addVertex(buffer, pos2, tan2, uv2); - addVertex(buffer, pos3, tan3, uv3); - - _numVertices += 3; - } -} - -int MaterialEntityRenderer::_numVertices = 0; -std::shared_ptr MaterialEntityRenderer::_streamFormat = nullptr; -std::shared_ptr MaterialEntityRenderer::_stream = nullptr; -std::shared_ptr MaterialEntityRenderer::_verticesBuffer = nullptr; - -void MaterialEntityRenderer::generateMesh() { - _streamFormat = std::make_shared(); - _stream = std::make_shared(); - _verticesBuffer = std::make_shared(); - - const int NUM_POS_COORDS = 3; - const int NUM_TANGENT_COORDS = 3; - const int VERTEX_TANGENT_OFFSET = NUM_POS_COORDS * sizeof(float); - const int VERTEX_TEXCOORD_OFFSET = VERTEX_TANGENT_OFFSET + NUM_TANGENT_COORDS * sizeof(float); - - _streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - _streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - _streamFormat->setAttribute(gpu::Stream::TANGENT, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_TANGENT_OFFSET); - _streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); - - _stream->addBuffer(_verticesBuffer, 0, _streamFormat->getChannels().at(0)._stride); - - std::vector vertexBuffer; - - // Top - addTriangleFan(vertexBuffer, 0, 1); - - // Middle section - for (int j = 1; j < STACKS - 1; j++) { - float v1 = ((float)j) / STACKS; - float v2 = ((float)(j + 1)) / STACKS; - float theta1 = v1 * (float)M_PI; - float theta2 = v2 * (float)M_PI; - for (int i = 0; i < SLICES; i++) { - float u1 = ((float)i) / SLICES; - float u2 = ((float)(i + 1)) / SLICES; - float phi1 = u1 * M_PI_TIMES_2; - float phi2 = u2 * M_PI_TIMES_2; - - /* - p2 ---- p3 - | / | - | / | - | / | - p1 ---- p4 - */ - - glm::vec3 pos1 = getVertexPos(phi1, theta2); - glm::vec3 pos2 = getVertexPos(phi1, theta1); - glm::vec3 pos3 = getVertexPos(phi2, theta1); - glm::vec3 pos4 = getVertexPos(phi2, theta2); - - glm::vec3 tan1 = getTangent(phi1, theta2); - glm::vec3 tan2 = getTangent(phi1, theta1); - glm::vec3 tan3 = getTangent(phi2, theta1); - glm::vec3 tan4 = getTangent(phi2, theta2); - - glm::vec2 uv1 = glm::vec2(u1, v2); - glm::vec2 uv2 = glm::vec2(u1, v1); - glm::vec2 uv3 = glm::vec2(u2, v1); - glm::vec2 uv4 = glm::vec2(u2, v2); - - addVertex(vertexBuffer, pos1, tan1, uv1); - addVertex(vertexBuffer, pos2, tan2, uv2); - addVertex(vertexBuffer, pos3, tan3, uv3); - - addVertex(vertexBuffer, pos3, tan3, uv3); - addVertex(vertexBuffer, pos4, tan4, uv4); - addVertex(vertexBuffer, pos1, tan1, uv1); - - _numVertices += 6; - } - } - - // Bottom - addTriangleFan(vertexBuffer, STACKS, -1); - - _verticesBuffer->append(vertexBuffer.size() * sizeof(float), (gpu::Byte*) vertexBuffer.data()); -} - void MaterialEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderableMaterialEntityItem::render"); Q_ASSERT(args->_batch); @@ -252,14 +121,7 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) { args->_details._materialSwitches++; // Draw! - if (_numVertices == 0) { - generateMesh(); - } + DependencyManager::get()->renderSphere(batch); - batch.setInputFormat(_streamFormat); - batch.setInputStream(0, *_stream); - batch.draw(gpu::TRIANGLES, _numVertices, 0); - - const int NUM_VERTICES_PER_TRIANGLE = 3; - args->_details._trianglesRendered += _numVertices / NUM_VERTICES_PER_TRIANGLE; + args->_details._trianglesRendered += (int)DependencyManager::get()->getSphereTriangleCount(); } diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h index fef1a41138..8de2190a0c 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h @@ -40,20 +40,6 @@ private: Transform _renderTransform; std::shared_ptr _drawMaterial; - - static int _numVertices; - static std::shared_ptr _streamFormat; - static std::shared_ptr _stream; - static std::shared_ptr _verticesBuffer; - - void generateMesh(); - void addTriangleFan(std::vector& buffer, int stack, int step); - static glm::vec3 getVertexPos(float phi, float theta); - static glm::vec3 getTangent(float phi, float theta); - static void addVertex(std::vector& buffer, const glm::vec3& pos, const glm::vec3& tan, const glm::vec2 uv); - const int SLICES = 15; - const int STACKS = 9; - const float M_PI_TIMES_2 = 2.0f * (float)M_PI; }; } } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index e5cbfdb59d..b05854da4e 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -19,6 +19,8 @@ #include "render-utils/simple_vert.h" #include "render-utils/simple_frag.h" +#include "RenderPipelines.h" + //#define SHAPE_ENTITY_USE_FADE_EFFECT #ifdef SHAPE_ENTITY_USE_FADE_EFFECT #include @@ -108,11 +110,94 @@ bool ShapeEntityRenderer::isTransparent() const { if (_procedural.isEnabled() && _procedural.isFading()) { return Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) < 1.0f; } - - // return _entity->getLocalRenderAlpha() < 1.0f || Parent::isTransparent(); + + auto mat = _materials.find("0"); + if (mat != _materials.end()) { + if (mat->second.top().material) { + auto matKey = mat->second.top().material->getKey(); + if (matKey.isTranslucent()) { + return true; + } + } + } + return Parent::isTransparent(); } +ItemKey ShapeEntityRenderer::getKey() { + ItemKey::Builder builder; + builder.withTypeShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); + + withReadLock([&] { + if (isTransparent()) { + builder.withTransparent(); + } + }); + + return builder.build(); +} + +bool ShapeEntityRenderer::useMaterialPipeline() const { + bool proceduralReady = resultWithReadLock([&] { + return _procedural.isReady(); + }); + if (proceduralReady) { + return false; + } + + graphics::MaterialKey drawMaterialKey; + auto mat = _materials.find("0"); + if (mat != _materials.end() && mat->second.top().material) { + drawMaterialKey = mat->second.top().material->getKey(); + } + + if (drawMaterialKey.isEmissive() || drawMaterialKey.isUnlit() || drawMaterialKey.isMetallic() || drawMaterialKey.isScattering()) { + return true; + } + + // If the material is using any map, we need to use a material ShapeKey + for (int i = 0; i < graphics::Material::MapChannel::NUM_MAP_CHANNELS; i++) { + if (drawMaterialKey.isMapChannel(graphics::Material::MapChannel(i))) { + return true; + } + } + return false; +} + +ShapeKey ShapeEntityRenderer::getShapeKey() { + if (useMaterialPipeline()) { + graphics::MaterialKey drawMaterialKey; + if (_materials["0"].top().material) { + drawMaterialKey = _materials["0"].top().material->getKey(); + } + + bool isTranslucent = drawMaterialKey.isTranslucent(); + bool hasTangents = drawMaterialKey.isNormalMap(); + bool hasLightmap = drawMaterialKey.isLightmapMap(); + bool isUnlit = drawMaterialKey.isUnlit(); + + ShapeKey::Builder builder; + builder.withMaterial(); + + if (isTranslucent) { + builder.withTranslucent(); + } + if (hasTangents) { + builder.withTangents(); + } + if (hasLightmap) { + builder.withLightmap(); + } + if (isUnlit) { + builder.withUnlit(); + } + + return builder.build(); + } else { + return Parent::getShapeKey(); + } +} + void ShapeEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); Q_ASSERT(args->_batch); @@ -149,7 +234,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { } else { geometryCache->renderShape(batch, geometryShape, outColor); } - } else { + } else if (!useMaterialPipeline()) { // FIXME, support instanced multi-shape rendering using multidraw indirect outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; auto pipeline = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline(); @@ -158,6 +243,11 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { } else { geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline); } + } else { + RenderPipelines::bindMaterial(mat, batch, args->_enableTexturing); + args->_details._materialSwitches++; + + geometryCache->renderShape(batch, geometryShape); } const auto triCount = geometryCache->getShapeTriangleCount(geometryShape); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index de855ce0c6..463ef187fc 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -24,6 +24,10 @@ public: virtual scriptable::ScriptableModelBase getScriptableModel() override; +protected: + ItemKey getKey() override; + ShapeKey getShapeKey() override; + private: virtual bool needsRenderUpdate() const override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; @@ -32,6 +36,8 @@ private: virtual void doRender(RenderArgs* args) override; virtual bool isTransparent() const override; + bool useMaterialPipeline() const; + Procedural _procedural; QString _lastUserData; Transform _renderTransform; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index f7aaa43b7d..bd00ded12d 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -46,6 +46,19 @@ static int YOUTUBE_MAX_FPS = 30; static QTouchDevice _touchDevice; +WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString& urlString) { + if (urlString.isEmpty()) { + return ContentType::NoContent; + } + + const QUrl url(urlString); + if (url.scheme() == "http" || url.scheme() == "https" || + urlString.toLower().endsWith(".htm") || urlString.toLower().endsWith(".html")) { + return ContentType::HtmlContent; + } + return ContentType::QmlContent; +} + WebEntityRenderer::WebEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { static std::once_flag once; std::call_once(once, [&]{ @@ -123,13 +136,45 @@ void WebEntityRenderer::onTimeout() { } void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { - withWriteLock([&] { - // This work must be done on the main thread - if (!hasWebSurface()) { - // If we couldn't create a new web surface, exit - if (!buildWebSurface(entity)) { - return; + // If the content type has changed, or the old content type was QML, we need to + // destroy the existing surface (because surfaces don't support changing the root + // object, so subsequent loads of content just overlap the existing content + bool urlChanged = false; + { + auto newSourceUrl = entity->getSourceUrl(); + auto newContentType = getContentType(newSourceUrl); + auto currentContentType = ContentType::NoContent; + withReadLock([&] { + urlChanged = _lastSourceUrl != newSourceUrl; + currentContentType = _contentType; + }); + + if (urlChanged) { + if (newContentType != ContentType::HtmlContent || currentContentType != ContentType::HtmlContent) { + destroyWebSurface(); } + + withWriteLock([&] { + _lastSourceUrl = newSourceUrl; + _contentType = newContentType; + }); + } + } + + + withWriteLock([&] { + if (_contentType == ContentType::NoContent) { + return; + } + + // This work must be done on the main thread + // If we couldn't create a new web surface, exit + if (!hasWebSurface() && !buildWebSurface(entity)) { + return; + } + + if (urlChanged) { + _webSurface->getRootItem()->setProperty("url", _lastSourceUrl); } if (_contextPosition != entity->getWorldPosition()) { @@ -138,11 +183,6 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(_contextPosition)); } - if (_lastSourceUrl != entity->getSourceUrl()) { - _lastSourceUrl = entity->getSourceUrl(); - loadSourceURL(); - } - _lastDPI = entity->getDPI(); _lastLocked = entity->getLocked(); @@ -199,7 +239,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) { } bool WebEntityRenderer::hasWebSurface() { - return (bool)_webSurface; + return (bool)_webSurface && _webSurface->getRootItem(); } bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) { @@ -232,9 +272,6 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) { // Let us interact with the keyboard surfaceContext->setContextProperty("tabletInterface", DependencyManager::get().data()); }); - _fadeStartTime = usecTimestampNow(); - loadSourceURL(); - _webSurface->resume(); // forward web events to EntityScriptingInterface auto entities = DependencyManager::get(); @@ -243,7 +280,30 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) { emit entities->webEventReceived(entityItemID, message); }); - return true; + if (_contentType == ContentType::HtmlContent) { + // We special case YouTube URLs since we know they are videos that we should play with at least 30 FPS. + // FIXME this doesn't handle redirects or shortened URLs, consider using a signaling method from the + // web entity + if (QUrl(_lastSourceUrl).host().endsWith("youtube.com", Qt::CaseInsensitive)) { + _webSurface->setMaxFps(YOUTUBE_MAX_FPS); + } else { + _webSurface->setMaxFps(DEFAULT_MAX_FPS); + } + _webSurface->load("controls/WebEntityView.qml", [this](QQmlContext* context, QObject* item) { + item->setProperty("url", _lastSourceUrl); + }); + } else if (_contentType == ContentType::QmlContent) { + _webSurface->load(_lastSourceUrl, [this](QQmlContext* context, QObject* item) { + if (item && item->objectName() == "tabletRoot") { + auto tabletScriptingInterface = DependencyManager::get(); + tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); + } + }); + } + _fadeStartTime = usecTimestampNow(); + _webSurface->resume(); + + return _webSurface->getRootItem(); } void WebEntityRenderer::destroyWebSurface() { @@ -289,32 +349,6 @@ glm::vec2 WebEntityRenderer::getWindowSize(const TypedEntityPointer& entity) con return dims; } -void WebEntityRenderer::loadSourceURL() { - const QUrl sourceUrl(_lastSourceUrl); - if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" || - _lastSourceUrl.toLower().endsWith(".htm") || _lastSourceUrl.toLower().endsWith(".html")) { - _contentType = htmlContent; - - // We special case YouTube URLs since we know they are videos that we should play with at least 30 FPS. - if (sourceUrl.host().endsWith("youtube.com", Qt::CaseInsensitive)) { - _webSurface->setMaxFps(YOUTUBE_MAX_FPS); - } else { - _webSurface->setMaxFps(DEFAULT_MAX_FPS); - } - - _webSurface->load("controls/WebEntityView.qml", [this](QQmlContext* context, QObject* item) { - item->setProperty("url", _lastSourceUrl); - }); - } else { - _contentType = qmlContent; - _webSurface->load(_lastSourceUrl); - if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") { - auto tabletScriptingInterface = DependencyManager::get(); - tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); - } - } -} - void WebEntityRenderer::hoverEnterEntity(const PointerEvent& event) { if (!_lastLocked && _webSurface) { PointerEvent webEvent = event; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 309e750f53..3100014e9b 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -47,15 +47,19 @@ private: bool buildWebSurface(const TypedEntityPointer& entity); void destroyWebSurface(); bool hasWebSurface(); - void loadSourceURL(); glm::vec2 getWindowSize(const TypedEntityPointer& entity) const; + int _geometryId{ 0 }; - enum contentType { - htmlContent, - qmlContent + enum class ContentType { + NoContent, + HtmlContent, + QmlContent }; - contentType _contentType; + + static ContentType getContentType(const QString& urlString); + + ContentType _contentType{ ContentType::NoContent }; QSharedPointer _webSurface; glm::vec3 _contextPosition; gpu::TexturePointer _texture; diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 90740948ce..5d7bd61854 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -91,6 +91,11 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, return; } + if (entityTree && entityTree->isServerlessMode()) { + // if we are in a serverless domain, don't send edit packets + return; + } + QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0); if (type == PacketType::EntityAdd) { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 2f1cbfc189..a02c00363e 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -963,7 +963,11 @@ void EntityItem::setHref(QString value) { // If the string has something and doesn't start with with "hifi://" it shouldn't be set // We allow the string to be empty, because that's the initial state of this property - if ( !(value.toLower().startsWith("hifi://")) && !value.isEmpty()) { + if (!value.isEmpty() && + !(value.toLower().startsWith("hifi://")) && + !(value.toLower().startsWith("file://")) + // TODO: serverless-domains will eventually support http and https also + ) { return; } withWriteLock([&] { diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index d348101b66..f9a96d2293 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -451,8 +451,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {Entities.EntityType} type - The entity type. You cannot change the type of an entity after it's created. (Though * its value may switch among "Box", "Shape", and "Sphere" depending on changes to * the shape property set for entities of these types.) Read-only. - * @property {boolean} clientOnly=false - If true then the entity is an avatar entity, otherwise it is a server - * entity. Read-only. + * @property {boolean} clientOnly=false - If true then the entity is an avatar entity; otherwise it is a server + * entity. An avatar entity follows you to each domain you visit, rendering at the same world coordinates unless it's + * parented to your avatar. Value cannot be changed after the entity is created.
+ * The value can also be set at entity creation by using the clientOnly parameter in + * {@link Entities.addEntity}. * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if clientOnly is * true, otherwise {@link Uuid|Uuid.NULL}. Read-only. * @@ -1413,7 +1416,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ANGULAR_VELOCITY, localAngularVelocity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_DIMENSIONS, localDimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); // Gettable but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); // Gettable but not settable except at entity creation COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); // Gettable but not settable // Rendering info @@ -2864,6 +2867,9 @@ void EntityItemProperties::markAllChanged() { _ambientLight.markAllChanged(); _skybox.markAllChanged(); + _keyLightModeChanged = true; + _skyboxModeChanged = true; + _ambientLightModeChanged = true; _hazeModeChanged = true; _animation.markAllChanged(); diff --git a/libraries/entities/src/EntityNodeData.h b/libraries/entities/src/EntityNodeData.h index eb5a1610cc..090a5e0526 100644 --- a/libraries/entities/src/EntityNodeData.h +++ b/libraries/entities/src/EntityNodeData.h @@ -33,7 +33,7 @@ public: // these can only be called from the OctreeSendThread for the given Node void insertSentFilteredEntity(const QUuid& entityID) { _sentFilteredEntities.insert(entityID); } void removeSentFilteredEntity(const QUuid& entityID) { _sentFilteredEntities.remove(entityID); } - bool sentFilteredEntity(const QUuid& entityID) { return _sentFilteredEntities.contains(entityID); } + bool sentFilteredEntity(const QUuid& entityID) const { return _sentFilteredEntities.contains(entityID); } QSet getSentFilteredEntities() { return _sentFilteredEntities; } // the following flagged extra entity methods can only be called from the OctreeSendThread for the given Node diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 266ef41cff..7e15e78624 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -596,7 +596,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { shouldDelete = false; } else { // only delete local entities, server entities will round trip through the server filters - if (entity->getClientOnly()) { + if (entity->getClientOnly() || _entityTree->isServerlessMode()) { _entityTree->deleteEntity(entityID); } } @@ -1285,10 +1285,10 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, } doTransmit = actor(simulation, entity); + _entityTree->entityChanged(entity); if (doTransmit) { properties.setClientOnly(entity->getClientOnly()); properties.setOwningAvatarID(entity->getOwningAvatarID()); - _entityTree->entityChanged(entity); } }); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index b483225390..9613a7a310 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -203,9 +203,9 @@ public slots: * Add a new entity with specified properties. * @function Entities.addEntity * @param {Entities.EntityProperties} properties - The properties of the entity to create. - * @param {boolean} [clientOnly=false] - If true, the entity is created as an avatar entity, otherwise it - * is created on the server. An avatar entity follows you to each domain you visit, rendering at the same world - * coordinates unless it's parented to your avatar. + * @param {boolean} [clientOnly=false] - If true, or if clientOnly is set true in + * the properties, the entity is created as an avatar entity; otherwise it is created on the server. An avatar entity + * follows you to each domain you visit, rendering at the same world coordinates unless it's parented to your avatar. * @returns {Uuid} The ID of the entity if successfully created, otherwise {@link Uuid|Uuid.NULL}. * @example Create a box entity in front of your avatar. * var entityID = Entities.addEntity({ diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 0ed328315b..5b9bab6413 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -495,7 +495,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti if (!properties.getClientOnly() && getIsClient() && !nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() && - !nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified()) { + !nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain) { return nullptr; } @@ -1517,7 +1517,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } if (isAdd && properties.getLocked() && !senderNode->isAllowedEditor()) { - // if a node can't change locks, don't allow them to create an already-locked entity + // if a node can't change locks, don't allow it to create an already-locked entity -- automatically + // clear the locked property and allow the unlocked entity to be created. properties.setLocked(false); bumpTimestamp(properties); } @@ -2189,23 +2190,25 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen localTree->recurseTreeWithOperator(&moveOperator); } - // send add-entity packets to the server - i = map.begin(); - while (i != map.end()) { - EntityItemID newID = i.value(); - EntityItemPointer entity = localTree->findEntityByEntityItemID(newID); - if (entity) { - // queue the packet to send to the server - entity->updateQueryAACube(); - EntityItemProperties properties = entity->getProperties(); - properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity - packetSender->queueEditEntityMessage(PacketType::EntityAdd, localTree, newID, properties); - i++; - } else { - i = map.erase(i); + if (!_serverlessDomain) { + // send add-entity packets to the server + i = map.begin(); + while (i != map.end()) { + EntityItemID newID = i.value(); + EntityItemPointer entity = localTree->findEntityByEntityItemID(newID); + if (entity) { + // queue the packet to send to the server + entity->updateQueryAACube(); + EntityItemProperties properties = entity->getProperties(); + properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity + packetSender->queueEditEntityMessage(PacketType::EntityAdd, localTree, newID, properties); + i++; + } else { + i = map.erase(i); + } } + packetSender->releaseQueuedMessages(); } - packetSender->releaseQueuedMessages(); return map.values().toVector(); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 330717bac0..13c3ed8a41 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -285,6 +285,9 @@ public: void swapStaleProxies(std::vector& proxies) { proxies.swap(_staleProxies); } + void setIsServerlessMode(bool value) { _serverlessDomain = value; } + bool isServerlessMode() const { return _serverlessDomain; } + static void setAddMaterialToEntityOperator(std::function addMaterialToEntityOperator) { _addMaterialToEntityOperator = addMaterialToEntityOperator; } static void setRemoveMaterialFromEntityOperator(std::function removeMaterialFromEntityOperator) { _removeMaterialFromEntityOperator = removeMaterialFromEntityOperator; } static bool addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName); @@ -327,7 +330,7 @@ protected: void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode); bool isScriptInWhitelist(const QString& scriptURL); - + QReadWriteLock _newlyCreatedHooksLock; QVector _newlyCreatedHooks; @@ -416,6 +419,8 @@ private: static std::function _removeMaterialFromOverlayOperator; std::vector _staleProxies; + + bool _serverlessDomain { false }; }; #endif // hifi_EntityTree_h diff --git a/libraries/entities/src/MaterialEntityItem.cpp b/libraries/entities/src/MaterialEntityItem.cpp index 44ef34a3a4..137d6ef396 100644 --- a/libraries/entities/src/MaterialEntityItem.cpp +++ b/libraries/entities/src/MaterialEntityItem.cpp @@ -157,7 +157,7 @@ void MaterialEntityItem::setMaterialURL(const QString& materialURLString, bool u } if (usingUserData) { - _parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(getUserData().toUtf8())); + _parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(getUserData().toUtf8()), materialURLString); // Since our material changed, the current name might not be valid anymore, so we need to update setCurrentMaterialName(_currentMaterialName); diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index 5029b489bc..511f253193 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -161,23 +161,19 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { case QMetaType::QString: { auto bytes = prop.toString().toUtf8(); - out << 'S'; - out << bytes.length(); - out << bytes; + out.device()->write("S", 1); out << (int32_t)bytes.size(); out.writeRawData(bytes, bytes.size()); break; } - case QMetaType::QByteArray: - { - auto bytes = prop.toByteArray(); - out.device()->write("S", 1); - out << (int32_t)bytes.size(); - out.writeRawData(bytes, bytes.size()); - break; - } - + { + auto bytes = prop.toByteArray(); + out.device()->write("S", 1); + out << (int32_t)bytes.size(); + out.writeRawData(bytes, bytes.size()); + break; + } default: { if (prop.canConvert>()) { diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 63fb93ae46..caac08f777 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -643,13 +643,13 @@ done: } -FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url) { +FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url) { PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr); QBuffer buffer { &model }; buffer.open(QIODevice::ReadOnly); - FBXGeometry* geometryPtr = new FBXGeometry(); - FBXGeometry& geometry = *geometryPtr; + auto geometryPtr { std::make_shared() }; + FBXGeometry& geometry { *geometryPtr }; OBJTokenizer tokenizer { &buffer }; float scaleGuess = 1.0f; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index df356fada8..13ddc6e21c 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -85,7 +85,7 @@ public: QString currentMaterialName; QHash materials; - FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); + FBXGeometry::Pointer readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); private: QUrl _url; diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 1bde9e289e..4a2c5fd7f7 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -69,7 +69,7 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) { qFatal("Offscreen surface is invalid"); } #endif - + if (gl::Context::enableDebugLogger()) { _context->makeCurrent(_offscreenSurface); QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this); diff --git a/libraries/gpu-gl-common/CMakeLists.txt b/libraries/gpu-gl-common/CMakeLists.txt new file mode 100644 index 0000000000..2b6f8d4d40 --- /dev/null +++ b/libraries/gpu-gl-common/CMakeLists.txt @@ -0,0 +1,6 @@ +set(TARGET_NAME gpu-gl-common) +setup_hifi_library(Concurrent) +link_hifi_libraries(shared gl gpu) +GroupSources("src") +target_opengl() + diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp similarity index 93% rename from libraries/gpu-gles/src/gpu/gl/GLBackend.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index e8e89a1da6..2089b8a378 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -1,9 +1,9 @@ // // GLBackend.cpp -// libraries/gpu-gl-android/src/gpu/gl +// libraries/gpu/src/gpu // -// Created by Cristian Duarte & Gabriel Calero on 9/21/2016. -// Copyright 2016 High Fidelity, Inc. +// Created by Sam Gateau on 10/27/2014. +// Copyright 2014 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 @@ -16,16 +16,11 @@ #include #include -#include "../gles/GLESBackend.h" - #if defined(NSIGHT_FOUND) #include "nvToolsExt.h" #endif -#include #include -#include -#include #include "GLTexture.h" #include "GLShader.h" @@ -33,39 +28,6 @@ using namespace gpu; using namespace gpu::gl; -static GLBackend* INSTANCE{ nullptr }; - -BackendPointer GLBackend::createBackend() { - // FIXME provide a mechanism to override the backend for testing - // Where the gpuContext is initialized and where the TRUE Backend is created and assigned - //auto version = QOpenGLContextWrapper::currentContextVersion(); - std::shared_ptr result; - - qDebug() << "Using OpenGL ES backend"; - result = std::make_shared(); - - result->initInput(); - result->initTransform(); - result->initTextureManagementStage(); - - INSTANCE = result.get(); - void* voidInstance = &(*result); - qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); - return result; -} - -GLBackend& getBackend() { - if (!INSTANCE) { - INSTANCE = static_cast(qApp->property(hifi::properties::gl::BACKEND).value()); - } - return *INSTANCE; -} - -bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { - return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); -} - - GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = { (&::gpu::gl::GLBackend::do_draw), @@ -93,12 +55,16 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_setUniformBuffer), (&::gpu::gl::GLBackend::do_setResourceBuffer), (&::gpu::gl::GLBackend::do_setResourceTexture), + (&::gpu::gl::GLBackend::do_setResourceFramebufferSwapChainTexture), (&::gpu::gl::GLBackend::do_setFramebuffer), + (&::gpu::gl::GLBackend::do_setFramebufferSwapChain), (&::gpu::gl::GLBackend::do_clearFramebuffer), (&::gpu::gl::GLBackend::do_blit), (&::gpu::gl::GLBackend::do_generateTextureMips), + (&::gpu::gl::GLBackend::do_advance), + (&::gpu::gl::GLBackend::do_beginQuery), (&::gpu::gl::GLBackend::do_endQuery), (&::gpu::gl::GLBackend::do_getQuery), @@ -147,6 +113,9 @@ void GLBackend::init() { qCDebug(gpugllogging) << "\tcard:" << gpu->getName(); qCDebug(gpugllogging) << "\tdriver:" << gpu->getDriver(); qCDebug(gpugllogging) << "\tdedicated memory:" << gpu->getMemory() << "MB"; +#if !defined(USE_GLES) + qCDebug(gpugllogging, "V-Sync is %s\n", (::gl::getSwapInterval() > 0 ? "ON" : "OFF")); +#endif #if THREADED_TEXTURE_BUFFERING // This has to happen on the main thread in order to give the thread // pool a reasonable parent object @@ -224,7 +193,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) { } { // Sync the transform buffers - PROFILE_RANGE(render_gpu_gl_detail, "transferGPUTransform"); + PROFILE_RANGE(render_gpu_gl_detail, "syncGPUTransform"); transferTransformState(batch); } @@ -292,7 +261,7 @@ void GLBackend::render(const Batch& batch) { #ifdef GPU_STEREO_DRAWCALL_INSTANCED if (_stereo.isStereo()) { - glEnable(GL_CLIP_DISTANCE0_EXT); + glEnable(GL_CLIP_DISTANCE0); } #endif { @@ -301,7 +270,7 @@ void GLBackend::render(const Batch& batch) { } #ifdef GPU_STEREO_DRAWCALL_INSTANCED if (_stereo.isStereo()) { - glDisable(GL_CLIP_DISTANCE0_EXT); + glDisable(GL_CLIP_DISTANCE0); } #endif diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h similarity index 99% rename from libraries/gpu-gl/src/gpu/gl/GLBackend.h rename to libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 14a3d202f2..f2e2271552 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -32,9 +32,13 @@ // Different versions for the stereo drawcall // Current preferred is "instanced" which draw the shape twice but instanced and rely on clipping plane to draw left/right side only -//#define GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE +#if defined(USE_GLES) +#define GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE +#else //#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER #define GPU_STEREO_TECHNIQUE_INSTANCED +#endif + // Let these be configured by the one define picked above diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp similarity index 100% rename from libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendOutput.cpp similarity index 97% rename from libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendOutput.cpp index 59e77ebe90..2285b0e486 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendOutput.cpp @@ -17,6 +17,11 @@ using namespace gpu; using namespace gpu::gl; +#if defined(USE_GLES) +#define GL_FRAMEBUFFER_SRGB GL_FRAMEBUFFER_SRGB_EXT +#define glClearDepth glClearDepthf +#endif + void GLBackend::syncOutputStateCache() { GLint currentFBO; glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBO); @@ -88,7 +93,7 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { if (masks & Framebuffer::BUFFER_STENCIL) { glClearStencil(stencil); glmask |= GL_STENCIL_BUFFER_BIT; - + cacheStencilMask = _pipeline._stateCache.stencilActivation.getWriteMaskFront(); if (cacheStencilMask != 0xFF) { restoreStencilMask = true; @@ -182,7 +187,11 @@ void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, co return; } +#if defined(USE_GLES) + GLenum format = GL_RGBA; +#else GLenum format = GL_BGRA; +#endif if (destImage.format() != QImage::Format_ARGB32) { qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : destImage format must be FORMAT_ARGB32 to receive the region of the framebuffer"; return; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendQuery.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp similarity index 97% rename from libraries/gpu-gl/src/gpu/gl/GLBackendQuery.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp index bdea67a99a..b64804fe7c 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendQuery.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp @@ -25,6 +25,7 @@ static bool timeElapsed = false; #endif void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -43,9 +44,11 @@ void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { glquery->_rangeQueryDepth = _queryStage._rangeQueryDepth; (void)CHECK_GL_ERROR(); } +#endif } void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -66,9 +69,11 @@ void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } +#endif } void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -90,6 +95,7 @@ void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } } +#endif } void GLBackend::resetQueryStage() { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp similarity index 63% rename from libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp index 93c9b0d2ff..35dc8a3fbd 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp @@ -14,15 +14,32 @@ using namespace gpu::gl; // GLSL version std::string GLBackend::getBackendShaderHeader() const { - return std::string("#version 410 core"); + +#if defined(USE_GLES) + static const std::string header( +R"SHADER(#version 310 es +#extension GL_EXT_texture_buffer : enable +precision lowp float; // check precision 2 +precision lowp samplerBuffer; +precision lowp sampler2DShadow; +)SHADER"); +#else + static const std::string header( +R"SHADER(#version 410 core +)SHADER"); +#endif + + return header; } + // Shader domain static const size_t NUM_SHADER_DOMAINS = 3; +static_assert(Shader::Type::NUM_DOMAINS == NUM_SHADER_DOMAINS, "GL shader domains must equal defined GPU shader domains"); // GL Shader type enums // Must match the order of type specified in gpu::Shader::Type -static const std::array SHADER_DOMAINS { { +static const std::array SHADER_DOMAINS{ { GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, @@ -30,22 +47,33 @@ static const std::array SHADER_DOMAINS { { // Domain specific defines // Must match the order of type specified in gpu::Shader::Type -static const std::array DOMAIN_DEFINES { { +static const std::array DOMAIN_DEFINES{ { "#define GPU_VERTEX_SHADER", "#define GPU_PIXEL_SHADER", "#define GPU_GEOMETRY_SHADER", } }; // Stereo specific defines -static const std::string stereoVersion { +static const std::string stereoVersion{ #ifdef GPU_STEREO_DRAWCALL_INSTANCED - "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN" +R"SHADER( +#define GPU_TRANSFORM_IS_STEREO +#define GPU_TRANSFORM_STEREO_CAMERA +#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED +#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN +)SHADER" #endif #ifdef GPU_STEREO_DRAWCALL_DOUBLED #ifdef GPU_STEREO_CAMERA_BUFFER - "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED" +R"SHADER( +#define GPU_TRANSFORM_IS_STEREO +#define GPU_TRANSFORM_STEREO_CAMERA +#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED +)SHADER" #else - "#define GPU_TRANSFORM_IS_STEREO" +R"SHADER( +#define GPU_TRANSFORM_IS_STEREO +)SHADER" #endif #endif }; @@ -67,7 +95,10 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co for (int version = 0; version < GLShader::NumVersions; version++) { auto& shaderObject = shaderObjects[version]; - std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version]; + std::string shaderDefines = getBackendShaderHeader() + "\n" + + DOMAIN_DEFINES[shader.getType()] + "\n" + + VERSION_DEFINES[version]; + if (handler) { bool retest = true; std::string currentSrc = shaderSource; @@ -154,149 +185,173 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader:: return object; } + GLBackend::ElementResource GLBackend::getFormatFromGLUniform(GLenum gltype) { switch (gltype) { - case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - /* - case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - */ - case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_FLOAT: + return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC2: + return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC3: + return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC4: + return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); -#if defined(Q_OS_WIN) - case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_INT: + return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC2: + return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC3: + return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC4: + return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); + + case GL_UNSIGNED_INT: + return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC2: + return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC3: + return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC4: + return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); + + case GL_BOOL: + return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC2: + return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC3: + return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC4: + return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); + + case GL_FLOAT_MAT2: + return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT3: + return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT4: + return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + + case GL_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); + case GL_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); + case GL_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); + case GL_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_2D_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); + case GL_SAMPLER_CUBE_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); + case GL_SAMPLER_2D_ARRAY_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_BUFFER: + return ElementResource(Element(SCALAR, gpu::FLOAT, RESOURCE_BUFFER), Resource::BUFFER); + case GL_INT_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); + case GL_INT_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); + case GL_INT_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); + case GL_UNSIGNED_INT_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + +#if !defined(USE_GLES) + case GL_SAMPLER_1D: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); + case GL_SAMPLER_1D_ARRAY: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_INT_SAMPLER_1D: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); + case GL_INT_SAMPLER_1D_ARRAY: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_1D: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); #endif - case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); - - - case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - - /* {GL_FLOAT_MAT2x3 mat2x3}, - {GL_FLOAT_MAT2x4 mat2x4}, - {GL_FLOAT_MAT3x2 mat3x2}, - {GL_FLOAT_MAT3x4 mat3x4}, - {GL_FLOAT_MAT4x2 mat4x2}, - {GL_FLOAT_MAT4x3 mat4x3}, - {GL_DOUBLE_MAT2 dmat2}, - {GL_DOUBLE_MAT3 dmat3}, - {GL_DOUBLE_MAT4 dmat4}, - {GL_DOUBLE_MAT2x3 dmat2x3}, - {GL_DOUBLE_MAT2x4 dmat2x4}, - {GL_DOUBLE_MAT3x2 dmat3x2}, - {GL_DOUBLE_MAT3x4 dmat3x4}, - {GL_DOUBLE_MAT4x2 dmat4x2}, - {GL_DOUBLE_MAT4x3 dmat4x3}, - */ - - case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); - case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); - - case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); - case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); - -#if defined(Q_OS_WIN) - case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); -#endif - - case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); -#if defined(Q_OS_WIN) - case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); - - case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); -#endif - - // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, - // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, - - case GL_SAMPLER_BUFFER: return ElementResource(Element(SCALAR, gpu::FLOAT, RESOURCE_BUFFER), Resource::BUFFER); - - // {GL_SAMPLER_2D_RECT sampler2DRect}, - // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, - -#if defined(Q_OS_WIN) - case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); - case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); - case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - - // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, - // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, - - case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); - case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); - case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); -#endif - // {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, - // {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, - /* - {GL_IMAGE_1D image1D}, - {GL_IMAGE_2D image2D}, - {GL_IMAGE_3D image3D}, - {GL_IMAGE_2D_RECT image2DRect}, - {GL_IMAGE_CUBE imageCube}, - {GL_IMAGE_BUFFER imageBuffer}, - {GL_IMAGE_1D_ARRAY image1DArray}, - {GL_IMAGE_2D_ARRAY image2DArray}, - {GL_IMAGE_2D_MULTISAMPLE image2DMS}, - {GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, - {GL_INT_IMAGE_1D iimage1D}, - {GL_INT_IMAGE_2D iimage2D}, - {GL_INT_IMAGE_3D iimage3D}, - {GL_INT_IMAGE_2D_RECT iimage2DRect}, - {GL_INT_IMAGE_CUBE iimageCube}, - {GL_INT_IMAGE_BUFFER iimageBuffer}, - {GL_INT_IMAGE_1D_ARRAY iimage1DArray}, - {GL_INT_IMAGE_2D_ARRAY iimage2DArray}, - {GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, - {GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, - {GL_UNSIGNED_INT_IMAGE_1D uimage1D}, - {GL_UNSIGNED_INT_IMAGE_2D uimage2D}, - {GL_UNSIGNED_INT_IMAGE_3D uimage3D}, - {GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, - {GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot - - {GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, - {GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, - {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, - {GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} - */ default: return ElementResource(Element(), Resource::BUFFER); } + // Non-covered types + //{GL_FLOAT_MAT2x3 mat2x3}, + //{GL_FLOAT_MAT2x4 mat2x4}, + //{GL_FLOAT_MAT3x2 mat3x2}, + //{GL_FLOAT_MAT3x4 mat3x4}, + //{GL_FLOAT_MAT4x2 mat4x2}, + //{GL_FLOAT_MAT4x3 mat4x3}, + //{GL_DOUBLE_MAT2 dmat2}, + //{GL_DOUBLE_MAT3 dmat3}, + //{GL_DOUBLE_MAT4 dmat4}, + //{GL_DOUBLE_MAT2x3 dmat2x3}, + //{GL_DOUBLE_MAT2x4 dmat2x4}, + //{GL_DOUBLE_MAT3x2 dmat3x2}, + //{GL_DOUBLE_MAT3x4 dmat3x4}, + //{GL_DOUBLE_MAT4x2 dmat4x2}, + //{GL_DOUBLE_MAT4x3 dmat4x3}, + //{GL_SAMPLER_1D_SHADOW sampler1DShadow}, + //{GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, + //{GL_SAMPLER_2D_RECT sampler2DRect}, + //{GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, + //{GL_INT_SAMPLER_BUFFER isamplerBuffer}, + //{GL_INT_SAMPLER_2D_RECT isampler2DRect}, + //{GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, + //{GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, + //{GL_IMAGE_1D image1D}, + //{GL_IMAGE_2D image2D}, + //{GL_IMAGE_3D image3D}, + //{GL_IMAGE_2D_RECT image2DRect}, + //{GL_IMAGE_CUBE imageCube}, + //{GL_IMAGE_BUFFER imageBuffer}, + //{GL_IMAGE_1D_ARRAY image1DArray}, + //{GL_IMAGE_2D_ARRAY image2DArray}, + //{GL_IMAGE_2D_MULTISAMPLE image2DMS}, + //{GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, + //{GL_INT_IMAGE_1D iimage1D}, + //{GL_INT_IMAGE_2D iimage2D}, + //{GL_INT_IMAGE_3D iimage3D}, + //{GL_INT_IMAGE_2D_RECT iimage2DRect}, + //{GL_INT_IMAGE_CUBE iimageCube}, + //{GL_INT_IMAGE_BUFFER iimageBuffer}, + //{GL_INT_IMAGE_1D_ARRAY iimage1DArray}, + //{GL_INT_IMAGE_2D_ARRAY iimage2DArray}, + //{GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, + //{GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, + //{GL_UNSIGNED_INT_IMAGE_1D uimage1D}, + //{GL_UNSIGNED_INT_IMAGE_2D uimage2D}, + //{GL_UNSIGNED_INT_IMAGE_3D uimage3D}, + //{GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, + //{GL_UNSIGNED_INT_IMAGE_CUBE uimageCube}, + //{GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, + //{GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, + //{GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, + //{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, + //{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, + //{GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} + + }; int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, @@ -550,4 +605,3 @@ void GLBackend::makeProgramBindings(ShaderObject& shaderObject) { qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; } } - diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendState.cpp similarity index 97% rename from libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendState.cpp index 24f90395d7..8363af9b00 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendState.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendState.cpp @@ -29,18 +29,19 @@ void GLBackend::resetPipelineState(State::Signature nextSignature) { } } + // Default line width accross the board + glLineWidth(1.0f); +#if !defined(USE_GLES) // force a few states regardless glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Point size is always on - // FIXME CORE //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); glEnable(GL_PROGRAM_POINT_SIZE_EXT); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); - // Default line width accross the board - glLineWidth(1.0f); glEnable(GL_LINE_SMOOTH); +#endif } @@ -48,17 +49,19 @@ void GLBackend::syncPipelineStateCache() { State::Data state; // force a few states regardless - glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); - - // Point size is always on - // FIXME CORE - //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); - glEnable(GL_PROGRAM_POINT_SIZE_EXT); - glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); // Default line width accross the board glLineWidth(1.0f); + +#if !defined(USE_GLES) + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + + // Point size is always on + //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + glEnable(GL_PROGRAM_POINT_SIZE_EXT); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); glEnable(GL_LINE_SMOOTH); +#endif getCurrentGLState(state); State::Signature signature = State::evalSignature(state); @@ -70,11 +73,13 @@ void GLBackend::syncPipelineStateCache() { void GLBackend::do_setStateFillMode(int32 mode) { if (_pipeline._stateCache.fillMode != mode) { +#if !defined(USE_GLES) static GLenum GL_FILL_MODES[] = { GL_POINT, GL_LINE, GL_FILL }; glPolygonMode(GL_FRONT_AND_BACK, GL_FILL_MODES[mode]); (void)CHECK_GL_ERROR(); _pipeline._stateCache.fillMode = State::FillMode(mode); +#endif } } @@ -106,14 +111,15 @@ void GLBackend::do_setStateFrontFaceClockwise(bool isClockwise) { void GLBackend::do_setStateDepthClampEnable(bool enable) { if (_pipeline._stateCache.depthClampEnable != enable) { +#if !defined(USE_GLES) if (enable) { glEnable(GL_DEPTH_CLAMP); } else { glDisable(GL_DEPTH_CLAMP); } (void)CHECK_GL_ERROR(); - _pipeline._stateCache.depthClampEnable = enable; +#endif } } @@ -132,6 +138,7 @@ void GLBackend::do_setStateScissorEnable(bool enable) { void GLBackend::do_setStateMultisampleEnable(bool enable) { if (_pipeline._stateCache.multisampleEnable != enable) { +#if !defined(USE_GLES) if (enable) { glEnable(GL_MULTISAMPLE); } else { @@ -140,11 +147,13 @@ void GLBackend::do_setStateMultisampleEnable(bool enable) { (void)CHECK_GL_ERROR(); _pipeline._stateCache.multisampleEnable = enable; +#endif } } void GLBackend::do_setStateAntialiasedLineEnable(bool enable) { if (_pipeline._stateCache.antialisedLineEnable != enable) { +#if !defined(USE_GLES) if (enable) { glEnable(GL_LINE_SMOOTH); } else { @@ -153,6 +162,7 @@ void GLBackend::do_setStateAntialiasedLineEnable(bool enable) { (void)CHECK_GL_ERROR(); _pipeline._stateCache.antialisedLineEnable = enable; +#endif } } @@ -160,13 +170,17 @@ void GLBackend::do_setStateDepthBias(Vec2 bias) { if ((bias.x != _pipeline._stateCache.depthBias) || (bias.y != _pipeline._stateCache.depthBiasSlopeScale)) { if ((bias.x != 0.0f) || (bias.y != 0.0f)) { glEnable(GL_POLYGON_OFFSET_FILL); +#if !defined(USE_GLES) glEnable(GL_POLYGON_OFFSET_LINE); glEnable(GL_POLYGON_OFFSET_POINT); +#endif glPolygonOffset(bias.x, bias.y); } else { glDisable(GL_POLYGON_OFFSET_FILL); +#if !defined(USE_GLES) glDisable(GL_POLYGON_OFFSET_LINE); glDisable(GL_POLYGON_OFFSET_POINT); +#endif } (void)CHECK_GL_ERROR(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLBackendTexture.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLBuffer.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBuffer.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLBuffer.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLBuffer.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLBuffer.h b/libraries/gpu-gl-common/src/gpu/gl/GLBuffer.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLBuffer.h rename to libraries/gpu-gl-common/src/gpu/gl/GLBuffer.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLFramebuffer.cpp similarity index 96% rename from libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLFramebuffer.cpp index 2ac7e9d060..8d5fb6b2ef 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLFramebuffer.cpp @@ -33,14 +33,18 @@ bool GLFramebuffer::checkStatus() const { case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT."; break; + case GL_FRAMEBUFFER_UNSUPPORTED: + qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; + break; +#if !defined(USE_GLES) case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER."; break; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER."; break; - case GL_FRAMEBUFFER_UNSUPPORTED: - qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; +#endif + default: break; } return false; diff --git a/libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gl-common/src/gpu/gl/GLFramebuffer.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLFramebuffer.h rename to libraries/gpu-gl-common/src/gpu/gl/GLFramebuffer.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLInputFormat.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLInputFormat.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLInputFormat.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLInputFormat.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLInputFormat.h b/libraries/gpu-gl-common/src/gpu/gl/GLInputFormat.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLInputFormat.h rename to libraries/gpu-gl-common/src/gpu/gl/GLInputFormat.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLPipeline.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLPipeline.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLPipeline.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLPipeline.h b/libraries/gpu-gl-common/src/gpu/gl/GLPipeline.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLPipeline.h rename to libraries/gpu-gl-common/src/gpu/gl/GLPipeline.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLQuery.h b/libraries/gpu-gl-common/src/gpu/gl/GLQuery.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLQuery.h rename to libraries/gpu-gl-common/src/gpu/gl/GLQuery.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLShader.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLShader.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLShader.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.h b/libraries/gpu-gl-common/src/gpu/gl/GLShader.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLShader.h rename to libraries/gpu-gl-common/src/gpu/gl/GLShader.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLShared.cpp similarity index 97% rename from libraries/gpu-gl/src/gpu/gl/GLShared.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLShared.cpp index dc1c8036b0..9ba85b8418 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLShared.cpp @@ -24,6 +24,7 @@ namespace gpu { namespace gl { gpu::Size getFreeDedicatedMemory() { Size result { 0 }; +#if !defined(USE_GLES) static bool nvidiaMemorySupported { true }; static bool atiMemorySupported { true }; if (nvidiaMemorySupported) { @@ -45,6 +46,7 @@ gpu::Size getFreeDedicatedMemory() { atiMemorySupported = false; } } +#endif return result; } @@ -144,6 +146,9 @@ State::BlendArg blendArgFromGL(GLenum blendArg) { void getCurrentGLState(State::Data& state) { { +#if defined(USE_GLES) + state.fillMode = State::FILL_FACE; +#else GLint modes[2]; glGetIntegerv(GL_POLYGON_MODE, modes); if (modes[0] == GL_FILL) { @@ -155,6 +160,7 @@ void getCurrentGLState(State::Data& state) { state.fillMode = State::FILL_POINT; } } +#endif } { if (glIsEnabled(GL_CULL_FACE)) { @@ -169,10 +175,16 @@ void getCurrentGLState(State::Data& state) { GLint winding; glGetIntegerv(GL_FRONT_FACE, &winding); state.frontFaceClockwise = (winding == GL_CW); - state.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP); - state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST); +#if defined(USE_GLES) + state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE_EXT); + state.antialisedLineEnable = false; + state.depthClampEnable = false; +#else state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE); state.antialisedLineEnable = glIsEnabled(GL_LINE_SMOOTH); + state.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP); +#endif + state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST); } { if (glIsEnabled(GL_POLYGON_OFFSET_FILL)) { @@ -269,6 +281,7 @@ void getCurrentGLState(State::Data& state) { (void)CHECK_GL_ERROR(); } + void serverWait() { auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); assert(fence); diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.h b/libraries/gpu-gl-common/src/gpu/gl/GLShared.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLShared.h rename to libraries/gpu-gl-common/src/gpu/gl/GLShared.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLState.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLState.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLState.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLState.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLState.h b/libraries/gpu-gl-common/src/gpu/gl/GLState.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLState.h rename to libraries/gpu-gl-common/src/gpu/gl/GLState.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.cpp similarity index 91% rename from libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.cpp index 560b7337c7..99a9afb4c4 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.cpp @@ -11,15 +11,81 @@ using namespace gpu; using namespace gpu::gl; +#if defined(USE_GLES) +// Missing GL formats +#define GL_R16 GL_R16_EXT +#define GL_R16_SNORM GL_R16_SNORM_EXT +#define GL_RG16 GL_RG16_EXT +#define GL_RG16_SNORM GL_RG16_SNORM_EXT +#define GL_RGBA2 GL_RGBA8 +#define GL_RGBA16 GL_RGBA16_EXT +#define GL_RGBA16_SNORM GL_RGBA16_SNORM_EXT +#define GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT32_OES +#define GL_SLUMINANCE8_EXT GL_SLUMINANCE8_NV +// Missing GL compressed formats +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#endif + bool GLTexelFormat::isCompressed() const { switch (internalFormat) { case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + + case GL_COMPRESSED_R11_EAC: + case GL_COMPRESSED_SIGNED_R11_EAC: + case GL_COMPRESSED_RG11_EAC: + case GL_COMPRESSED_SIGNED_RG11_EAC: + case GL_COMPRESSED_RGB8_ETC2: + case GL_COMPRESSED_SRGB8_ETC2: + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_RGBA8_ETC2_EAC: + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: case GL_COMPRESSED_RED_RGTC1: case GL_COMPRESSED_RG_RGTC2: case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: + +#if defined(USE_GLES) + case GL_COMPRESSED_RGBA_ASTC_4x4: + case GL_COMPRESSED_RGBA_ASTC_5x4: + case GL_COMPRESSED_RGBA_ASTC_5x5: + case GL_COMPRESSED_RGBA_ASTC_6x5: + case GL_COMPRESSED_RGBA_ASTC_6x6: + case GL_COMPRESSED_RGBA_ASTC_8x5: + case GL_COMPRESSED_RGBA_ASTC_8x6: + case GL_COMPRESSED_RGBA_ASTC_8x8: + case GL_COMPRESSED_RGBA_ASTC_10x5: + case GL_COMPRESSED_RGBA_ASTC_10x6: + case GL_COMPRESSED_RGBA_ASTC_10x8: + case GL_COMPRESSED_RGBA_ASTC_10x10: + case GL_COMPRESSED_RGBA_ASTC_12x10: + case GL_COMPRESSED_RGBA_ASTC_12x12: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12: +#endif + + return true; default: return false; @@ -390,7 +456,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (srcFormat.getSemantic()) { case gpu::BGRA: case gpu::SBGRA: +#if defined(USE_GLES) + texel.format = GL_RGBA; +#else texel.format = GL_BGRA; +#endif break; case gpu::RGB: case gpu::RGBA: @@ -427,8 +497,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (srcFormat.getSemantic()) { case gpu::BGRA: case gpu::SBGRA: +#if !defined(USE_GLES) texel.format = GL_BGRA; break; +#endif case gpu::RGB: case gpu::RGBA: case gpu::SRGB: @@ -478,6 +550,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; switch (dstFormat.getSemantic()) { + case gpu::RED: case gpu::RGB: case gpu::RGBA: @@ -788,8 +861,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (srcFormat.getSemantic()) { case gpu::BGRA: case gpu::SBGRA: +#if !defined(USE_GLES) texel.format = GL_BGRA; break; +#endif case gpu::RGB: case gpu::RGBA: case gpu::SRGB: diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h b/libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h rename to libraries/gpu-gl-common/src/gpu/gl/GLTexelFormat.h diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLTexture.cpp similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLTexture.cpp rename to libraries/gpu-gl-common/src/gpu/gl/GLTexture.cpp diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h similarity index 100% rename from libraries/gpu-gl/src/gpu/gl/GLTexture.h rename to libraries/gpu-gl-common/src/gpu/gl/GLTexture.h diff --git a/libraries/gpu-gl/CMakeLists.txt b/libraries/gpu-gl/CMakeLists.txt index dc744e73f2..faddab8531 100644 --- a/libraries/gpu-gl/CMakeLists.txt +++ b/libraries/gpu-gl/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME gpu-gl) setup_hifi_library(Concurrent) -link_hifi_libraries(shared gl gpu) +link_hifi_libraries(shared gl gpu gpu-gl-common) if (UNIX) target_link_libraries(${TARGET_NAME} pthread) endif(UNIX) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp deleted file mode 100644 index b4e4656dc7..0000000000 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ /dev/null @@ -1,772 +0,0 @@ -// -// GLBackend.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 10/27/2014. -// Copyright 2014 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 "GLBackend.h" - -#include -#include -#include -#include -#include - -#include "../gl41/GL41Backend.h" -#include "../gl45/GL45Backend.h" - -#if defined(NSIGHT_FOUND) -#include "nvToolsExt.h" -#endif - -#include -#include -#include -#include - -#include "GLTexture.h" -#include "GLShader.h" - -using namespace gpu; -using namespace gpu::gl; - -static const QString DEBUG_FLAG("HIFI_DISABLE_OPENGL_45"); -static bool disableOpenGL45 = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); - -static GLBackend* INSTANCE{ nullptr }; - -BackendPointer GLBackend::createBackend() { - // FIXME provide a mechanism to override the backend for testing - // Where the gpuContext is initialized and where the TRUE Backend is created and assigned - auto version = QOpenGLContextWrapper::currentContextVersion(); - std::shared_ptr result; - if (!disableOpenGL45 && version >= 0x0405) { - qCDebug(gpugllogging) << "Using OpenGL 4.5 backend"; - result = std::make_shared(); - } else { - qCDebug(gpugllogging) << "Using OpenGL 4.1 backend"; - result = std::make_shared(); - } - result->initInput(); - result->initTransform(); - result->initTextureManagementStage(); - - INSTANCE = result.get(); - void* voidInstance = &(*result); - qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); - return result; -} - -GLBackend& getBackend() { - if (!INSTANCE) { - INSTANCE = static_cast(qApp->property(hifi::properties::gl::BACKEND).value()); - } - return *INSTANCE; -} - -bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { - return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); -} - -GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = -{ - (&::gpu::gl::GLBackend::do_draw), - (&::gpu::gl::GLBackend::do_drawIndexed), - (&::gpu::gl::GLBackend::do_drawInstanced), - (&::gpu::gl::GLBackend::do_drawIndexedInstanced), - (&::gpu::gl::GLBackend::do_multiDrawIndirect), - (&::gpu::gl::GLBackend::do_multiDrawIndexedIndirect), - - (&::gpu::gl::GLBackend::do_setInputFormat), - (&::gpu::gl::GLBackend::do_setInputBuffer), - (&::gpu::gl::GLBackend::do_setIndexBuffer), - (&::gpu::gl::GLBackend::do_setIndirectBuffer), - - (&::gpu::gl::GLBackend::do_setModelTransform), - (&::gpu::gl::GLBackend::do_setViewTransform), - (&::gpu::gl::GLBackend::do_setProjectionTransform), - (&::gpu::gl::GLBackend::do_setViewportTransform), - (&::gpu::gl::GLBackend::do_setDepthRangeTransform), - - (&::gpu::gl::GLBackend::do_setPipeline), - (&::gpu::gl::GLBackend::do_setStateBlendFactor), - (&::gpu::gl::GLBackend::do_setStateScissorRect), - - (&::gpu::gl::GLBackend::do_setUniformBuffer), - (&::gpu::gl::GLBackend::do_setResourceBuffer), - (&::gpu::gl::GLBackend::do_setResourceTexture), - (&::gpu::gl::GLBackend::do_setResourceFramebufferSwapChainTexture), - - (&::gpu::gl::GLBackend::do_setFramebuffer), - (&::gpu::gl::GLBackend::do_setFramebufferSwapChain), - (&::gpu::gl::GLBackend::do_clearFramebuffer), - (&::gpu::gl::GLBackend::do_blit), - (&::gpu::gl::GLBackend::do_generateTextureMips), - - (&::gpu::gl::GLBackend::do_advance), - - (&::gpu::gl::GLBackend::do_beginQuery), - (&::gpu::gl::GLBackend::do_endQuery), - (&::gpu::gl::GLBackend::do_getQuery), - - (&::gpu::gl::GLBackend::do_resetStages), - - (&::gpu::gl::GLBackend::do_disableContextViewCorrection), - (&::gpu::gl::GLBackend::do_restoreContextViewCorrection), - (&::gpu::gl::GLBackend::do_disableContextStereo), - (&::gpu::gl::GLBackend::do_restoreContextStereo), - - (&::gpu::gl::GLBackend::do_runLambda), - - (&::gpu::gl::GLBackend::do_startNamedCall), - (&::gpu::gl::GLBackend::do_stopNamedCall), - - (&::gpu::gl::GLBackend::do_glUniform1i), - (&::gpu::gl::GLBackend::do_glUniform1f), - (&::gpu::gl::GLBackend::do_glUniform2f), - (&::gpu::gl::GLBackend::do_glUniform3f), - (&::gpu::gl::GLBackend::do_glUniform4f), - (&::gpu::gl::GLBackend::do_glUniform3fv), - (&::gpu::gl::GLBackend::do_glUniform4fv), - (&::gpu::gl::GLBackend::do_glUniform4iv), - (&::gpu::gl::GLBackend::do_glUniformMatrix3fv), - (&::gpu::gl::GLBackend::do_glUniformMatrix4fv), - - (&::gpu::gl::GLBackend::do_glColor4f), - - (&::gpu::gl::GLBackend::do_pushProfileRange), - (&::gpu::gl::GLBackend::do_popProfileRange), -}; - -void GLBackend::init() { - static std::once_flag once; - std::call_once(once, [] { - QString vendor{ (const char*)glGetString(GL_VENDOR) }; - QString renderer{ (const char*)glGetString(GL_RENDERER) }; - qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); - qCDebug(gpugllogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); - qCDebug(gpugllogging) << "GL Vendor: " << vendor; - qCDebug(gpugllogging) << "GL Renderer: " << renderer; - GPUIdent* gpu = GPUIdent::getInstance(vendor, renderer); - // From here on, GPUIdent::getInstance()->getMumble() should efficiently give the same answers. - qCDebug(gpugllogging) << "GPU:"; - qCDebug(gpugllogging) << "\tcard:" << gpu->getName(); - qCDebug(gpugllogging) << "\tdriver:" << gpu->getDriver(); - qCDebug(gpugllogging) << "\tdedicated memory:" << gpu->getMemory() << "MB"; - qCDebug(gpugllogging, "V-Sync is %s\n", (::gl::getSwapInterval() > 0 ? "ON" : "OFF")); -#if THREADED_TEXTURE_BUFFERING - // This has to happen on the main thread in order to give the thread - // pool a reasonable parent object - GLVariableAllocationSupport::TransferJob::startBufferingThread(); -#endif - }); -} - -GLBackend::GLBackend() { - _pipeline._cameraCorrectionBuffer._buffer->flush(); - glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment); -} - - -GLBackend::~GLBackend() { - killInput(); - killTransform(); -} - -void GLBackend::renderPassTransfer(const Batch& batch) { - const size_t numCommands = batch.getCommands().size(); - const Batch::Commands::value_type* command = batch.getCommands().data(); - const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); - - _inRenderTransferPass = true; - { // Sync all the buffers - PROFILE_RANGE(render_gpu_gl_detail, "syncGPUBuffer"); - - for (auto& cached : batch._buffers._items) { - if (cached._data) { - syncGPUObject(*cached._data); - } - } - } - - { // Sync all the transform states - PROFILE_RANGE(render_gpu_gl_detail, "syncCPUTransform"); - _transform._cameras.clear(); - _transform._cameraOffsets.clear(); - - for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { - switch (*command) { - case Batch::COMMAND_draw: - case Batch::COMMAND_drawIndexed: - case Batch::COMMAND_drawInstanced: - case Batch::COMMAND_drawIndexedInstanced: - case Batch::COMMAND_multiDrawIndirect: - case Batch::COMMAND_multiDrawIndexedIndirect: - _transform.preUpdate(_commandIndex, _stereo); - break; - - case Batch::COMMAND_disableContextStereo: - _stereo._contextDisable = true; - break; - - case Batch::COMMAND_restoreContextStereo: - _stereo._contextDisable = false; - break; - - case Batch::COMMAND_setViewportTransform: - case Batch::COMMAND_setViewTransform: - case Batch::COMMAND_setProjectionTransform: { - CommandCall call = _commandCalls[(*command)]; - (this->*(call))(batch, *offset); - break; - } - - default: - break; - } - command++; - offset++; - } - } - - { // Sync the transform buffers - PROFILE_RANGE(render_gpu_gl_detail, "syncGPUTransform"); - transferTransformState(batch); - } - - _inRenderTransferPass = false; -} - -void GLBackend::renderPassDraw(const Batch& batch) { - _currentDraw = -1; - _transform._camerasItr = _transform._cameraOffsets.begin(); - const size_t numCommands = batch.getCommands().size(); - const Batch::Commands::value_type* command = batch.getCommands().data(); - const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); - for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { - switch (*command) { - // Ignore these commands on this pass, taken care of in the transfer pass - // Note we allow COMMAND_setViewportTransform to occur in both passes - // as it both updates the transform object (and thus the uniforms in the - // UBO) as well as executes the actual viewport call - case Batch::COMMAND_setModelTransform: - case Batch::COMMAND_setViewTransform: - case Batch::COMMAND_setProjectionTransform: - break; - - case Batch::COMMAND_draw: - case Batch::COMMAND_drawIndexed: - case Batch::COMMAND_drawInstanced: - case Batch::COMMAND_drawIndexedInstanced: - case Batch::COMMAND_multiDrawIndirect: - case Batch::COMMAND_multiDrawIndexedIndirect: { - // updates for draw calls - ++_currentDraw; - updateInput(); - updateTransform(batch); - updatePipeline(); - - CommandCall call = _commandCalls[(*command)]; - (this->*(call))(batch, *offset); - break; - } - default: { - CommandCall call = _commandCalls[(*command)]; - (this->*(call))(batch, *offset); - break; - } - } - - command++; - offset++; - } -} - -void GLBackend::render(const Batch& batch) { - _transform._skybox = _stereo._skybox = batch.isSkyboxEnabled(); - // Allow the batch to override the rendering stereo settings - // for things like full framebuffer copy operations (deferred lighting passes) - bool savedStereo = _stereo._enable; - if (!batch.isStereoEnabled()) { - _stereo._enable = false; - } - - { - PROFILE_RANGE(render_gpu_gl_detail, "Transfer"); - renderPassTransfer(batch); - } - -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - if (_stereo.isStereo()) { - glEnable(GL_CLIP_DISTANCE0); - } -#endif - { - PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render"); - renderPassDraw(batch); - } -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - if (_stereo.isStereo()) { - glDisable(GL_CLIP_DISTANCE0); - } -#endif - // Restore the saved stereo state for the next batch - _stereo._enable = savedStereo; -} - - -void GLBackend::syncCache() { - PROFILE_RANGE(render_gpu_gl_detail, __FUNCTION__); - - syncTransformStateCache(); - syncPipelineStateCache(); - syncInputStateCache(); - syncOutputStateCache(); -} - -#ifdef GPU_STEREO_DRAWCALL_DOUBLED -void GLBackend::setupStereoSide(int side) { - ivec4 vp = _transform._viewport; - vp.z /= 2; - glViewport(vp.x + side * vp.z, vp.y, vp.z, vp.w); - - -#ifdef GPU_STEREO_CAMERA_BUFFER -#ifdef GPU_STEREO_DRAWCALL_DOUBLED - glVertexAttribI1i(14, side); -#endif -#else - _transform.bindCurrentCamera(side); -#endif - -} -#else -#endif - -void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) { - resetStages(); -} - -void GLBackend::do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) { - _transform._viewCorrectionEnabled = false; -} - -void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) { - _transform._viewCorrectionEnabled = true; -} - -void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) { - -} - -void GLBackend::do_restoreContextStereo(const Batch& batch, size_t paramOffset) { - -} - -void GLBackend::do_runLambda(const Batch& batch, size_t paramOffset) { - std::function f = batch._lambdas.get(batch._params[paramOffset]._uint); - f(); -} - -void GLBackend::do_startNamedCall(const Batch& batch, size_t paramOffset) { - batch._currentNamedCall = batch._names.get(batch._params[paramOffset]._uint); - _currentDraw = -1; -} - -void GLBackend::do_stopNamedCall(const Batch& batch, size_t paramOffset) { - batch._currentNamedCall.clear(); -} - -void GLBackend::resetStages() { - resetInputStage(); - resetPipelineStage(); - resetTransformStage(); - resetUniformStage(); - resetResourceStage(); - resetOutputStage(); - resetQueryStage(); - - (void) CHECK_GL_ERROR(); -} - - -void GLBackend::do_pushProfileRange(const Batch& batch, size_t paramOffset) { - if (trace_render_gpu_gl_detail().isDebugEnabled()) { - auto name = batch._profileRanges.get(batch._params[paramOffset]._uint); - profileRanges.push_back(name); -#if defined(NSIGHT_FOUND) - nvtxRangePush(name.c_str()); -#endif - } -} - -void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) { - if (trace_render_gpu_gl_detail().isDebugEnabled()) { - profileRanges.pop_back(); -#if defined(NSIGHT_FOUND) - nvtxRangePop(); -#endif - } -} - -// TODO: As long as we have gl calls explicitely issued from interface -// code, we need to be able to record and batch these calls. THe long -// term strategy is to get rid of any GL calls in favor of the HIFI GPU API - -// As long as we don;t use several versions of shaders we can avoid this more complex code path -#ifdef GPU_STEREO_CAMERA_BUFFER -#define GET_UNIFORM_LOCATION(shaderUniformLoc) ((_pipeline._programShader) ? _pipeline._programShader->getUniformLocation(shaderUniformLoc, (GLShader::Version) isStereo()) : -1) -#else -#define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc -#endif - -void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) { - if (_pipeline._program == 0) { - // We should call updatePipeline() to bind the program but we are not doing that - // because these uniform setters are deprecated and we don;t want to create side effect - return; - } - updatePipeline(); - - glUniform1i( - GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int), - batch._params[paramOffset + 0]._int); - (void)CHECK_GL_ERROR(); -} - -void GLBackend::do_glUniform1f(const Batch& batch, size_t paramOffset) { - if (_pipeline._program == 0) { - // We should call updatePipeline() to bind the program but we are not doing that - // because these uniform setters are deprecated and we don;t want to create side effect - return; - } - updatePipeline(); - - glUniform1f( - GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int), - batch._params[paramOffset + 0]._float); - (void)CHECK_GL_ERROR(); -} - -void GLBackend::do_glUniform2f(const Batch& batch, size_t paramOffset) { - if (_pipeline._program == 0) { - // We should call updatePipeline() to bind the program but we are not doing that - // because these uniform setters are deprecated and we don;t want to create side effect - return; - } - updatePipeline(); - glUniform2f( - GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int), - batch._params[paramOffset + 1]._float, - batch._params[paramOffset + 0]._float); - (void)CHECK_GL_ERROR(); -} - -void GLBackend::do_glUniform3f(const Batch& batch, size_t paramOffset) { - if (_pipeline._program == 0) { - // We should call updatePipeline() to bind the program but we are not doing that - // because these uniform setters are deprecated and we don;t want to create side effect - return; - } - updatePipeline(); - glUniform3f( - GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int), - batch._params[paramOffset + 2]._float, - batch._params[paramOffset + 1]._float, - batch._params[paramOffset + 0]._float); - (void)CHECK_GL_ERROR(); -} - -void GLBackend::do_glUniform4f(const Batch& batch, size_t paramOffset) { - if (_pipeline._program == 0) { - // We should call updatePipeline() to bind the program but we are not doing that - // because these uniform setters are deprecated and we don;t want to create side effect - return; - } - updatePipeline(); - glUniform4f( - GET_UNIFORM_LOCATION(batch._params[paramOffset + 4]._int), - batch._params[paramOffset + 3]._float, - batch._params[paramOffset + 2]._float, - batch._params[paramOffset + 1]._float, - batch._params[paramOffset + 0]._float); - (void)CHECK_GL_ERROR(); -} - -void GLBackend::do_glUniform3fv(const Batch& batch, size_t paramOffset) { - if (_pipeline._program == 0) { - // We should call updatePipeline() to bind the program but we are not doing that - // because these uniform setters are deprecated and we don;t want to create side effect - return; - } - updatePipeline(); - glUniform3fv( - GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int), - batch._params[paramOffset + 1]._uint, - (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); - - (void)CHECK_GL_ERROR(); -} - -void GLBackend::do_glUniform4fv(const Batch& batch, size_t paramOffset) { - if (_pipeline._program == 0) { - // We should call updatePipeline() to bind the program but we are not doing that - // because these uniform setters are deprecated and we don;t want to create side effect - return; - } - updatePipeline(); - - GLint location = GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int); - GLsizei count = batch._params[paramOffset + 1]._uint; - const GLfloat* value = (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint); - glUniform4fv(location, count, value); - - (void)CHECK_GL_ERROR(); -} - -void GLBackend::do_glUniform4iv(const Batch& batch, size_t paramOffset) { - if (_pipeline._program == 0) { - // We should call updatePipeline() to bind the program but we are not doing that - // because these uniform setters are deprecated and we don;t want to create side effect - return; - } - updatePipeline(); - glUniform4iv( - GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int), - batch._params[paramOffset + 1]._uint, - (const GLint*)batch.readData(batch._params[paramOffset + 0]._uint)); - - (void)CHECK_GL_ERROR(); -} - -void GLBackend::do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) { - if (_pipeline._program == 0) { - // We should call updatePipeline() to bind the program but we are not doing that - // because these uniform setters are deprecated and we don;t want to create side effect - return; - } - updatePipeline(); - - glUniformMatrix3fv( - GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int), - batch._params[paramOffset + 2]._uint, - batch._params[paramOffset + 1]._uint, - (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); - (void)CHECK_GL_ERROR(); -} - -void GLBackend::do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) { - if (_pipeline._program == 0) { - // We should call updatePipeline() to bind the program but we are not doing that - // because these uniform setters are deprecated and we don;t want to create side effect - return; - } - updatePipeline(); - - glUniformMatrix4fv( - GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int), - batch._params[paramOffset + 2]._uint, - batch._params[paramOffset + 1]._uint, - (const GLfloat*)batch.readData(batch._params[paramOffset + 0]._uint)); - (void)CHECK_GL_ERROR(); -} - -void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) { - - glm::vec4 newColor( - batch._params[paramOffset + 3]._float, - batch._params[paramOffset + 2]._float, - batch._params[paramOffset + 1]._float, - batch._params[paramOffset + 0]._float); - - if (_input._colorAttribute != newColor) { - _input._colorAttribute = newColor; - glVertexAttrib4fv(gpu::Stream::COLOR, &_input._colorAttribute.r); - // Color has been changed and is not white. To prevent colors from bleeding - // between different objects, we need to set the _hadColorAttribute flag - // as if a previous render call had potential colors - _input._hadColorAttribute = (newColor != glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); - } - (void)CHECK_GL_ERROR(); -} - -void GLBackend::releaseBuffer(GLuint id, Size size) const { - Lock lock(_trashMutex); - _buffersTrash.push_back({ id, size }); -} - -void GLBackend::releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const { - Lock lock(_trashMutex); - _externalTexturesTrash.push_back({ id, recycler }); -} - -void GLBackend::releaseTexture(GLuint id, Size size) const { - Lock lock(_trashMutex); - _texturesTrash.push_back({ id, size }); -} - -void GLBackend::releaseFramebuffer(GLuint id) const { - Lock lock(_trashMutex); - _framebuffersTrash.push_back(id); -} - -void GLBackend::releaseShader(GLuint id) const { - Lock lock(_trashMutex); - _shadersTrash.push_back(id); -} - -void GLBackend::releaseProgram(GLuint id) const { - Lock lock(_trashMutex); - _programsTrash.push_back(id); -} - -void GLBackend::releaseQuery(GLuint id) const { - Lock lock(_trashMutex); - _queriesTrash.push_back(id); -} - -void GLBackend::queueLambda(const std::function lambda) const { - Lock lock(_trashMutex); - _lambdaQueue.push_back(lambda); -} - -void GLBackend::recycle() const { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__) - { - std::list> lamdbasTrash; - { - Lock lock(_trashMutex); - std::swap(_lambdaQueue, lamdbasTrash); - } - for (auto lambda : lamdbasTrash) { - lambda(); - } - } - - { - std::vector ids; - std::list> buffersTrash; - { - Lock lock(_trashMutex); - std::swap(_buffersTrash, buffersTrash); - } - ids.reserve(buffersTrash.size()); - for (auto pair : buffersTrash) { - ids.push_back(pair.first); - } - if (!ids.empty()) { - glDeleteBuffers((GLsizei)ids.size(), ids.data()); - } - } - - { - std::vector ids; - std::list framebuffersTrash; - { - Lock lock(_trashMutex); - std::swap(_framebuffersTrash, framebuffersTrash); - } - ids.reserve(framebuffersTrash.size()); - for (auto id : framebuffersTrash) { - ids.push_back(id); - } - if (!ids.empty()) { - glDeleteFramebuffers((GLsizei)ids.size(), ids.data()); - } - } - - { - std::vector ids; - std::list> texturesTrash; - { - Lock lock(_trashMutex); - std::swap(_texturesTrash, texturesTrash); - } - ids.reserve(texturesTrash.size()); - for (auto pair : texturesTrash) { - ids.push_back(pair.first); - } - if (!ids.empty()) { - glDeleteTextures((GLsizei)ids.size(), ids.data()); - } - } - - { - std::list> externalTexturesTrash; - { - Lock lock(_trashMutex); - std::swap(_externalTexturesTrash, externalTexturesTrash); - } - if (!externalTexturesTrash.empty()) { - std::vector fences; - fences.resize(externalTexturesTrash.size()); - for (size_t i = 0; i < externalTexturesTrash.size(); ++i) { - fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - } - // External texture fences will be read in another thread/context, so we need a flush - glFlush(); - size_t index = 0; - for (auto pair : externalTexturesTrash) { - auto fence = fences[index++]; - pair.second(pair.first, fence); - } - } - } - - { - std::list programsTrash; - { - Lock lock(_trashMutex); - std::swap(_programsTrash, programsTrash); - } - for (auto id : programsTrash) { - glDeleteProgram(id); - } - } - - { - std::list shadersTrash; - { - Lock lock(_trashMutex); - std::swap(_shadersTrash, shadersTrash); - } - for (auto id : shadersTrash) { - glDeleteShader(id); - } - } - - { - std::vector ids; - std::list queriesTrash; - { - Lock lock(_trashMutex); - std::swap(_queriesTrash, queriesTrash); - } - ids.reserve(queriesTrash.size()); - for (auto id : queriesTrash) { - ids.push_back(id); - } - if (!ids.empty()) { - glDeleteQueries((GLsizei)ids.size(), ids.data()); - } - } - - GLVariableAllocationSupport::manageMemory(); - GLVariableAllocationSupport::_frameTexturesCreated = 0; - Texture::KtxStorage::releaseOpenKtxFiles(); -} - -void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset) { - auto invCorrection = glm::inverse(correction); - auto invPrevView = glm::inverse(prevRenderView); - _transform._correction.prevView = (reset ? Mat4() : prevRenderView); - _transform._correction.prevViewInverse = (reset ? Mat4() : invPrevView); - _transform._correction.correction = correction; - _transform._correction.correctionInverse = invCorrection; - _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); - _pipeline._cameraCorrectionBuffer._buffer->flush(); -} diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp deleted file mode 100644 index cac214b01e..0000000000 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// -// GLBackendInput.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 3/8/2015. -// Copyright 2014 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 "GLBackend.h" -#include "GLShared.h" -#include "GLInputFormat.h" - -using namespace gpu; -using namespace gpu::gl; - -void GLBackend::do_setInputFormat(const Batch& batch, size_t paramOffset) { - Stream::FormatPointer format = batch._streamFormats.get(batch._params[paramOffset]._uint); - if (format != _input._format) { - _input._format = format; - if (format) { - auto inputFormat = GLInputFormat::sync((*format)); - assert(inputFormat); - if (_input._formatKey != inputFormat->key) { - _input._formatKey = inputFormat->key; - _input._invalidFormat = true; - } - } else { - _input._formatKey.clear(); - _input._invalidFormat = true; - } - } -} - -void GLBackend::do_setInputBuffer(const Batch& batch, size_t paramOffset) { - Offset stride = batch._params[paramOffset + 0]._uint; - Offset offset = batch._params[paramOffset + 1]._uint; - BufferPointer buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); - uint32 channel = batch._params[paramOffset + 3]._uint; - - if (channel < getNumInputBuffers()) { - bool isModified = false; - if (_input._buffers[channel] != buffer) { - _input._buffers[channel] = buffer; - - GLuint vbo = 0; - if (buffer) { - vbo = getBufferID((*buffer)); - } - _input._bufferVBOs[channel] = vbo; - - isModified = true; - } - - if (_input._bufferOffsets[channel] != offset) { - _input._bufferOffsets[channel] = offset; - isModified = true; - } - - if (_input._bufferStrides[channel] != stride) { - _input._bufferStrides[channel] = stride; - isModified = true; - } - - if (isModified) { - _input._invalidBuffers.set(channel); - } - } -} - -void GLBackend::initInput() { - if(!_input._defaultVAO) { - glGenVertexArrays(1, &_input._defaultVAO); - } - glBindVertexArray(_input._defaultVAO); - (void) CHECK_GL_ERROR(); -} - -void GLBackend::killInput() { - glBindVertexArray(0); - if(_input._defaultVAO) { - glDeleteVertexArrays(1, &_input._defaultVAO); - } - (void) CHECK_GL_ERROR(); -} - -void GLBackend::syncInputStateCache() { - for (uint32_t i = 0; i < _input._attributeActivation.size(); i++) { - GLint active = 0; - glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &active); - _input._attributeActivation[i] = active; - } - //_input._defaultVAO - glBindVertexArray(_input._defaultVAO); -} - -void GLBackend::resetInputStage() { - // Reset index buffer - _input._indexBufferType = UINT32; - _input._indexBufferOffset = 0; - _input._indexBuffer.reset(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - (void) CHECK_GL_ERROR(); - - // Reset vertex buffer and format - _input._format.reset(); - _input._formatKey.clear(); - _input._invalidFormat = false; - _input._attributeActivation.reset(); - - for (uint32_t i = 0; i < _input._buffers.size(); i++) { - _input._buffers[i].reset(); - _input._bufferOffsets[i] = 0; - _input._bufferStrides[i] = 0; - _input._bufferVBOs[i] = 0; - } - _input._invalidBuffers.reset(); - - // THe vertex array binding MUST be reset in the specific Backend versions as they use different techniques -} - -void GLBackend::do_setIndexBuffer(const Batch& batch, size_t paramOffset) { - _input._indexBufferType = (Type)batch._params[paramOffset + 2]._uint; - _input._indexBufferOffset = batch._params[paramOffset + 0]._uint; - - BufferPointer indexBuffer = batch._buffers.get(batch._params[paramOffset + 1]._uint); - if (indexBuffer != _input._indexBuffer) { - _input._indexBuffer = indexBuffer; - if (indexBuffer) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getBufferID(*indexBuffer)); - } else { - // FIXME do we really need this? Is there ever a draw call where we care that the element buffer is null? - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - } - (void) CHECK_GL_ERROR(); -} - -void GLBackend::do_setIndirectBuffer(const Batch& batch, size_t paramOffset) { - _input._indirectBufferOffset = batch._params[paramOffset + 1]._uint; - _input._indirectBufferStride = batch._params[paramOffset + 2]._uint; - - BufferPointer buffer = batch._buffers.get(batch._params[paramOffset]._uint); - if (buffer != _input._indirectBuffer) { - _input._indirectBuffer = buffer; - if (buffer) { - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, getBufferID(*buffer)); - } else { - // FIXME do we really need this? Is there ever a draw call where we care that the element buffer is null? - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); - } - } - - (void)CHECK_GL_ERROR(); -} - diff --git a/libraries/gpu-gl/src/gpu/gl/GLDesktopBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLDesktopBackend.cpp new file mode 100644 index 0000000000..72a76f8f90 --- /dev/null +++ b/libraries/gpu-gl/src/gpu/gl/GLDesktopBackend.cpp @@ -0,0 +1,63 @@ +// +// GLBackend.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 10/27/2014. +// Copyright 2014 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 + +#include + +#include + +#include +#include +#include + +#include "../gl41/GL41Backend.h" +#include "../gl45/GL45Backend.h" + +using namespace gpu; +using namespace gpu::gl; + +static const QString DEBUG_FLAG("HIFI_DISABLE_OPENGL_45"); +static bool disableOpenGL45 = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); + +static GLBackend* INSTANCE{ nullptr }; + +BackendPointer GLBackend::createBackend() { + // FIXME provide a mechanism to override the backend for testing + // Where the gpuContext is initialized and where the TRUE Backend is created and assigned + auto version = QOpenGLContextWrapper::currentContextVersion(); + std::shared_ptr result; + if (!disableOpenGL45 && version >= 0x0405) { + qCDebug(gpugllogging) << "Using OpenGL 4.5 backend"; + result = std::make_shared(); + } else { + qCDebug(gpugllogging) << "Using OpenGL 4.1 backend"; + result = std::make_shared(); + } + result->initInput(); + result->initTransform(); + result->initTextureManagementStage(); + + INSTANCE = result.get(); + void* voidInstance = &(*result); + qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); + return result; +} + +GLBackend& getBackend() { + if (!INSTANCE) { + INSTANCE = static_cast(qApp->property(hifi::properties::gl::BACKEND).value()); + } + return *INSTANCE; +} + +bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { + return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); +} diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 42926fdb1c..3c59781f78 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -13,8 +13,8 @@ #include -#include "../gl/GLBackend.h" -#include "../gl/GLTexture.h" +#include +#include #define GPU_CORE_41 410 #define GPU_CORE_43 430 diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp index eb66cc4544..62ade673b4 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL41Backend.h" -#include "../gl/GLBuffer.h" +#include namespace gpu { namespace gl41 { diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp index 195b155bf3..a5ef2d92e1 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendOutput.cpp @@ -12,8 +12,8 @@ #include -#include "../gl/GLFramebuffer.h" -#include "../gl/GLTexture.h" +#include +#include namespace gpu { namespace gl41 { diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp index f712550973..0f75c29f38 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendQuery.cpp @@ -10,7 +10,7 @@ // #include "GL41Backend.h" -#include "../gl/GLQuery.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp index ff9ddaae63..35bfafdc50 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL41Backend.h" -#include "../gl/GLShader.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 2834a8463c..61c3da391b 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -13,7 +13,7 @@ #include #include -#include "../gl/GLTexelFormat.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 1a4b63d35f..b90c0b1857 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -12,8 +12,9 @@ #ifndef hifi_gpu_45_GL45Backend_h #define hifi_gpu_45_GL45Backend_h -#include "../gl/GLBackend.h" -#include "../gl/GLTexture.h" +#include +#include + #include #define INCREMENTAL_TRANSFER 0 diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp index b4a6410612..2afbea3876 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL45Backend.h" -#include "../gl/GLBuffer.h" +#include namespace gpu { namespace gl45 { using namespace gpu::gl; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp index 4a43fc988c..34bf6774f7 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL45Backend.h" -#include "../gl/GLShared.h" +#include using namespace gpu; using namespace gpu::gl45; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp index 9648af9b21..ca53d6c624 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL45Backend.h" -#include "../gl/GLFramebuffer.h" -#include "../gl/GLTexture.h" +#include +#include #include diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp index df81d7914e..62f87ed913 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendQuery.cpp @@ -10,7 +10,7 @@ // #include "GL45Backend.h" -#include "../gl/GLQuery.h" +#include namespace gpu { namespace gl45 { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp index 80860edcbd..6bdc22bf07 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GL45Backend.h" -#include "../gl/GLShader.h" +#include //#include using namespace gpu; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 1d415c7ca3..5e64e1845e 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -20,7 +20,7 @@ #include #include -#include "../gl/GLTexelFormat.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 0f1de0f868..d2d7b2bf55 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -21,7 +21,7 @@ #include #include -#include "../gl/GLTexelFormat.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gles/CMakeLists.txt b/libraries/gpu-gles/CMakeLists.txt index ea69919f6d..82bf670781 100644 --- a/libraries/gpu-gles/CMakeLists.txt +++ b/libraries/gpu-gles/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME gpu-gles) setup_hifi_library(Gui Concurrent) -link_hifi_libraries(shared gl gpu) +link_hifi_libraries(shared gl gpu gpu-gl-common) GroupSources("src") target_opengl() diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.h b/libraries/gpu-gles/src/gpu/gl/GLBackend.h deleted file mode 100644 index 33bec07854..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.h +++ /dev/null @@ -1,476 +0,0 @@ -// -// Created by Cristian Duarte & Gabriel Calero on 09/21/2016 -// Copyright 2016 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_gpu_gles_Backend_h -#define hifi_gpu_gles_Backend_h - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include - -#include "GLShared.h" - - -// Different versions for the stereo drawcall -// Current preferred is "instanced" which draw the shape twice but instanced and rely on clipping plane to draw left/right side only -#define GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE -//#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER -//#define GPU_STEREO_TECHNIQUE_INSTANCED - - -// Let these be configured by the one define picked above -#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE -#define GPU_STEREO_DRAWCALL_DOUBLED -#endif - -#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER -#define GPU_STEREO_DRAWCALL_DOUBLED -#define GPU_STEREO_CAMERA_BUFFER -#endif - -#ifdef GPU_STEREO_TECHNIQUE_INSTANCED -#define GPU_STEREO_DRAWCALL_INSTANCED -#define GPU_STEREO_CAMERA_BUFFER -#endif - -namespace gpu { namespace gl { - -class GLBackend : public Backend, public std::enable_shared_from_this { - // Context Backend static interface required - friend class gpu::Context; - static void init(); - static BackendPointer createBackend(); - -protected: - explicit GLBackend(bool syncCache); - GLBackend(); -public: - static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet(), const Shader::CompilationHandler& handler = nullptr); - - virtual ~GLBackend(); - - void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset = false); - void render(const Batch& batch) final override; - - // This call synchronize the Full Backend cache with the current GLState - // THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync - // the gpu::Backend state with the true gl state which has probably been messed up by these ugly naked gl calls - // Let's try to avoid to do that as much as possible! - void syncCache() final override; - - // This is the ugly "download the pixels to sysmem for taking a snapshot" - // Just avoid using it, it's ugly and will break performances - virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, - const Vec4i& region, QImage& destImage) final override; - - - // this is the maximum numeber of available input buffers - size_t getNumInputBuffers() const { return _input._invalidBuffers.size(); } - - // this is the maximum per shader stage on the low end apple - // TODO make it platform dependant at init time - static const int MAX_NUM_UNIFORM_BUFFERS = 12; - size_t getMaxNumUniformBuffers() const { return MAX_NUM_UNIFORM_BUFFERS; } - - // this is the maximum per shader stage on the low end apple - // TODO make it platform dependant at init time - static const int MAX_NUM_RESOURCE_BUFFERS = 16; - size_t getMaxNumResourceBuffers() const { return MAX_NUM_RESOURCE_BUFFERS; } - static const int MAX_NUM_RESOURCE_TEXTURES = 16; - size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; } - - // Draw Stage - virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0; - virtual void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) = 0; - virtual void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) = 0; - - // Input Stage - virtual void do_setInputFormat(const Batch& batch, size_t paramOffset) final; - virtual void do_setInputBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final; - - // Transform Stage - virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final; - - // Uniform Stage - virtual void do_setUniformBuffer(const Batch& batch, size_t paramOffset) final; - - // Resource Stage - virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final; - - // Pipeline Stage - virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final; - - // Output stage - virtual void do_setFramebuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_clearFramebuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_blit(const Batch& batch, size_t paramOffset) = 0; - - // Query section - virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final; - virtual void do_endQuery(const Batch& batch, size_t paramOffset) final; - virtual void do_getQuery(const Batch& batch, size_t paramOffset) final; - - // Reset stages - virtual void do_resetStages(const Batch& batch, size_t paramOffset) final; - - - virtual void do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) final; - virtual void do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) final; - - virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final; - virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final; - - virtual void do_runLambda(const Batch& batch, size_t paramOffset) final; - - virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final; - virtual void do_stopNamedCall(const Batch& batch, size_t paramOffset) final; - - static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; - // The drawcall Info attribute channel is reserved and is the upper bound for the number of availables Input buffers - static const int MAX_NUM_INPUT_BUFFERS = Stream::DRAW_CALL_INFO; - - virtual void do_pushProfileRange(const Batch& batch, size_t paramOffset) final; - virtual void do_popProfileRange(const Batch& batch, size_t paramOffset) final; - - // TODO: As long as we have gl calls explicitely issued from interface - // code, we need to be able to record and batch these calls. THe long - // term strategy is to get rid of any GL calls in favor of the HIFI GPU API - virtual void do_glUniform1i(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform1f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform2f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform3f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform3fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4iv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final; - - virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final; - - // The State setters called by the GLState::Commands when a new state is assigned - virtual void do_setStateFillMode(int32 mode) final; - virtual void do_setStateCullMode(int32 mode) final; - virtual void do_setStateFrontFaceClockwise(bool isClockwise) final; - virtual void do_setStateDepthClampEnable(bool enable) final; - virtual void do_setStateScissorEnable(bool enable) final; - virtual void do_setStateMultisampleEnable(bool enable) final; - virtual void do_setStateAntialiasedLineEnable(bool enable) final; - virtual void do_setStateDepthBias(Vec2 bias) final; - virtual void do_setStateDepthTest(State::DepthTest test) final; - virtual void do_setStateStencil(State::StencilActivation activation, State::StencilTest frontTest, State::StencilTest backTest) final; - virtual void do_setStateAlphaToCoverageEnable(bool enable) final; - virtual void do_setStateSampleMask(uint32 mask) final; - virtual void do_setStateBlend(State::BlendFunction blendFunction) final; - virtual void do_setStateColorWriteMask(uint32 mask) final; - virtual void do_setStateBlendFactor(const Batch& batch, size_t paramOffset) final; - virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final; - - virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; - virtual GLuint getTextureID(const TexturePointer& texture) final; - virtual GLuint getBufferID(const Buffer& buffer) = 0; - virtual GLuint getQueryID(const QueryPointer& query) = 0; - - virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; - virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; - virtual GLTexture* syncGPUObject(const TexturePointer& texture); - virtual GLQuery* syncGPUObject(const Query& query) = 0; - //virtual bool isTextureReady(const TexturePointer& texture); - - virtual void releaseBuffer(GLuint id, Size size) const; - virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const; - virtual void releaseTexture(GLuint id, Size size) const; - virtual void releaseFramebuffer(GLuint id) const; - virtual void releaseShader(GLuint id) const; - virtual void releaseProgram(GLuint id) const; - virtual void releaseQuery(GLuint id) const; - virtual void queueLambda(const std::function lambda) const; - - bool isTextureManagementSparseEnabled() const override { return (_textureManagement._sparseCapable && Texture::getEnableSparseTextures()); } - -protected: - - void recycle() const override; - - static const size_t INVALID_OFFSET = (size_t)-1; - bool _inRenderTransferPass { false }; - int32_t _uboAlignment { 0 }; - int _currentDraw { -1 }; - - std::list profileRanges; - mutable Mutex _trashMutex; - mutable std::list> _buffersTrash; - mutable std::list> _texturesTrash; - mutable std::list> _externalTexturesTrash; - mutable std::list _framebuffersTrash; - mutable std::list _shadersTrash; - mutable std::list _programsTrash; - mutable std::list _queriesTrash; - mutable std::list> _lambdaQueue; - - void renderPassTransfer(const Batch& batch); - void renderPassDraw(const Batch& batch); - -#ifdef GPU_STEREO_DRAWCALL_DOUBLED - void setupStereoSide(int side); -#endif - - virtual void initInput() final; - virtual void killInput() final; - virtual void syncInputStateCache() final; - virtual void resetInputStage(); - virtual void updateInput() = 0; - - struct InputStageState { - bool _invalidFormat { true }; - bool _hadColorAttribute{ true }; - Stream::FormatPointer _format; - std::string _formatKey; - - typedef std::bitset ActivationCache; - ActivationCache _attributeActivation { 0 }; - - typedef std::bitset BuffersState; - - BuffersState _invalidBuffers{ 0 }; - BuffersState _attribBindingBuffers{ 0 }; - - Buffers _buffers; - Offsets _bufferOffsets; - Offsets _bufferStrides; - std::vector _bufferVBOs; - - glm::vec4 _colorAttribute{ 0.0f }; - - BufferPointer _indexBuffer; - Offset _indexBufferOffset { 0 }; - Type _indexBufferType { UINT32 }; - - BufferPointer _indirectBuffer; - Offset _indirectBufferOffset{ 0 }; - Offset _indirectBufferStride{ 0 }; - - GLuint _defaultVAO { 0 }; - - InputStageState() : - _invalidFormat(true), - _format(0), - _formatKey(), - _attributeActivation(0), - _buffers(_invalidBuffers.size(), BufferPointer(0)), - _bufferOffsets(_invalidBuffers.size(), 0), - _bufferStrides(_invalidBuffers.size(), 0), - _bufferVBOs(_invalidBuffers.size(), 0) {} - } _input; - - virtual void initTransform() = 0; - void killTransform(); - // Synchronize the state cache of this Backend with the actual real state of the GL Context - void syncTransformStateCache(); - virtual void updateTransform(const Batch& batch) = 0; - virtual void resetTransformStage(); - - // Allows for correction of the camera pose to account for changes - // between the time when a was recorded and the time(s) when it is - // executed - struct CameraCorrection { - Mat4 correction; - Mat4 correctionInverse; - Mat4 prevView; - Mat4 prevViewInverse; - }; - - struct TransformStageState { -#ifdef GPU_STEREO_CAMERA_BUFFER - struct Cameras { - TransformCamera _cams[2]; - - Cameras() {}; - Cameras(const TransformCamera& cam) { memcpy(_cams, &cam, sizeof(TransformCamera)); }; - Cameras(const TransformCamera& camL, const TransformCamera& camR) { memcpy(_cams, &camL, sizeof(TransformCamera)); memcpy(_cams + 1, &camR, sizeof(TransformCamera)); }; - }; - - using CameraBufferElement = Cameras; -#else - using CameraBufferElement = TransformCamera; -#endif - using TransformCameras = std::vector; - - TransformCamera _camera; - TransformCameras _cameras; - - mutable std::map _drawCallInfoOffsets; - - GLuint _objectBuffer { 0 }; - GLuint _cameraBuffer { 0 }; - GLuint _drawCallInfoBuffer { 0 }; - GLuint _objectBufferTexture { 0 }; - size_t _cameraUboSize { 0 }; - bool _viewIsCamera{ false }; - bool _skybox { false }; - Transform _view; - CameraCorrection _correction; - bool _viewCorrectionEnabled{ true }; - - - Mat4 _projection; - Vec4i _viewport { 0, 0, 1, 1 }; - Vec2 _depthRange { 0.0f, 1.0f }; - bool _invalidView { false }; - bool _invalidProj { false }; - bool _invalidViewport { false }; - - bool _enabledDrawcallInfoBuffer{ false }; - - using Pair = std::pair; - using List = std::list; - List _cameraOffsets; - mutable List::const_iterator _camerasItr; - mutable size_t _currentCameraOffset{ INVALID_OFFSET }; - - void preUpdate(size_t commandIndex, const StereoState& stereo); - void update(size_t commandIndex, const StereoState& stereo) const; - void bindCurrentCamera(int stereoSide) const; - } _transform; - - virtual void transferTransformState(const Batch& batch) const = 0; - - struct UniformStageState { - std::array _buffers; - //Buffers _buffers { }; - } _uniform; - - void releaseUniformBuffer(uint32_t slot); - void resetUniformStage(); - - // update resource cache and do the gl bind/unbind call with the current gpu::Buffer cached at slot s - // This is using different gl object depending on the gl version - virtual bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) = 0; - virtual void releaseResourceBuffer(uint32_t slot) = 0; - - // update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s - void releaseResourceTexture(uint32_t slot); - - void resetResourceStage(); - - struct ResourceStageState { - std::array _buffers; - std::array _textures; - //Textures _textures { { MAX_NUM_RESOURCE_TEXTURES } }; - int findEmptyTextureSlot() const; - } _resource; - - size_t _commandIndex{ 0 }; - - // Standard update pipeline check that the current Program and current State or good to go for a - void updatePipeline(); - // Force to reset all the state fields indicated by the 'toBeReset" signature - void resetPipelineState(State::Signature toBeReset); - // Synchronize the state cache of this Backend with the actual real state of the GL Context - void syncPipelineStateCache(); - void resetPipelineStage(); - - struct PipelineStageState { - PipelinePointer _pipeline; - - GLuint _program { 0 }; - GLint _cameraCorrectionLocation { -1 }; - GLShader* _programShader { nullptr }; - bool _invalidProgram { false }; - - BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; - BufferView _cameraCorrectionBufferIdentity { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; - - State::Data _stateCache{ State::DEFAULT }; - State::Signature _stateSignatureCache { 0 }; - - GLState* _state { nullptr }; - bool _invalidState { false }; - - PipelineStageState() { - _cameraCorrectionBuffer.edit() = CameraCorrection(); - _cameraCorrectionBufferIdentity.edit() = CameraCorrection(); - _cameraCorrectionBufferIdentity._buffer->flush(); - } - } _pipeline; - - // Backend dependant compilation of the shader - virtual GLShader* compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler); - virtual GLShader* compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler); - virtual std::string getBackendShaderHeader() const; - virtual void makeProgramBindings(ShaderObject& shaderObject); - class ElementResource { - public: - gpu::Element _element; - uint16 _resource; - ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {} - }; - ElementResource getFormatFromGLUniform(GLenum gltype); - static const GLint UNUSED_SLOT {-1}; - static bool isUnusedSlot(GLint binding) { return (binding == UNUSED_SLOT); } - virtual int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, - Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers); - virtual int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers); - virtual int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers) = 0; - virtual int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs); - virtual int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs); - - - // Synchronize the state cache of this Backend with the actual real state of the GL Context - void syncOutputStateCache(); - void resetOutputStage(); - - struct OutputStageState { - FramebufferPointer _framebuffer { nullptr }; - GLuint _drawFBO { 0 }; - } _output; - - void resetQueryStage(); - struct QueryStageState { - uint32_t _rangeQueryDepth { 0 }; - } _queryStage; - - void resetStages(); - - struct TextureManagementStageState { - bool _sparseCapable { false }; - } _textureManagement; - virtual void initTextureManagementStage() {} - - typedef void (GLBackend::*CommandCall)(const Batch&, size_t); - static CommandCall _commandCalls[Batch::NUM_COMMANDS]; - friend class GLState; - friend class GLTexture; - friend class GLShader; -}; - -} } - -#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp deleted file mode 100644 index 45c0de8ed7..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp +++ /dev/null @@ -1,175 +0,0 @@ -// -// GLBackendTexture.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 1/19/2015. -// Copyright 2014 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 "GLBackend.h" -#include "GLShared.h" -#include "GLFramebuffer.h" - -#include - -using namespace gpu; -using namespace gpu::gl; - -void GLBackend::syncOutputStateCache() { - GLint currentFBO; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBO); - - _output._drawFBO = currentFBO; - _output._framebuffer.reset(); -} - -void GLBackend::resetOutputStage() { - if (_output._framebuffer) { - _output._framebuffer.reset(); - _output._drawFBO = 0; - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - } - - glEnable(GL_FRAMEBUFFER_SRGB_EXT); -} - -void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) { - auto framebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); - if (_output._framebuffer != framebuffer) { - auto newFBO = getFramebufferID(framebuffer); - if (_output._drawFBO != newFBO) { - _output._drawFBO = newFBO; - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, newFBO); - } - _output._framebuffer = framebuffer; - } -} - -void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { - if (_stereo.isStereo() && !_pipeline._stateCache.scissorEnable) { - qWarning("Clear without scissor in stereo mode"); - } - - uint32 masks = batch._params[paramOffset + 7]._uint; - Vec4 color; - color.x = batch._params[paramOffset + 6]._float; - color.y = batch._params[paramOffset + 5]._float; - color.z = batch._params[paramOffset + 4]._float; - color.w = batch._params[paramOffset + 3]._float; - float depth = batch._params[paramOffset + 2]._float; - int stencil = batch._params[paramOffset + 1]._int; - int useScissor = batch._params[paramOffset + 0]._int; - - GLuint glmask = 0; - bool restoreStencilMask = false; - uint8_t cacheStencilMask = 0xFF; - if (masks & Framebuffer::BUFFER_STENCIL) { - glClearStencil(stencil); - glmask |= GL_STENCIL_BUFFER_BIT; - cacheStencilMask = _pipeline._stateCache.stencilActivation.getWriteMaskFront(); - if (cacheStencilMask != 0xFF) { - restoreStencilMask = true; - glStencilMask(0xFF); - } - } - - bool restoreDepthMask = false; - if (masks & Framebuffer::BUFFER_DEPTH) { - glClearDepthf(depth); - glmask |= GL_DEPTH_BUFFER_BIT; - - bool cacheDepthMask = _pipeline._stateCache.depthTest.getWriteMask(); - if (!cacheDepthMask) { - restoreDepthMask = true; - glDepthMask(GL_TRUE); - } - } - - std::vector drawBuffers; - if (masks & Framebuffer::BUFFER_COLORS) { - if (_output._framebuffer) { - for (unsigned int i = 0; i < Framebuffer::MAX_NUM_RENDER_BUFFERS; i++) { - if (masks & (1 << i)) { - drawBuffers.push_back(GL_COLOR_ATTACHMENT0 + i); - } - } - - if (!drawBuffers.empty()) { - glDrawBuffers((GLsizei)drawBuffers.size(), drawBuffers.data()); - glClearColor(color.x, color.y, color.z, color.w); - glmask |= GL_COLOR_BUFFER_BIT; - - (void) CHECK_GL_ERROR(); - } - } else { - glClearColor(color.x, color.y, color.z, color.w); - glmask |= GL_COLOR_BUFFER_BIT; - } - - // Force the color mask cache to WRITE_ALL if not the case - do_setStateColorWriteMask(State::ColorMask::WRITE_ALL); - } - - // Apply scissor if needed and if not already on - bool doEnableScissor = (useScissor && (!_pipeline._stateCache.scissorEnable)); - if (doEnableScissor) { - glEnable(GL_SCISSOR_TEST); - } - - // Clear! - glClear(glmask); - - // Restore scissor if needed - if (doEnableScissor) { - glDisable(GL_SCISSOR_TEST); - } - - // Restore Stencil write mask - if (restoreStencilMask) { - glStencilMask(cacheStencilMask); - } - - // Restore write mask meaning turn back off - if (restoreDepthMask) { - glDepthMask(GL_FALSE); - } - - // Restore the color draw buffers only if a frmaebuffer is bound - if (_output._framebuffer && !drawBuffers.empty()) { - auto glFramebuffer = syncGPUObject(*_output._framebuffer); - if (glFramebuffer) { - glDrawBuffers((GLsizei)glFramebuffer->_colorBuffers.size(), glFramebuffer->_colorBuffers.data()); - } - } - - (void) CHECK_GL_ERROR(); -} - -void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) { - auto readFBO = getFramebufferID(srcFramebuffer); - if (srcFramebuffer && readFBO) { - if ((srcFramebuffer->getWidth() < (region.x + region.z)) || (srcFramebuffer->getHeight() < (region.y + region.w))) { - qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : srcFramebuffer is too small to provide the region queried"; - return; - } - } - - if ((destImage.width() < region.z) || (destImage.height() < region.w)) { - qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : destImage is too small to receive the region of the framebuffer"; - return; - } - - GLenum format = GL_RGBA; - if (destImage.format() != QImage::Format_ARGB32) { - qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : destImage format must be FORMAT_ARGB32 to receive the region of the framebuffer"; - return; - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, getFramebufferID(srcFramebuffer)); - glReadPixels(region.x, region.y, region.z, region.w, format, GL_UNSIGNED_BYTE, destImage.bits()); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - - (void) CHECK_GL_ERROR(); -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp deleted file mode 100644 index b80be8492f..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp +++ /dev/null @@ -1,291 +0,0 @@ -// -// GLBackendPipeline.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 3/8/2015. -// Copyright 2014 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 "GLBackend.h" -#include "GLShared.h" -#include "GLPipeline.h" -#include "GLShader.h" -#include "GLState.h" -#include "GLBuffer.h" -#include "GLTexture.h" - -using namespace gpu; -using namespace gpu::gl; - -void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { - PipelinePointer pipeline = batch._pipelines.get(batch._params[paramOffset + 0]._uint); - - if (_pipeline._pipeline == pipeline) { - return; - } - - // A true new Pipeline - _stats._PSNumSetPipelines++; - - // null pipeline == reset - if (!pipeline) { - _pipeline._pipeline.reset(); - - _pipeline._program = 0; - _pipeline._cameraCorrectionLocation = -1; - _pipeline._programShader = nullptr; - _pipeline._invalidProgram = true; - - _pipeline._state = nullptr; - _pipeline._invalidState = true; - } else { - auto pipelineObject = GLPipeline::sync(*this, *pipeline); - if (!pipelineObject) { - return; - } - - // check the program cache - // pick the program version - // check the program cache - // pick the program version -#ifdef GPU_STEREO_CAMERA_BUFFER - GLuint glprogram = pipelineObject->_program->getProgram((GLShader::Version) isStereo()); -#else - GLuint glprogram = pipelineObject->_program->getProgram(); -#endif - - if (_pipeline._program != glprogram) { - _pipeline._program = glprogram; - _pipeline._programShader = pipelineObject->_program; - _pipeline._invalidProgram = true; - _pipeline._cameraCorrectionLocation = pipelineObject->_cameraCorrection; - } - - // Now for the state - if (_pipeline._state != pipelineObject->_state) { - _pipeline._state = pipelineObject->_state; - _pipeline._invalidState = true; - } - - // Remember the new pipeline - _pipeline._pipeline = pipeline; - } - - // THis should be done on Pipeline::update... - if (_pipeline._invalidProgram) { - glUseProgram(_pipeline._program); - if (_pipeline._cameraCorrectionLocation != -1) { - gl::GLBuffer* cameraCorrectionBuffer = nullptr; - if (_transform._viewCorrectionEnabled) { - cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer); - } else { - cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBufferIdentity._buffer); - } - glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection)); - } - (void) CHECK_GL_ERROR(); - _pipeline._invalidProgram = false; - } -} - -void GLBackend::updatePipeline() { - if (_pipeline._invalidProgram) { - // doing it here is aproblem for calls to glUniform.... so will do it on assing... - glUseProgram(_pipeline._program); - (void) CHECK_GL_ERROR(); - _pipeline._invalidProgram = false; - } - - if (_pipeline._invalidState) { - if (_pipeline._state) { - // first reset to default what should be - // the fields which were not to default and are default now - resetPipelineState(_pipeline._state->_signature); - - // Update the signature cache with what's going to be touched - _pipeline._stateSignatureCache |= _pipeline._state->_signature; - - // And perform - for (auto command: _pipeline._state->_commands) { - command->run(this); - } - } else { - // No state ? anyway just reset everything - resetPipelineState(0); - } - _pipeline._invalidState = false; - } -} - -void GLBackend::resetPipelineStage() { - // First reset State to default - State::Signature resetSignature(0); - resetPipelineState(resetSignature); - _pipeline._state = nullptr; - _pipeline._invalidState = false; - - // Second the shader side - _pipeline._invalidProgram = false; - _pipeline._program = 0; - _pipeline._programShader = nullptr; - _pipeline._pipeline.reset(); - glUseProgram(0); -} - -void GLBackend::releaseUniformBuffer(uint32_t slot) { - auto& buf = _uniform._buffers[slot]; - if (buf) { - auto* object = Backend::getGPUObject(*buf); - if (object) { - glBindBufferBase(GL_UNIFORM_BUFFER, slot, 0); // RELEASE - (void) CHECK_GL_ERROR(); - } - buf.reset(); - } -} - -void GLBackend::resetUniformStage() { - for (uint32_t i = 0; i < _uniform._buffers.size(); i++) { - releaseUniformBuffer(i); - } -} - -void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { - GLuint slot = batch._params[paramOffset + 3]._uint; - if (slot >(GLuint)MAX_NUM_UNIFORM_BUFFERS) { - qCDebug(gpugllogging) << "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" << slot << " which doesn't exist. MaxNumUniformBuffers = " << getMaxNumUniformBuffers(); - return; - } - BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); - GLintptr rangeStart = batch._params[paramOffset + 1]._uint; - GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; - - if (!uniformBuffer) { - releaseUniformBuffer(slot); - return; - } - - // check cache before thinking - if (_uniform._buffers[slot] == uniformBuffer) { - return; - } - - // Sync BufferObject - auto* object = syncGPUObject(*uniformBuffer); - if (object) { - glBindBufferRange(GL_UNIFORM_BUFFER, slot, object->_buffer, rangeStart, rangeSize); - - _uniform._buffers[slot] = uniformBuffer; - (void) CHECK_GL_ERROR(); - } else { - releaseUniformBuffer(slot); - return; - } -} - -void GLBackend::releaseResourceTexture(uint32_t slot) { - auto& tex = _resource._textures[slot]; - if (tex) { - auto* object = Backend::getGPUObject(*tex); - if (object) { - GLuint target = object->_target; - glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(target, 0); // RELEASE - (void) CHECK_GL_ERROR(); - } - tex.reset(); - } -} - -void GLBackend::resetResourceStage() { - for (uint32_t i = 0; i < _resource._buffers.size(); i++) { - releaseResourceBuffer(i); - } - for (uint32_t i = 0; i < _resource._textures.size(); i++) { - releaseResourceTexture(i); - } -} - -void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) { - GLuint slot = batch._params[paramOffset + 1]._uint; - if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) { - qCDebug(gpugllogging) << "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" << slot << " which doesn't exist. MaxNumResourceBuffers = " << getMaxNumResourceBuffers(); - return; - } - - auto resourceBuffer = batch._buffers.get(batch._params[paramOffset + 0]._uint); - - if (!resourceBuffer) { - releaseResourceBuffer(slot); - return; - } - // check cache before thinking - if (_resource._buffers[slot] == resourceBuffer) { - return; - } - - // One more True Buffer bound - _stats._RSNumResourceBufferBounded++; - - // If successful bind then cache it - if (bindResourceBuffer(slot, resourceBuffer)) { - _resource._buffers[slot] = resourceBuffer; - } else { // else clear slot and cache - releaseResourceBuffer(slot); - return; - } -} - -void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { - GLuint slot = batch._params[paramOffset + 1]._uint; - if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) { - qCDebug(gpugllogging) << "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures(); - return; - } - - TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); - - if (!resourceTexture) { - releaseResourceTexture(slot); - return; - } - // check cache before thinking - if (_resource._textures[slot] == resourceTexture) { - return; - } - - // One more True texture bound - _stats._RSNumTextureBounded++; - - // Always make sure the GLObject is in sync - GLTexture* object = syncGPUObject(resourceTexture); - if (object) { - GLuint to = object->_texture; - GLuint target = object->_target; - glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(target, to); - - (void) CHECK_GL_ERROR(); - - _resource._textures[slot] = resourceTexture; - - _stats._RSAmountTextureMemoryBounded += (int) object->size(); - - } else { - releaseResourceTexture(slot); - return; - } -} - -int GLBackend::ResourceStageState::findEmptyTextureSlot() const { - // start from the end of the slots, try to find an empty one that can be used - for (auto i = MAX_NUM_RESOURCE_TEXTURES - 1; i > 0; i--) { - if (!_textures[i]) { - return i; - } - } - return -1; -} - diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp deleted file mode 100644 index 43c8f8f465..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// -// GLBackendQuery.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 7/7/2015. -// Copyright 2015 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 "GLBackend.h" -#include "GLQuery.h" -#include "GLShared.h" - -using namespace gpu; -using namespace gpu::gl; - -// Eventually, we want to test with TIME_ELAPSED instead of TIMESTAMP -#ifdef Q_OS_MAC -//const uint32_t MAX_RANGE_QUERY_DEPTH = 1; -static bool timeElapsed = true; -#else -//const uint32_t MAX_RANGE_QUERY_DEPTH = 10000; -static bool timeElapsed = false; -#endif - -void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { -#if !defined(USE_GLES) - auto query = batch._queries.get(batch._params[paramOffset]._uint); - GLQuery* glquery = syncGPUObject(*query); - if (glquery) { - //glGetInteger64v(GL_TIMESTAMP_EXT, (GLint64*)&glquery->_batchElapsedTime); - glquery->_batchElapsedTime = 1; - if (timeElapsed) { - glBeginQuery(GL_TIME_ELAPSED_EXT, glquery->_endqo); - } else { - if (glQueryCounterEXT != NULL) { - glQueryCounterEXT(glquery->_beginqo, GL_TIMESTAMP_EXT); - } - } - glquery->_rangeQueryDepth = _queryStage._rangeQueryDepth; - (void)CHECK_GL_ERROR(); - } -#endif -} - -void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { -#if !defined(USE_GLES) - auto query = batch._queries.get(batch._params[paramOffset]._uint); - GLQuery* glquery = syncGPUObject(*query); - if (glquery) { - if (timeElapsed) { - glEndQuery(GL_TIME_ELAPSED_EXT); - } else { - if (glQueryCounterEXT != NULL) { - glQueryCounterEXT(glquery->_endqo, GL_TIMESTAMP_EXT); - } - } - - --_queryStage._rangeQueryDepth; - GLint64 now; - //glGetInteger64v(GL_TIMESTAMP_EXT, &now); - //glquery->_batchElapsedTime = now - glquery->_batchElapsedTime; - now = 1; - glquery->_batchElapsedTime = 1; - - PROFILE_RANGE_END(render_gpu_gl, glquery->_profileRangeId); - - (void)CHECK_GL_ERROR(); - } -#endif -} - -void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { -#if !defined(USE_GLES) - auto query = batch._queries.get(batch._params[paramOffset]._uint); - if (glGetQueryObjectui64vEXT == NULL) - return; - GLQuery* glquery = syncGPUObject(*query); - if (glquery) { - glGetQueryObjectui64vEXT(glquery->_endqo, GL_QUERY_RESULT_AVAILABLE, &glquery->_result); - if (glquery->_result == GL_TRUE) { - if (timeElapsed) { - glGetQueryObjectui64vEXT(glquery->_endqo, GL_QUERY_RESULT, &glquery->_result); - } else { - GLuint64 start, end; - glGetQueryObjectui64vEXT(glquery->_beginqo, GL_QUERY_RESULT, &start); - glGetQueryObjectui64vEXT(glquery->_endqo, GL_QUERY_RESULT, &end); - glquery->_result = end - start; - } - query->triggerReturnHandler(glquery->_result, glquery->_batchElapsedTime); - } - (void)CHECK_GL_ERROR(); - } -#endif -} - -void GLBackend::resetQueryStage() { -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp deleted file mode 100644 index 677bba97ca..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp +++ /dev/null @@ -1,583 +0,0 @@ -// -// Created by Gabriel Calero & Cristian Duarte on 2017/12/28 -// Copyright 2013-2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#include "GLBackend.h" -#include "GLShader.h" -#include - -using namespace gpu; -using namespace gpu::gl; - -// GLSL version -std::string GLBackend::getBackendShaderHeader() const { - return std::string("#version 310 es"); -} - -// Shader domain -static const size_t NUM_SHADER_DOMAINS = 2; - -// GL Shader type enums -// Must match the order of type specified in gpu::Shader::Type -static const std::array SHADER_DOMAINS{ { - GL_VERTEX_SHADER, - GL_FRAGMENT_SHADER, - // GL_GEOMETRY_SHADER, -} }; - -// Domain specific defines -// Must match the order of type specified in gpu::Shader::Type -static const std::array DOMAIN_DEFINES{ { - "#define GPU_VERTEX_SHADER", - "#define GPU_PIXEL_SHADER", - // "#define GPU_GEOMETRY_SHADER", -} }; - -// Stereo specific defines -static const std::string stereoVersion{ -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN" -#endif -#ifdef GPU_STEREO_DRAWCALL_DOUBLED -#ifdef GPU_STEREO_CAMERA_BUFFER - "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED" -#else - "#define GPU_TRANSFORM_IS_STEREO" -#endif -#endif -}; - -// Versions specific of the shader -static const std::array VERSION_DEFINES { { - "", - stereoVersion -} }; - -GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler) { - // Any GLSLprogram ? normally yes... - const std::string& shaderSource = shader.getSource().getCode(); - GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; - GLShader::ShaderObjects shaderObjects; - Shader::CompilationLogs compilationLogs(GLShader::NumVersions); - - for (int version = 0; version < GLShader::NumVersions; version++) { - auto& shaderObject = shaderObjects[version]; - - std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version] - + "\n#extension GL_EXT_texture_buffer : enable" - + "\nprecision lowp float; // check precision 2" - + "\nprecision lowp samplerBuffer;" - + "\nprecision lowp sampler2DShadow;"; - if (handler) { - bool retest = true; - std::string currentSrc = shaderSource; - // When a Handler is specified, we can try multiple times to build the shader and let the handler change the source if the compilation fails. - // The retest bool is set to false as soon as the compilation succeed to wexit the while loop. - // The handler tells us if we should retry or not while returning a modified version of the source. - while (retest) { - bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderDefines, shaderObject.glshader, compilationLogs[version].message); - compilationLogs[version].compiled = result; - if (!result) { - std::string newSrc; - retest = handler(shader, currentSrc, compilationLogs[version], newSrc); - currentSrc = newSrc; - } else { - retest = false; - } - } - } else { - compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message); - } - - if (!compilationLogs[version].compiled) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << compilationLogs[version].message.c_str(); - shader.setCompilationLogs(compilationLogs); - return nullptr; - } - } - // Compilation feedback - shader.setCompilationLogs(compilationLogs); - - // So far so good, the shader is created successfully - GLShader* object = new GLShader(this->shared_from_this()); - object->_shaderObjects = shaderObjects; - - return object; -} - -GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler) { - if (!program.isProgram()) { - return nullptr; - } - - GLShader::ShaderObjects programObjects; - - Shader::CompilationLogs compilationLogs(GLShader::NumVersions); - - for (int version = 0; version < GLShader::NumVersions; version++) { - auto& programObject = programObjects[version]; - - // Let's go through every shaders and make sure they are ready to go - std::vector< GLuint > shaderGLObjects; - for (auto subShader : program.getShaders()) { - auto object = GLShader::sync((*this), *subShader, handler); - if (object) { - shaderGLObjects.push_back(object->_shaderObjects[version].glshader); - } else { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?"; - program.setCompilationLogs(compilationLogs); - return nullptr; - } - } - - GLuint glprogram = ::gl::compileProgram(shaderGLObjects, compilationLogs[version].message, compilationLogs[version].binary); - if (glprogram == 0) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str(); - program.setCompilationLogs(compilationLogs); - return nullptr; - } - - programObject.glprogram = glprogram; - - makeProgramBindings(programObject); - } - // Compilation feedback - program.setCompilationLogs(compilationLogs); - - // So far so good, the program versions have all been created successfully - GLShader* object = new GLShader(this->shared_from_this()); - object->_shaderObjects = programObjects; - - return object; -} - -GLBackend::ElementResource GLBackend::getFormatFromGLUniform(GLenum gltype) { - switch (gltype) { - case GL_FLOAT: - return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC2: - return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC3: - return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC4: - return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - - case GL_INT: - return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC2: - return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC3: - return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC4: - return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); - - case GL_UNSIGNED_INT: - return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC2: - return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC3: - return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC4: - return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); - - case GL_BOOL: - return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC2: - return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC3: - return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC4: - return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); - - case GL_FLOAT_MAT2: - return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT3: - return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT4: - return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - - //{GL_FLOAT_MAT2x3 mat2x3}, - //{GL_FLOAT_MAT2x4 mat2x4}, - //{GL_FLOAT_MAT3x2 mat3x2}, - //{GL_FLOAT_MAT3x4 mat3x4}, - //{GL_FLOAT_MAT4x2 mat4x2}, - //{GL_FLOAT_MAT4x3 mat4x3}, - //{GL_DOUBLE_MAT2 dmat2}, - //{GL_DOUBLE_MAT3 dmat3}, - //{GL_DOUBLE_MAT4 dmat4}, - //{GL_DOUBLE_MAT2x3 dmat2x3}, - //{GL_DOUBLE_MAT2x4 dmat2x4}, - //{GL_DOUBLE_MAT3x2 dmat3x2}, - //{GL_DOUBLE_MAT3x4 dmat3x4}, - //{GL_DOUBLE_MAT4x2 dmat4x2}, - //{GL_DOUBLE_MAT4x3 dmat4x3}, - - case GL_SAMPLER_2D: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); - - case GL_SAMPLER_3D: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); - case GL_SAMPLER_CUBE: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_SAMPLER_2D_MULTISAMPLE: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_SAMPLER_2D_ARRAY: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - - case GL_SAMPLER_2D_SHADOW: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); - case GL_SAMPLER_CUBE_SHADOW: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); - - case GL_SAMPLER_2D_ARRAY_SHADOW: - return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); - - // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, - // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, - - case GL_SAMPLER_BUFFER: - return ElementResource(Element(SCALAR, gpu::FLOAT, RESOURCE_BUFFER), Resource::BUFFER); - - // {GL_SAMPLER_2D_RECT sampler2DRect}, - // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, - - case GL_INT_SAMPLER_2D: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_2D_MULTISAMPLE: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_3D: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); - case GL_INT_SAMPLER_CUBE: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_INT_SAMPLER_2D_ARRAY: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - - // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, - // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, - - case GL_UNSIGNED_INT_SAMPLER_2D: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_3D: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); - case GL_UNSIGNED_INT_SAMPLER_CUBE: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - //{GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, - //{GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, - - //{GL_IMAGE_1D image1D}, - //{GL_IMAGE_2D image2D}, - //{GL_IMAGE_3D image3D}, - //{GL_IMAGE_2D_RECT image2DRect}, - //{GL_IMAGE_CUBE imageCube}, - //{GL_IMAGE_BUFFER imageBuffer}, - //{GL_IMAGE_1D_ARRAY image1DArray}, - //{GL_IMAGE_2D_ARRAY image2DArray}, - //{GL_IMAGE_2D_MULTISAMPLE image2DMS}, - //{GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, - //{GL_INT_IMAGE_1D iimage1D}, - //{GL_INT_IMAGE_2D iimage2D}, - //{GL_INT_IMAGE_3D iimage3D}, - //{GL_INT_IMAGE_2D_RECT iimage2DRect}, - //{GL_INT_IMAGE_CUBE iimageCube}, - //{GL_INT_IMAGE_BUFFER iimageBuffer}, - //{GL_INT_IMAGE_1D_ARRAY iimage1DArray}, - //{GL_INT_IMAGE_2D_ARRAY iimage2DArray}, - //{GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, - //{GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, - //{GL_UNSIGNED_INT_IMAGE_1D uimage1D}, - //{GL_UNSIGNED_INT_IMAGE_2D uimage2D}, - //{GL_UNSIGNED_INT_IMAGE_3D uimage3D}, - //{GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, - //{GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot - - //{GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, - //{GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, - //{GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, - //{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, - //{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, - //{GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} -#if 0 - case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); - case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); - case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); -#endif - - default: - return ElementResource(Element(), Resource::BUFFER); - } -}; - -int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, - Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { - GLint uniformsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); - - for (int i = 0; i < uniformsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - GLint location = glGetUniformLocation(glprogram, name); - const GLint INVALID_UNIFORM_LOCATION = -1; - - // Try to make sense of the gltype - auto elementResource = getFormatFromGLUniform(type); - - // The uniform as a standard var type - if (location != INVALID_UNIFORM_LOCATION) { - // Let's make sure the name doesn't contains an array element - std::string sname(name); - auto foundBracket = sname.find_first_of('['); - if (foundBracket != std::string::npos) { - // std::string arrayname = sname.substr(0, foundBracket); - - if (sname[foundBracket + 1] == '0') { - sname = sname.substr(0, foundBracket); - } else { - // skip this uniform since it's not the first element of an array - continue; - } - } - - if (elementResource._resource == Resource::BUFFER) { - uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); - } else { - // For texture/Sampler, the location is the actual binding value - GLint binding = -1; - glGetUniformiv(glprogram, location, &binding); - - auto requestedBinding = slotBindings.find(std::string(sname)); - if (requestedBinding != slotBindings.end()) { - if (binding != (*requestedBinding)._location) { - binding = (*requestedBinding)._location; - for (auto i = 0; i < size; i++) { - // If we are working with an array of textures, reserve for each elemet - glProgramUniform1i(glprogram, location + i, binding + i); - } - } - } - - textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); - samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); - } - } - } - - return uniformsCount; -} - -int GLBackend::makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { - GLint buffersCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); - - // fast exit - if (buffersCount == 0) { - return 0; - } - - GLint maxNumUniformBufferSlots = 0; - glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); - std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); - - struct UniformBlockInfo { - using Vector = std::vector; - const GLuint index{ 0 }; - const std::string name; - GLint binding{ -1 }; - GLint size{ 0 }; - - static std::string getName(GLuint glprogram, GLuint i) { - static const GLint NAME_LENGTH = 256; - GLint length = 0; - GLchar nameBuffer[NAME_LENGTH]; - glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); - glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer); - return std::string(nameBuffer); - } - - UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { - glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding); - glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); - } - }; - - UniformBlockInfo::Vector uniformBlocks; - uniformBlocks.reserve(buffersCount); - for (int i = 0; i < buffersCount; i++) { - uniformBlocks.push_back(UniformBlockInfo(glprogram, i)); - } - - for (auto& info : uniformBlocks) { - auto requestedBinding = slotBindings.find(info.name); - if (requestedBinding != slotBindings.end()) { - info.binding = (*requestedBinding)._location; - glUniformBlockBinding(glprogram, info.index, info.binding); - uniformBufferSlotMap[info.binding] = info.index; - } - } - - for (auto& info : uniformBlocks) { - if (slotBindings.count(info.name)) { - continue; - } - - // If the binding is 0, or the binding maps to an already used binding - if (info.binding == 0 || !isUnusedSlot(uniformBufferSlotMap[info.binding])) { - // If no binding was assigned then just do it finding a free slot - auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), GLBackend::isUnusedSlot); - if (slotIt != uniformBufferSlotMap.end()) { - info.binding = slotIt - uniformBufferSlotMap.begin(); - glUniformBlockBinding(glprogram, info.index, info.binding); - } else { - // This should neve happen, an active ubo cannot find an available slot among the max available?! - info.binding = -1; - } - } - - uniformBufferSlotMap[info.binding] = info.index; - } - - for (auto& info : uniformBlocks) { - static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); - buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); - } - return buffersCount; -} - -int GLBackend::makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { - GLint inputsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); - - for (int i = 0; i < inputsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - - GLint binding = glGetAttribLocation(glprogram, name); - - auto elementResource = getFormatFromGLUniform(type); - inputs.insert(Shader::Slot(name, binding, elementResource._element, -1)); - } - - return inputsCount; -} - -int GLBackend::makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { - /* GLint outputsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); - - for (int i = 0; i < inputsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - - auto element = getFormatFromGLUniform(type); - outputs.insert(Shader::Slot(name, i, element)); - } - */ - return 0; //inputsCount; -} - -void GLBackend::makeProgramBindings(ShaderObject& shaderObject) { - if (!shaderObject.glprogram) { - return; - } - GLuint glprogram = shaderObject.glprogram; - GLint loc = -1; - - //Check for gpu specific attribute slotBindings - loc = glGetAttribLocation(glprogram, "inPosition"); - if (loc >= 0 && loc != gpu::Stream::POSITION) { - glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition"); - } - - loc = glGetAttribLocation(glprogram, "inNormal"); - if (loc >= 0 && loc != gpu::Stream::NORMAL) { - glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal"); - } - - loc = glGetAttribLocation(glprogram, "inColor"); - if (loc >= 0 && loc != gpu::Stream::COLOR) { - glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor"); - } - - loc = glGetAttribLocation(glprogram, "inTexCoord0"); - if (loc >= 0 && loc != gpu::Stream::TEXCOORD) { - glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0"); - } - - loc = glGetAttribLocation(glprogram, "inTangent"); - if (loc >= 0 && loc != gpu::Stream::TANGENT) { - glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent"); - } - - char attribName[] = "inTexCoordn"; - for (auto i = 0; i < 4; i++) { - auto streamId = gpu::Stream::TEXCOORD1 + i; - - attribName[strlen(attribName) - 1] = '1' + i; - loc = glGetAttribLocation(glprogram, attribName); - if (loc >= 0 && loc != streamId) { - glBindAttribLocation(glprogram, streamId, attribName); - } - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterIndex"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex"); - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterWeight"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight"); - } - - loc = glGetAttribLocation(glprogram, "_drawCallInfo"); - if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) { - glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo"); - } - - // Link again to take into account the assigned attrib location - glLinkProgram(glprogram); - - GLint linked = 0; - glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) { - qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; - } -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp deleted file mode 100644 index 4a5c772b8b..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp +++ /dev/null @@ -1,334 +0,0 @@ -// -// GLBackendState.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 3/22/2015. -// Copyright 2014 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 "GLBackend.h" -#include "GLState.h" - -#include - -using namespace gpu; -using namespace gpu::gl; - -void GLBackend::resetPipelineState(State::Signature nextSignature) { - auto currentNotSignature = ~_pipeline._stateSignatureCache; - auto nextNotSignature = ~nextSignature; - auto fieldsToBeReset = currentNotSignature ^ (currentNotSignature | nextNotSignature); - if (fieldsToBeReset.any()) { - for (auto i = 0; i < State::NUM_FIELDS; i++) { - if (fieldsToBeReset[i]) { - GLState::_resetStateCommands[i]->run(this); - _pipeline._stateSignatureCache.reset(i); - } - } - } -} - -void GLBackend::syncPipelineStateCache() { - State::Data state; - - //glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); - qDebug() << "TODO: GLBackendState.cpp:syncPipelineStateCache GL_TEXTURE_CUBE_MAP_SEAMLESS"; - - // Point size is always on - // FIXME CORE - //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); - //glEnable(GL_PROGRAM_POINT_SIZE_EXT); - qDebug() << "TODO: GLBackendState.cpp:syncPipelineStateCache GL_PROGRAM_POINT_SIZE_EXT"; - - //glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); - qDebug() << "TODO: GLBackendState.cpp:syncPipelineStateCache GL_VERTEX_PROGRAM_POINT_SIZE"; - - // Default line width accross the board - glLineWidth(1.0f); - - getCurrentGLState(state); - State::Signature signature = State::evalSignature(state); - - _pipeline._stateCache = state; - _pipeline._stateSignatureCache = signature; -} - - -void GLBackend::do_setStateFillMode(int32 mode) { - if (_pipeline._stateCache.fillMode != mode) { - //static GLenum GL_FILL_MODES[] = { /*GL_POINT, GL_LINE, GL_FILL*/ }; - //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL_MODES[mode]); - qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_POINT"; - qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_LINE"; - qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode GL_FILL"; - qDebug() << "TODO: GLBackendState.cpp:do_setStateFillMode glPolygonMode"; - (void)CHECK_GL_ERROR(); - - _pipeline._stateCache.fillMode = State::FillMode(mode); - } -} - -void GLBackend::do_setStateCullMode(int32 mode) { - if (_pipeline._stateCache.cullMode != mode) { - static GLenum GL_CULL_MODES[] = { GL_FRONT_AND_BACK, GL_FRONT, GL_BACK }; - if (mode == State::CULL_NONE) { - glDisable(GL_CULL_FACE); - glCullFace(GL_FRONT_AND_BACK); - } else { - glEnable(GL_CULL_FACE); - glCullFace(GL_CULL_MODES[mode]); - } - (void)CHECK_GL_ERROR(); - - _pipeline._stateCache.cullMode = State::CullMode(mode); - } -} - -void GLBackend::do_setStateFrontFaceClockwise(bool isClockwise) { - if (_pipeline._stateCache.frontFaceClockwise != isClockwise) { - static GLenum GL_FRONT_FACES[] = { GL_CCW, GL_CW }; - glFrontFace(GL_FRONT_FACES[isClockwise]); - (void)CHECK_GL_ERROR(); - - _pipeline._stateCache.frontFaceClockwise = isClockwise; - } -} - -void GLBackend::do_setStateDepthClampEnable(bool enable) { - if (_pipeline._stateCache.depthClampEnable != enable) { - //if (enable) { - // glEnable(GL_DEPTH_CLAMP); - //} else { - // glDisable(GL_DEPTH_CLAMP); - //} - (void)CHECK_GL_ERROR(); - - _pipeline._stateCache.depthClampEnable = enable; - } -} - -void GLBackend::do_setStateScissorEnable(bool enable) { - if (_pipeline._stateCache.scissorEnable != enable) { - if (enable) { - glEnable(GL_SCISSOR_TEST); - } else { - glDisable(GL_SCISSOR_TEST); - } - (void)CHECK_GL_ERROR(); - - _pipeline._stateCache.scissorEnable = enable; - } -} - -void GLBackend::do_setStateMultisampleEnable(bool enable) { - if (_pipeline._stateCache.multisampleEnable != enable) { -#if !defined(USE_GLES) - if (enable) { - glEnable(GL_MULTISAMPLE); - } else { - glDisable(GL_MULTISAMPLE); - } - (void)CHECK_GL_ERROR(); -#endif - - _pipeline._stateCache.multisampleEnable = enable; - } -} - -void GLBackend::do_setStateAntialiasedLineEnable(bool enable) { - if (_pipeline._stateCache.antialisedLineEnable != enable) { -#if !defined(USE_GLES) - if (enable) { - glEnable(GL_LINE_SMOOTH); - } else { - glDisable(GL_LINE_SMOOTH); - } - (void)CHECK_GL_ERROR(); -#endif - - _pipeline._stateCache.antialisedLineEnable = enable; - } -} - -void GLBackend::do_setStateDepthBias(Vec2 bias) { - if ((bias.x != _pipeline._stateCache.depthBias) || (bias.y != _pipeline._stateCache.depthBiasSlopeScale)) { - if ((bias.x != 0.0f) || (bias.y != 0.0f)) { - glEnable(GL_POLYGON_OFFSET_FILL); -#if !defined(USE_GLES) - glEnable(GL_POLYGON_OFFSET_LINE); - glEnable(GL_POLYGON_OFFSET_POINT); -#endif - glPolygonOffset(bias.x, bias.y); - } else { - glDisable(GL_POLYGON_OFFSET_FILL); -#if !defined(USE_GLES) - glDisable(GL_POLYGON_OFFSET_LINE); - glDisable(GL_POLYGON_OFFSET_POINT); -#endif - } - (void)CHECK_GL_ERROR(); - - _pipeline._stateCache.depthBias = bias.x; - _pipeline._stateCache.depthBiasSlopeScale = bias.y; - } -} - -void GLBackend::do_setStateDepthTest(State::DepthTest test) { - const auto& current = _pipeline._stateCache.depthTest; - if (current != test) { - if (test.isEnabled()) { - glEnable(GL_DEPTH_TEST); - } else { - glDisable(GL_DEPTH_TEST); - } - if (test.getWriteMask() != current.getWriteMask()) { - glDepthMask(test.getWriteMask()); - } - if (test.getFunction() != current.getFunction()) { - glDepthFunc(COMPARISON_TO_GL[test.getFunction()]); - } - if (CHECK_GL_ERROR()) { - qCDebug(gpulogging) << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled") - << "Mask=" << (test.getWriteMask() ? "Write" : "no Write") - << "Func=" << test.getFunction() - << "Raw=" << test.getRaw(); - } - _pipeline._stateCache.depthTest = test; - } -} - -void GLBackend::do_setStateStencil(State::StencilActivation activation, State::StencilTest testFront, State::StencilTest testBack) { - const auto& currentActivation = _pipeline._stateCache.stencilActivation; - const auto& currentTestFront = _pipeline._stateCache.stencilTestFront; - const auto& currentTestBack = _pipeline._stateCache.stencilTestBack; - if ((currentActivation != activation) - || (currentTestFront != testFront) - || (currentTestBack != testBack)) { - - if (activation.isEnabled()) { - glEnable(GL_STENCIL_TEST); - } else { - glDisable(GL_STENCIL_TEST); - } - - if (activation.getWriteMaskFront() != activation.getWriteMaskBack()) { - glStencilMaskSeparate(GL_FRONT, activation.getWriteMaskFront()); - glStencilMaskSeparate(GL_BACK, activation.getWriteMaskBack()); - } else { - glStencilMask(activation.getWriteMaskFront()); - } - - static GLenum STENCIL_OPS[State::NUM_STENCIL_OPS] = { - GL_KEEP, - GL_ZERO, - GL_REPLACE, - GL_INCR_WRAP, - GL_DECR_WRAP, - GL_INVERT, - GL_INCR, - GL_DECR }; - - if (testFront != testBack) { - glStencilOpSeparate(GL_FRONT, STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getDepthFailOp()], STENCIL_OPS[testFront.getPassOp()]); - glStencilFuncSeparate(GL_FRONT, COMPARISON_TO_GL[testFront.getFunction()], testFront.getReference(), testFront.getReadMask()); - - glStencilOpSeparate(GL_BACK, STENCIL_OPS[testBack.getFailOp()], STENCIL_OPS[testBack.getDepthFailOp()], STENCIL_OPS[testBack.getPassOp()]); - glStencilFuncSeparate(GL_BACK, COMPARISON_TO_GL[testBack.getFunction()], testBack.getReference(), testBack.getReadMask()); - } else { - glStencilOp(STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getDepthFailOp()], STENCIL_OPS[testFront.getPassOp()]); - glStencilFunc(COMPARISON_TO_GL[testFront.getFunction()], testFront.getReference(), testFront.getReadMask()); - } - - (void)CHECK_GL_ERROR(); - - _pipeline._stateCache.stencilActivation = activation; - _pipeline._stateCache.stencilTestFront = testFront; - _pipeline._stateCache.stencilTestBack = testBack; - } -} - -void GLBackend::do_setStateAlphaToCoverageEnable(bool enable) { - if (_pipeline._stateCache.alphaToCoverageEnable != enable) { - if (enable) { - glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); - } else { - glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); - } - (void)CHECK_GL_ERROR(); - - _pipeline._stateCache.alphaToCoverageEnable = enable; - } -} - -void GLBackend::do_setStateSampleMask(uint32 mask) { - if (_pipeline._stateCache.sampleMask != mask) { - if (mask == 0xFFFFFFFF) { - glDisable(GL_SAMPLE_MASK); - } else { - glEnable(GL_SAMPLE_MASK); - glSampleMaski(0, mask); - } - (void)CHECK_GL_ERROR(); - _pipeline._stateCache.sampleMask = mask; - } -} - -void GLBackend::do_setStateBlend(State::BlendFunction function) { - if (_pipeline._stateCache.blendFunction != function) { - if (function.isEnabled()) { - glEnable(GL_BLEND); - - glBlendEquationSeparate(BLEND_OPS_TO_GL[function.getOperationColor()], BLEND_OPS_TO_GL[function.getOperationAlpha()]); - (void)CHECK_GL_ERROR(); - - - glBlendFuncSeparate(BLEND_ARGS_TO_GL[function.getSourceColor()], BLEND_ARGS_TO_GL[function.getDestinationColor()], - BLEND_ARGS_TO_GL[function.getSourceAlpha()], BLEND_ARGS_TO_GL[function.getDestinationAlpha()]); - } else { - glDisable(GL_BLEND); - } - (void)CHECK_GL_ERROR(); - - _pipeline._stateCache.blendFunction = function; - } -} - -void GLBackend::do_setStateColorWriteMask(uint32 mask) { - if (_pipeline._stateCache.colorWriteMask != mask) { - glColorMask(mask & State::ColorMask::WRITE_RED, - mask & State::ColorMask::WRITE_GREEN, - mask & State::ColorMask::WRITE_BLUE, - mask & State::ColorMask::WRITE_ALPHA); - (void)CHECK_GL_ERROR(); - - _pipeline._stateCache.colorWriteMask = mask; - } -} - - -void GLBackend::do_setStateBlendFactor(const Batch& batch, size_t paramOffset) { - Vec4 factor(batch._params[paramOffset + 0]._float, - batch._params[paramOffset + 1]._float, - batch._params[paramOffset + 2]._float, - batch._params[paramOffset + 3]._float); - - glBlendColor(factor.x, factor.y, factor.z, factor.w); - (void)CHECK_GL_ERROR(); -} - -void GLBackend::do_setStateScissorRect(const Batch& batch, size_t paramOffset) { - Vec4i rect; - memcpy(&rect, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i)); - - if (_stereo._enable) { - rect.z /= 2; - if (_stereo._pass) { - rect.x += rect.z; - } - } - glScissor(rect.x, rect.y, rect.z, rect.w); - (void)CHECK_GL_ERROR(); -} - diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp deleted file mode 100644 index ace33766ce..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// -// -// GLBackendTexture.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 1/19/2015. -// Copyright 2014 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 "GLBackend.h" -#include "GLTexture.h" - -using namespace gpu; -using namespace gpu::gl; - - -GLuint GLBackend::getTextureID(const TexturePointer& texture) { - GLTexture* object = syncGPUObject(texture); - - if (!object) { - return 0; - } - - return object->_id; -} - -GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { - const Texture& texture = *texturePointer; - // Special case external textures - if (TextureUsageType::EXTERNAL == texture.getUsageType()) { - Texture::ExternalUpdates updates = texture.getUpdates(); - if (!updates.empty()) { - Texture::ExternalRecycler recycler = texture.getExternalRecycler(); - Q_ASSERT(recycler); - // Discard any superfluous updates - while (updates.size() > 1) { - const auto& update = updates.front(); - // Superfluous updates will never have been read, but we want to ensure the previous - // writes to them are complete before they're written again, so return them with the - // same fences they arrived with. This can happen on any thread because no GL context - // work is involved - recycler(update.first, update.second); - updates.pop_front(); - } - - // The last texture remaining is the one we'll use to create the GLTexture - const auto& update = updates.front(); - // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence - if (update.second) { - GLsync fence = static_cast(update.second); - glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync(fence); - } - - // Create the new texture object (replaces any previous texture object) - new GLExternalTexture(shared_from_this(), texture, update.first); - } - - // Return the texture object (if any) associated with the texture, without extensive logic - // (external textures are - return Backend::getGPUObject(texture); - } - - return nullptr; -} - -void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { - TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); - if (!resourceTexture) { - return; - } - - // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(resourceTexture); - if (!object) { - return; - } - - object->generateMips(); -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp deleted file mode 100644 index f286a5cca9..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp +++ /dev/null @@ -1,171 +0,0 @@ -// -// GLBackendTransform.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 3/8/2015. -// Copyright 2014 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 "GLBackend.h" - -using namespace gpu; -using namespace gpu::gl; - -// Transform Stage -void GLBackend::do_setModelTransform(const Batch& batch, size_t paramOffset) { -} - -void GLBackend::do_setViewTransform(const Batch& batch, size_t paramOffset) { - _transform._view = batch._transforms.get(batch._params[paramOffset]._uint); - _transform._viewIsCamera = batch._params[paramOffset + 1]._uint != 0; - _transform._invalidView = true; -} - -void GLBackend::do_setProjectionTransform(const Batch& batch, size_t paramOffset) { - memcpy(&_transform._projection, batch.readData(batch._params[paramOffset]._uint), sizeof(Mat4)); - _transform._invalidProj = true; -} - -void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) { - memcpy(&_transform._viewport, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i)); - -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - { - ivec4& vp = _transform._viewport; - glViewport(vp.x, vp.y, vp.z, vp.w); - - // Where we assign the GL viewport - if (_stereo.isStereo()) { - vp.z /= 2; - if (_stereo._pass) { - vp.x += vp.z; - } - } - } -#else - if (!_inRenderTransferPass && !isStereo()) { - ivec4& vp = _transform._viewport; - glViewport(vp.x, vp.y, vp.z, vp.w); - } -#endif - - // The Viewport is tagged invalid because the CameraTransformUBO is not up to date and will need update on next drawcall - _transform._invalidViewport = true; -} - -void GLBackend::do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) { - - Vec2 depthRange(batch._params[paramOffset + 1]._float, batch._params[paramOffset + 0]._float); - - if ((depthRange.x != _transform._depthRange.x) || (depthRange.y != _transform._depthRange.y)) { - _transform._depthRange = depthRange; - - glDepthRangef(depthRange.x, depthRange.y); - } -} - -void GLBackend::killTransform() { - glDeleteBuffers(1, &_transform._objectBuffer); - glDeleteBuffers(1, &_transform._cameraBuffer); - glDeleteBuffers(1, &_transform._drawCallInfoBuffer); - glDeleteTextures(1, &_transform._objectBufferTexture); -} - -void GLBackend::syncTransformStateCache() { - _transform._invalidViewport = true; - _transform._invalidProj = true; - _transform._invalidView = true; - - glGetIntegerv(GL_VIEWPORT, (GLint*) &_transform._viewport); - - glGetFloatv(GL_DEPTH_RANGE, (GLfloat*)&_transform._depthRange); - - Mat4 modelView; - auto modelViewInv = glm::inverse(modelView); - _transform._view.evalFromRawMatrix(modelViewInv); - - glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); - _transform._enabledDrawcallInfoBuffer = false; -} - -void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo) { - // Check all the dirty flags and update the state accordingly - if (_invalidViewport) { - _camera._viewport = glm::vec4(_viewport); - } - - if (_invalidProj) { - _camera._projection = _projection; - } - - if (_invalidView) { - // Apply the correction - if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) { - // FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out? - Transform result; - _view.mult(result, _view, _correction.correction); - if (_skybox) { - result.setTranslation(vec3()); - } - _view = result; - } - // This is when the _view matrix gets assigned - _view.getInverseMatrix(_camera._view); - } - - if (_invalidView || _invalidProj || _invalidViewport) { - size_t offset = _cameraUboSize * _cameras.size(); - _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); - - if (stereo.isStereo()) { -#ifdef GPU_STEREO_CAMERA_BUFFER - _cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view))); -#else - _cameras.push_back((_camera.getEyeCamera(0, stereo, _view))); - _cameras.push_back((_camera.getEyeCamera(1, stereo, _view))); -#endif - } else { -#ifdef GPU_STEREO_CAMERA_BUFFER - _cameras.push_back(CameraBufferElement(_camera.recomputeDerived(_view))); -#else - _cameras.push_back((_camera.recomputeDerived(_view))); -#endif - } - } - - // Flags are clean - _invalidView = _invalidProj = _invalidViewport = false; -} - -void GLBackend::TransformStageState::update(size_t commandIndex, const StereoState& stereo) const { - size_t offset = INVALID_OFFSET; - while ((_camerasItr != _cameraOffsets.end()) && (commandIndex >= (*_camerasItr).first)) { - offset = (*_camerasItr).second; - _currentCameraOffset = offset; - ++_camerasItr; - } - - if (offset != INVALID_OFFSET) { -#ifdef GPU_STEREO_CAMERA_BUFFER - bindCurrentCamera(0); -#else - if (!stereo.isStereo()) { - bindCurrentCamera(0); - } -#endif - } - (void)CHECK_GL_ERROR(); -} - -void GLBackend::TransformStageState::bindCurrentCamera(int eye) const { - if (_currentCameraOffset != INVALID_OFFSET) { - glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _cameraBuffer, _currentCameraOffset + eye * _cameraUboSize, sizeof(CameraBufferElement)); - } -} - -void GLBackend::resetTransformStage() { - glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); - _transform._enabledDrawcallInfoBuffer = false; -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp b/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp deleted file mode 100644 index 4f7d0a8632..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBuffer.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// -// Created by Gabriel Calero & Cristian Duarte on 09/27/2016 -// Copyright 2013-2016 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 "GLBuffer.h" -#include "GLBackend.h" - -using namespace gpu; -using namespace gpu::gl; - -GLBuffer::~GLBuffer() { - Backend::bufferCount.decrement(); - Backend::bufferGPUMemSize.update(_size, 0); - - if (_id) { - auto backend = _backend.lock(); - if (backend) { - backend->releaseBuffer(_id, _size); - } - } -} - -GLBuffer::GLBuffer(const std::weak_ptr& backend, const Buffer& buffer, GLuint id) : - GLObject(backend, buffer, id), - _size((GLuint)buffer._renderSysmem.getSize()), - _stamp(buffer._renderSysmem.getStamp()) -{ - Backend::bufferCount.increment(); - Backend::bufferGPUMemSize.update(0, _size); -} - diff --git a/libraries/gpu-gles/src/gpu/gl/GLBuffer.h b/libraries/gpu-gles/src/gpu/gl/GLBuffer.h deleted file mode 100644 index 182014e764..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLBuffer.h +++ /dev/null @@ -1,66 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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_gpu_gl_GLBuffer_h -#define hifi_gpu_gl_GLBuffer_h - -#include "GLShared.h" -#include "GLBackend.h" - -namespace gpu { namespace gl { - -class GLBuffer : public GLObject { -public: - template - static GLBufferType* sync(GLBackend& backend, const Buffer& buffer) { - if (buffer.getSysmem().getSize() != 0) { - if (buffer._getUpdateCount == 0) { - qWarning() << "Unsynced buffer"; - } - if (buffer._getUpdateCount < buffer._applyUpdateCount) { - qWarning() << "Unsynced buffer " << buffer._getUpdateCount << " " << buffer._applyUpdateCount; - } - } - GLBufferType* object = Backend::getGPUObject(buffer); - - // Has the storage size changed? - if (!object || object->_stamp != buffer._renderSysmem.getStamp()) { - object = new GLBufferType(backend.shared_from_this(), buffer, object); - } - - if (0 != (buffer._renderPages._flags & PageManager::DIRTY)) { - object->transfer(); - } - - return object; - } - - template - static GLuint getId(GLBackend& backend, const Buffer& buffer) { - GLBuffer* bo = sync(backend, buffer); - if (bo) { - return bo->_buffer; - } else { - return 0; - } - } - - const GLuint& _buffer { _id }; - const GLuint _size; - const Stamp _stamp; - - ~GLBuffer(); - - virtual void transfer() = 0; - -protected: - GLBuffer(const std::weak_ptr& backend, const Buffer& buffer, GLuint id); -}; - -} } - -#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLESBackend.cpp b/libraries/gpu-gles/src/gpu/gl/GLESBackend.cpp new file mode 100644 index 0000000000..2e2c988e77 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLESBackend.cpp @@ -0,0 +1,56 @@ +// +// GLBackend.cpp +// libraries/gpu-gl-android/src/gpu/gl +// +// Created by Cristian Duarte & Gabriel Calero on 9/21/2016. +// Copyright 2016 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 + +#include + + +#include +#include +#include +#include + +#include "../gles/GLESBackend.h" + +using namespace gpu; +using namespace gpu::gl; + +static GLBackend* INSTANCE{ nullptr }; + +BackendPointer GLBackend::createBackend() { + // FIXME provide a mechanism to override the backend for testing + // Where the gpuContext is initialized and where the TRUE Backend is created and assigned + //auto version = QOpenGLContextWrapper::currentContextVersion(); + std::shared_ptr result; + + qDebug() << "Using OpenGL ES backend"; + result = std::make_shared(); + + result->initInput(); + result->initTransform(); + result->initTextureManagementStage(); + + INSTANCE = result.get(); + void* voidInstance = &(*result); + qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); + return result; +} + +GLBackend& getBackend() { + if (!INSTANCE) { + INSTANCE = static_cast(qApp->property(hifi::properties::gl::BACKEND).value()); + } + return *INSTANCE; +} + +bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { + return GLShader::makeProgram(getBackend(), shader, slotBindings, handler); +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp deleted file mode 100644 index 0bca9e86d9..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// -// Created by Gabriel Calero & Cristian Duarte on 09/27/2016 -// Copyright 2016 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 "GLFramebuffer.h" -#include "GLBackend.h" - -using namespace gpu; -using namespace gpu::gl; - -GLFramebuffer::~GLFramebuffer() { - if (_id) { - auto backend = _backend.lock(); - if (backend) { - backend->releaseFramebuffer(_id); - } - } -} - -bool GLFramebuffer::checkStatus() const { - switch (_status) { - case GL_FRAMEBUFFER_COMPLETE: - // Success ! - return true; - - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT."; - break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT."; - break; - //case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - // qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER."; - // break; - //case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - // qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER."; - // break; - case GL_FRAMEBUFFER_UNSUPPORTED: - qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; - break; - } - return false; -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h deleted file mode 100644 index 5a388e1965..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h +++ /dev/null @@ -1,77 +0,0 @@ -// -// Created by Gabriel Calero & Cristian Duarte on 09/27/2016 -// Copyright 2016 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_gpu_gl_GLFramebuffer_h -#define hifi_gpu_gl_GLFramebuffer_h - -#include "GLShared.h" -#include "GLBackend.h" - -namespace gpu { namespace gl { - -class GLFramebuffer : public GLObject { -public: - template - static GLFramebufferType* sync(GLBackend& backend, const Framebuffer& framebuffer) { - GLFramebufferType* object = Backend::getGPUObject(framebuffer); - - bool needsUpate { false }; - if (!object || - framebuffer.getDepthStamp() != object->_depthStamp || - framebuffer.getColorStamps() != object->_colorStamps) { - needsUpate = true; - } - - // If GPU object already created and in sync - if (!needsUpate) { - return object; - } else if (framebuffer.isEmpty()) { - // NO framebuffer definition yet so let's avoid thinking - return nullptr; - } - - // need to have a gpu object? - if (!object) { - // All is green, assign the gpuobject to the Framebuffer - object = new GLFramebufferType(backend.shared_from_this(), framebuffer); - Backend::setGPUObject(framebuffer, object); - (void)CHECK_GL_ERROR(); - } - - object->update(); - return object; - } - - template - static GLuint getId(GLBackend& backend, const Framebuffer& framebuffer) { - GLFramebufferType* fbo = sync(backend, framebuffer); - if (fbo) { - return fbo->_id; - } else { - return 0; - } - } - - const GLuint& _fbo { _id }; - std::vector _colorBuffers; - Stamp _depthStamp { 0 }; - std::vector _colorStamps; - -protected: - GLenum _status { GL_FRAMEBUFFER_COMPLETE }; - virtual void update() = 0; - bool checkStatus() const; - - GLFramebuffer(const std::weak_ptr& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {} - ~GLFramebuffer(); - -}; - -} } - - -#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLInputFormat.cpp b/libraries/gpu-gles/src/gpu/gl/GLInputFormat.cpp deleted file mode 100644 index 7f42350c3b..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLInputFormat.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// -// Created by Sam Gateau on 2016/07/21 -// Copyright 2013-2016 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 "GLInputFormat.h" -#include "GLBackend.h" - -using namespace gpu; -using namespace gpu::gl; - - -GLInputFormat::GLInputFormat() { -} - -GLInputFormat:: ~GLInputFormat() { - -} - -GLInputFormat* GLInputFormat::sync(const Stream::Format& inputFormat) { - GLInputFormat* object = Backend::getGPUObject(inputFormat); - - if (!object) { - object = new GLInputFormat(); - object->key = inputFormat.getKey(); - Backend::setGPUObject(inputFormat, object); - } - - return object; -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLInputFormat.h b/libraries/gpu-gles/src/gpu/gl/GLInputFormat.h deleted file mode 100644 index a14e3d4d91..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLInputFormat.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// Created by Sam Gateau on 2016/07/21 -// Copyright 2013-2016 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_gpu_gl_GLInputFormat_h -#define hifi_gpu_gl_GLInputFormat_h - -#include "GLShared.h" - -namespace gpu { -namespace gl { - -class GLInputFormat : public GPUObject { - public: - static GLInputFormat* sync(const Stream::Format& inputFormat); - - GLInputFormat(); - ~GLInputFormat(); - - std::string key; -}; - -} -} - -#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp b/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp deleted file mode 100644 index ebf1a55232..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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 "GLPipeline.h" - -#include "GLShader.h" -#include "GLState.h" - -using namespace gpu; -using namespace gpu::gl; - -GLPipeline* GLPipeline::sync(GLBackend& backend, const Pipeline& pipeline) { - GLPipeline* object = Backend::getGPUObject(pipeline); - - // If GPU object already created then good - if (object) { - return object; - } - - // No object allocated yet, let's see if it's worth it... - ShaderPointer shader = pipeline.getProgram(); - - // If this pipeline's shader has already failed to compile, don't try again - if (shader->compilationHasFailed()) { - return nullptr; - } - - GLShader* programObject = GLShader::sync(backend, *shader); - if (programObject == nullptr) { - shader->setCompilationHasFailed(true); - return nullptr; - } - - StatePointer state = pipeline.getState(); - GLState* stateObject = GLState::sync(*state); - if (stateObject == nullptr) { - return nullptr; - } - - // Program and state are valid, we can create the pipeline object - if (!object) { - object = new GLPipeline(); - Backend::setGPUObject(pipeline, object); - } - - // Special case for view correction matrices, any pipeline that declares the correction buffer - // uniform will automatically have it provided without any client code necessary. - // Required for stable lighting in the HMD. - object->_cameraCorrection = shader->getUniformBuffers().findLocation("cameraCorrectionBuffer"); - object->_program = programObject; - object->_state = stateObject; - - return object; -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLPipeline.h b/libraries/gpu-gles/src/gpu/gl/GLPipeline.h deleted file mode 100644 index a298f149d9..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLPipeline.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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_gpu_gl_GLPipeline_h -#define hifi_gpu_gl_GLPipeline_h - -#include "GLShared.h" - -namespace gpu { namespace gl { - -class GLPipeline : public GPUObject { -public: - static GLPipeline* sync(GLBackend& backend, const Pipeline& pipeline); - - GLShader* _program { nullptr }; - GLState* _state { nullptr }; - // Bit of a hack, any pipeline can need the camera correction buffer at execution time, so - // we store whether a given pipeline has declared the uniform buffer for it. - int32 _cameraCorrection { -1 }; -}; - -} } - - -#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLQuery.h b/libraries/gpu-gles/src/gpu/gl/GLQuery.h deleted file mode 100644 index 23b1f38621..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLQuery.h +++ /dev/null @@ -1,67 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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_gpu_gl_GLQuery_h -#define hifi_gpu_gl_GLQuery_h - -#include "GLShared.h" -#include "GLBackend.h" - -namespace gpu { namespace gl { - -class GLQuery : public GLObject { - using Parent = gpu::gl::GLObject; -public: - template - static GLQueryType* sync(GLBackend& backend, const Query& query) { - GLQueryType* object = Backend::getGPUObject(query); - - // need to have a gpu object? - if (!object) { - // All is green, assign the gpuobject to the Query - object = new GLQueryType(backend.shared_from_this(), query); - (void)CHECK_GL_ERROR(); - Backend::setGPUObject(query, object); - } - - return object; - } - - template - static GLuint getId(GLBackend& backend, const QueryPointer& query) { - if (!query) { - return 0; - } - - GLQuery* object = sync(backend, *query); - if (!object) { - return 0; - } - - return object->_endqo; - } - - const GLuint& _endqo = { _id }; - const GLuint _beginqo = { 0 }; - GLuint64 _result { (GLuint64)-1 }; - GLuint64 _batchElapsedTime { (GLuint64) 0 }; - uint64_t _profileRangeId { 0 }; - uint32_t _rangeQueryDepth { 0 }; - -protected: - GLQuery(const std::weak_ptr& backend, const Query& query, GLuint endId, GLuint beginId) : Parent(backend, query, endId), _beginqo(beginId) {} - ~GLQuery() { - if (_id) { - GLuint ids[2] = { _endqo, _beginqo }; - glDeleteQueries(2, ids); - } - } -}; - -} } - -#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp deleted file mode 100644 index 010a7c479c..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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 "GLShader.h" -#include - -#include "GLBackend.h" - -using namespace gpu; -using namespace gpu::gl; - -GLShader::GLShader(const std::weak_ptr& backend) : _backend(backend) { -} - -GLShader::~GLShader() { - for (auto& so : _shaderObjects) { - auto backend = _backend.lock(); - if (backend) { - if (so.glshader != 0) { - backend->releaseShader(so.glshader); - } - if (so.glprogram != 0) { - backend->releaseProgram(so.glprogram); - } - } - } -} - -GLShader* GLShader::sync(GLBackend& backend, const Shader& shader, const Shader::CompilationHandler& handler) { - GLShader* object = Backend::getGPUObject(shader); - - // If GPU object already created then good - if (object) { - return object; - } - // need to have a gpu object? - if (shader.isProgram()) { - GLShader* tempObject = backend.compileBackendProgram(shader, handler); - if (tempObject) { - object = tempObject; - Backend::setGPUObject(shader, object); - } - } else if (shader.isDomain()) { - GLShader* tempObject = backend.compileBackendShader(shader, handler); - if (tempObject) { - object = tempObject; - Backend::setGPUObject(shader, object); - } - } - - glFinish(); - return object; -} - -bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler) { - - // First make sure the Shader has been compiled - GLShader* object = sync(backend, shader, handler); - if (!object) { - return false; - } - - // Apply bindings to all program versions and generate list of slots from default version - for (int version = 0; version < GLShader::NumVersions; version++) { - auto& shaderObject = object->_shaderObjects[version]; - if (shaderObject.glprogram) { - Shader::SlotSet buffers; - backend.makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers); - - Shader::SlotSet uniforms; - Shader::SlotSet textures; - Shader::SlotSet samplers; - backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers); - - Shader::SlotSet resourceBuffers; - backend.makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers); - - Shader::SlotSet inputs; - backend.makeInputSlots(shaderObject.glprogram, slotBindings, inputs); - - Shader::SlotSet outputs; - backend.makeOutputSlots(shaderObject.glprogram, slotBindings, outputs); - - // Define the public slots only from the default version - if (version == 0) { - shader.defineSlots(uniforms, buffers, resourceBuffers, textures, samplers, inputs, outputs); - } // else - { - GLShader::UniformMapping mapping; - for (auto srcUniform : shader.getUniforms()) { - mapping[srcUniform._location] = uniforms.findLocation(srcUniform._name); - } - object->_uniformMappings.push_back(mapping); - } - } - } - - return true; -} - - - diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.h b/libraries/gpu-gles/src/gpu/gl/GLShader.h deleted file mode 100644 index f2a144a81c..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.h +++ /dev/null @@ -1,66 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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_gpu_gl_GLShader_h -#define hifi_gpu_gl_GLShader_h - -#include "GLShared.h" - -namespace gpu { namespace gl { - -struct ShaderObject { - GLuint glshader { 0 }; - GLuint glprogram { 0 }; - GLint transformCameraSlot { -1 }; - GLint transformObjectSlot { -1 }; -}; - -class GLShader : public GPUObject { -public: - static GLShader* sync(GLBackend& backend, const Shader& shader, const Shader::CompilationHandler& handler = nullptr); - static bool makeProgram(GLBackend& backend, Shader& shader, const Shader::BindingSet& slotBindings, const Shader::CompilationHandler& handler = nullptr); - - enum Version { - Mono = 0, - Stereo, - - NumVersions - }; - - using ShaderObject = gpu::gl::ShaderObject; - using ShaderObjects = std::array< ShaderObject, NumVersions >; - - using UniformMapping = std::map; - using UniformMappingVersions = std::vector; - - GLShader(const std::weak_ptr& backend); - ~GLShader(); - - ShaderObjects _shaderObjects; - UniformMappingVersions _uniformMappings; - - GLuint getProgram(Version version = Mono) const { - return _shaderObjects[version].glprogram; - } - - GLint getUniformLocation(GLint srcLoc, Version version = Mono) const { - // This check protect against potential invalid src location for this shader, if unknown then return -1. - const auto& mapping = _uniformMappings[version]; - auto found = mapping.find(srcLoc); - if (found == mapping.end()) { - return -1; - } - return found->second; - } - - const std::weak_ptr _backend; -}; - -} } - - -#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLShared.cpp b/libraries/gpu-gles/src/gpu/gl/GLShared.cpp deleted file mode 100644 index f818a221b2..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLShared.cpp +++ /dev/null @@ -1,335 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/14 -// Copyright 2013-2016 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 "GLShared.h" - -#include - -#include - -#include -#include -#include - -Q_LOGGING_CATEGORY(gpugllogging, "hifi.gpu.gl") -Q_LOGGING_CATEGORY(trace_render_gpu_gl, "trace.render.gpu.gl") -Q_LOGGING_CATEGORY(trace_render_gpu_gl_detail, "trace.render.gpu.gl.detail") - -namespace gpu { namespace gl { - -bool checkGLError(const char* name) { - GLenum error = glGetError(); - if (!error) { - return false; - } else { - switch (error) { - case GL_INVALID_ENUM: - qCWarning(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; - break; - case GL_INVALID_VALUE: - qCWarning(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag"; - break; - case GL_INVALID_OPERATION: - qCWarning(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag.."; - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - qCWarning(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag."; - break; - case GL_OUT_OF_MEMORY: - qCWarning(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded."; - break; - case GL_STACK_UNDERFLOW: - qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to underflow."; - break; - case GL_STACK_OVERFLOW: - qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to overflow."; - break; - } - return true; - } -} - -bool checkGLErrorDebug(const char* name) { - // FIXME, disable in debug mode when near release - return checkGLError(name); -} - -gpu::Size getFreeDedicatedMemory() { - Size result { 0 }; - static bool nvidiaMemorySupported { false }; - static bool atiMemorySupported { false }; - if (nvidiaMemorySupported) { - - GLint nvGpuMemory { 0 }; - qDebug() << "TODO: GLShared.cpp getFreeDedicatedMemory GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX"; - //glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &nvGpuMemory); - if (GL_NO_ERROR == glGetError()) { - result = KB_TO_BYTES(nvGpuMemory); - } else { - nvidiaMemorySupported = false; - } - } else if (atiMemorySupported) { - GLint atiGpuMemory[4]; - qDebug() << "TODO: GLShared.cpp getFreeDedicatedMemory GL_TEXTURE_FREE_MEMORY_ATI"; - // not really total memory, but close enough if called early enough in the application lifecycle - //glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, atiGpuMemory); - if (GL_NO_ERROR == glGetError()) { - result = KB_TO_BYTES(atiGpuMemory[0]); - } else { - atiMemorySupported = false; - } - } - return result; -} - -ComparisonFunction comparisonFuncFromGL(GLenum func) { - if (func == GL_NEVER) { - return NEVER; - } else if (func == GL_LESS) { - return LESS; - } else if (func == GL_EQUAL) { - return EQUAL; - } else if (func == GL_LEQUAL) { - return LESS_EQUAL; - } else if (func == GL_GREATER) { - return GREATER; - } else if (func == GL_NOTEQUAL) { - return NOT_EQUAL; - } else if (func == GL_GEQUAL) { - return GREATER_EQUAL; - } else if (func == GL_ALWAYS) { - return ALWAYS; - } - - return ALWAYS; -} - -State::StencilOp stencilOpFromGL(GLenum stencilOp) { - if (stencilOp == GL_KEEP) { - return State::STENCIL_OP_KEEP; - } else if (stencilOp == GL_ZERO) { - return State::STENCIL_OP_ZERO; - } else if (stencilOp == GL_REPLACE) { - return State::STENCIL_OP_REPLACE; - } else if (stencilOp == GL_INCR_WRAP) { - return State::STENCIL_OP_INCR_SAT; - } else if (stencilOp == GL_DECR_WRAP) { - return State::STENCIL_OP_DECR_SAT; - } else if (stencilOp == GL_INVERT) { - return State::STENCIL_OP_INVERT; - } else if (stencilOp == GL_INCR) { - return State::STENCIL_OP_INCR; - } else if (stencilOp == GL_DECR) { - return State::STENCIL_OP_DECR; - } - - return State::STENCIL_OP_KEEP; -} - -State::BlendOp blendOpFromGL(GLenum blendOp) { - if (blendOp == GL_FUNC_ADD) { - return State::BLEND_OP_ADD; - } else if (blendOp == GL_FUNC_SUBTRACT) { - return State::BLEND_OP_SUBTRACT; - } else if (blendOp == GL_FUNC_REVERSE_SUBTRACT) { - return State::BLEND_OP_REV_SUBTRACT; - } else if (blendOp == GL_MIN) { - return State::BLEND_OP_MIN; - } else if (blendOp == GL_MAX) { - return State::BLEND_OP_MAX; - } - - return State::BLEND_OP_ADD; -} - -State::BlendArg blendArgFromGL(GLenum blendArg) { - if (blendArg == GL_ZERO) { - return State::ZERO; - } else if (blendArg == GL_ONE) { - return State::ONE; - } else if (blendArg == GL_SRC_COLOR) { - return State::SRC_COLOR; - } else if (blendArg == GL_ONE_MINUS_SRC_COLOR) { - return State::INV_SRC_COLOR; - } else if (blendArg == GL_DST_COLOR) { - return State::DEST_COLOR; - } else if (blendArg == GL_ONE_MINUS_DST_COLOR) { - return State::INV_DEST_COLOR; - } else if (blendArg == GL_SRC_ALPHA) { - return State::SRC_ALPHA; - } else if (blendArg == GL_ONE_MINUS_SRC_ALPHA) { - return State::INV_SRC_ALPHA; - } else if (blendArg == GL_DST_ALPHA) { - return State::DEST_ALPHA; - } else if (blendArg == GL_ONE_MINUS_DST_ALPHA) { - return State::INV_DEST_ALPHA; - } else if (blendArg == GL_CONSTANT_COLOR) { - return State::FACTOR_COLOR; - } else if (blendArg == GL_ONE_MINUS_CONSTANT_COLOR) { - return State::INV_FACTOR_COLOR; - } else if (blendArg == GL_CONSTANT_ALPHA) { - return State::FACTOR_ALPHA; - } else if (blendArg == GL_ONE_MINUS_CONSTANT_ALPHA) { - return State::INV_FACTOR_ALPHA; - } - - return State::ONE; -} - -void getCurrentGLState(State::Data& state) { - { - //GLint modes[2]; - //glGetIntegerv(GL_POLYGON_MODE, modes); - //if (modes[0] == GL_FILL) { - // state.fillMode = State::FILL_FACE; - //} else { - // if (modes[0] == GL_LINE) { - // state.fillMode = State::FILL_LINE; - // } else { - // state.fillMode = State::FILL_POINT; - // } - //} - } - { - if (glIsEnabled(GL_CULL_FACE)) { - GLint mode; - glGetIntegerv(GL_CULL_FACE_MODE, &mode); - state.cullMode = (mode == GL_FRONT ? State::CULL_FRONT : State::CULL_BACK); - } else { - state.cullMode = State::CULL_NONE; - } - } - { - GLint winding; - glGetIntegerv(GL_FRONT_FACE, &winding); - state.frontFaceClockwise = (winding == GL_CW); - state.depthClampEnable = false; //glIsEnabled(GL_DEPTH_CLAMP_EXT); - state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST); - state.multisampleEnable = false; //glIsEnabled(GL_MULTISAMPLE_EXT); - state.antialisedLineEnable = false; //glIsEnabled(GL_LINE_SMOOTH); - } - { - if (glIsEnabled(GL_POLYGON_OFFSET_FILL)) { - glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &state.depthBiasSlopeScale); - glGetFloatv(GL_POLYGON_OFFSET_UNITS, &state.depthBias); - } - } - { - GLboolean isEnabled = glIsEnabled(GL_DEPTH_TEST); - GLboolean writeMask; - glGetBooleanv(GL_DEPTH_WRITEMASK, &writeMask); - GLint func; - glGetIntegerv(GL_DEPTH_FUNC, &func); - - state.depthTest = State::DepthTest(isEnabled, writeMask, comparisonFuncFromGL(func)); - } - { - GLboolean isEnabled = glIsEnabled(GL_STENCIL_TEST); - - GLint frontWriteMask; - GLint frontReadMask; - GLint frontRef; - GLint frontFail; - GLint frontDepthFail; - GLint frontPass; - GLint frontFunc; - glGetIntegerv(GL_STENCIL_WRITEMASK, &frontWriteMask); - glGetIntegerv(GL_STENCIL_VALUE_MASK, &frontReadMask); - glGetIntegerv(GL_STENCIL_REF, &frontRef); - glGetIntegerv(GL_STENCIL_FAIL, &frontFail); - glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &frontDepthFail); - glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &frontPass); - glGetIntegerv(GL_STENCIL_FUNC, &frontFunc); - - GLint backWriteMask; - GLint backReadMask; - GLint backRef; - GLint backFail; - GLint backDepthFail; - GLint backPass; - GLint backFunc; - glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &backWriteMask); - glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &backReadMask); - glGetIntegerv(GL_STENCIL_BACK_REF, &backRef); - glGetIntegerv(GL_STENCIL_BACK_FAIL, &backFail); - glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &backDepthFail); - glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &backPass); - glGetIntegerv(GL_STENCIL_BACK_FUNC, &backFunc); - - state.stencilActivation = State::StencilActivation(isEnabled, frontWriteMask, backWriteMask); - state.stencilTestFront = State::StencilTest(frontRef, frontReadMask, comparisonFuncFromGL(frontFunc), stencilOpFromGL(frontFail), stencilOpFromGL(frontDepthFail), stencilOpFromGL(frontPass)); - state.stencilTestBack = State::StencilTest(backRef, backReadMask, comparisonFuncFromGL(backFunc), stencilOpFromGL(backFail), stencilOpFromGL(backDepthFail), stencilOpFromGL(backPass)); - } - { - GLint mask = 0xFFFFFFFF; - if (glIsEnabled(GL_SAMPLE_MASK)) { - glGetIntegerv(GL_SAMPLE_MASK, &mask); - state.sampleMask = mask; - } - state.sampleMask = mask; - } - { - state.alphaToCoverageEnable = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE); - } - { - GLboolean isEnabled = glIsEnabled(GL_BLEND); - GLint srcRGB; - GLint srcA; - GLint dstRGB; - GLint dstA; - glGetIntegerv(GL_BLEND_SRC_RGB, &srcRGB); - glGetIntegerv(GL_BLEND_SRC_ALPHA, &srcA); - glGetIntegerv(GL_BLEND_DST_RGB, &dstRGB); - glGetIntegerv(GL_BLEND_DST_ALPHA, &dstA); - - GLint opRGB; - GLint opA; - glGetIntegerv(GL_BLEND_EQUATION_RGB, &opRGB); - glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &opA); - - state.blendFunction = State::BlendFunction(isEnabled, - blendArgFromGL(srcRGB), blendOpFromGL(opRGB), blendArgFromGL(dstRGB), - blendArgFromGL(srcA), blendOpFromGL(opA), blendArgFromGL(dstA)); - } - { - GLboolean mask[4]; - glGetBooleanv(GL_COLOR_WRITEMASK, mask); - state.colorWriteMask = (mask[0] ? State::WRITE_RED : 0) - | (mask[1] ? State::WRITE_GREEN : 0) - | (mask[2] ? State::WRITE_BLUE : 0) - | (mask[3] ? State::WRITE_ALPHA : 0); - } - - (void)CHECK_GL_ERROR(); -} - - -void serverWait() { - auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - assert(fence); - glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); - glDeleteSync(fence); -} - -void clientWait() { - auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - assert(fence); - auto result = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0); - while (GL_TIMEOUT_EXPIRED == result || GL_WAIT_FAILED == result) { - // Minimum sleep - QThread::usleep(1); - result = glClientWaitSync(fence, 0, 0); - } - glDeleteSync(fence); -} - -} } - - -using namespace gpu; - - diff --git a/libraries/gpu-gles/src/gpu/gl/GLShared.h b/libraries/gpu-gles/src/gpu/gl/GLShared.h deleted file mode 100644 index 1341dd16fa..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLShared.h +++ /dev/null @@ -1,152 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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_gpu_GLShared_h -#define hifi_gpu_GLShared_h - -#include -#include - -#include -#include -#include -#include - - -Q_DECLARE_LOGGING_CATEGORY(gpugllogging) -Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl) -Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl_detail) - -#define BUFFER_OFFSET(bytes) ((GLubyte*) nullptr + (bytes)) - -namespace gpu { namespace gl { - -// Create a fence and inject a GPU wait on the fence -void serverWait(); - -// Create a fence and synchronously wait on the fence -void clientWait(); - -gpu::Size getFreeDedicatedMemory(); -ComparisonFunction comparisonFuncFromGL(GLenum func); -State::StencilOp stencilOpFromGL(GLenum stencilOp); -State::BlendOp blendOpFromGL(GLenum blendOp); -State::BlendArg blendArgFromGL(GLenum blendArg); -void getCurrentGLState(State::Data& state); - -enum GLSyncState { - // The object is currently undergoing no processing, although it's content - // may be out of date, or it's storage may be invalid relative to the - // owning GPU object - Idle, - // The object has been queued for transfer to the GPU - Pending, - // The object has been transferred to the GPU, but is awaiting - // any post transfer operations that may need to occur on the - // primary rendering thread - Transferred, -}; - -static const GLenum BLEND_OPS_TO_GL[State::NUM_BLEND_OPS] = { - GL_FUNC_ADD, - GL_FUNC_SUBTRACT, - GL_FUNC_REVERSE_SUBTRACT, - GL_MIN, - GL_MAX -}; - -static const GLenum BLEND_ARGS_TO_GL[State::NUM_BLEND_ARGS] = { - GL_ZERO, - GL_ONE, - GL_SRC_COLOR, - GL_ONE_MINUS_SRC_COLOR, - GL_SRC_ALPHA, - GL_ONE_MINUS_SRC_ALPHA, - GL_DST_ALPHA, - GL_ONE_MINUS_DST_ALPHA, - GL_DST_COLOR, - GL_ONE_MINUS_DST_COLOR, - GL_SRC_ALPHA_SATURATE, - GL_CONSTANT_COLOR, - GL_ONE_MINUS_CONSTANT_COLOR, - GL_CONSTANT_ALPHA, - GL_ONE_MINUS_CONSTANT_ALPHA, -}; - -static const GLenum COMPARISON_TO_GL[gpu::NUM_COMPARISON_FUNCS] = { - GL_NEVER, - GL_LESS, - GL_EQUAL, - GL_LEQUAL, - GL_GREATER, - GL_NOTEQUAL, - GL_GEQUAL, - GL_ALWAYS -}; - -static const GLenum PRIMITIVE_TO_GL[gpu::NUM_PRIMITIVES] = { - GL_POINTS, - GL_LINES, - GL_LINE_STRIP, - GL_TRIANGLES, - GL_TRIANGLE_STRIP, - GL_TRIANGLE_FAN, -}; - -static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = { - GL_FLOAT, - GL_INT, - GL_UNSIGNED_INT, - GL_HALF_FLOAT, - GL_SHORT, - GL_UNSIGNED_SHORT, - GL_BYTE, - GL_UNSIGNED_BYTE, - // Normalized values - GL_INT, - GL_UNSIGNED_INT, - GL_SHORT, - GL_UNSIGNED_SHORT, - GL_BYTE, - GL_UNSIGNED_BYTE, - GL_UNSIGNED_BYTE, - GL_INT_2_10_10_10_REV, -}; - -bool checkGLError(const char* name = nullptr); -bool checkGLErrorDebug(const char* name = nullptr); - -class GLBackend; - -template -struct GLObject : public GPUObject { -public: - GLObject(const std::weak_ptr& backend, const GPUType& gpuObject, GLuint id) : _gpuObject(gpuObject), _id(id), _backend(backend) {} - - virtual ~GLObject() { } - - const GPUType& _gpuObject; - const GLuint _id; -protected: - const std::weak_ptr _backend; -}; - -class GlBuffer; -class GLFramebuffer; -class GLPipeline; -class GLQuery; -class GLState; -class GLShader; -class GLTexture; -struct ShaderObject; - -} } // namespace gpu::gl - -#endif - - - diff --git a/libraries/gpu-gles/src/gpu/gl/GLState.cpp b/libraries/gpu-gles/src/gpu/gl/GLState.cpp deleted file mode 100644 index b6d917b928..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLState.cpp +++ /dev/null @@ -1,248 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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 -// - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#if __GNUC__ >= 5 && __GNUC_MINOR__ >= 1 -#pragma GCC diagnostic ignored "-Wsuggest-override" -#endif -#endif - - -#include "GLState.h" - -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif - - -#include "GLBackend.h" - -using namespace gpu; -using namespace gpu::gl; - -typedef GLState::Command Command; -typedef GLState::CommandPointer CommandPointer; -typedef GLState::Command1 Command1U; -typedef GLState::Command1 Command1I; -typedef GLState::Command1 Command1B; -typedef GLState::Command1 CommandDepthBias; -typedef GLState::Command1 CommandDepthTest; -typedef GLState::Command3 CommandStencil; -typedef GLState::Command1 CommandBlend; - -const GLState::Commands makeResetStateCommands(); - -// NOTE: This must stay in sync with the ordering of the State::Field enum -const GLState::Commands makeResetStateCommands() { - // Since State::DEFAULT is a static defined in another .cpp the initialisation order is random - // and we have a 50/50 chance that State::DEFAULT is not yet initialized. - // Since State::DEFAULT = State::Data() it is much easier to not use the actual State::DEFAULT - // but another State::Data object with a default initialization. - const State::Data DEFAULT = State::Data(); - - auto depthBiasCommand = std::make_shared(&GLBackend::do_setStateDepthBias, - Vec2(DEFAULT.depthBias, DEFAULT.depthBiasSlopeScale)); - auto stencilCommand = std::make_shared(&GLBackend::do_setStateStencil, DEFAULT.stencilActivation, - DEFAULT.stencilTestFront, DEFAULT.stencilTestBack); - - // The state commands to reset to default, - // WARNING depending on the order of the State::Field enum - return { - std::make_shared(&GLBackend::do_setStateFillMode, DEFAULT.fillMode), - std::make_shared(&GLBackend::do_setStateCullMode, DEFAULT.cullMode), - std::make_shared(&GLBackend::do_setStateFrontFaceClockwise, DEFAULT.frontFaceClockwise), - std::make_shared(&GLBackend::do_setStateDepthClampEnable, DEFAULT.depthClampEnable), - std::make_shared(&GLBackend::do_setStateScissorEnable, DEFAULT.scissorEnable), - std::make_shared(&GLBackend::do_setStateMultisampleEnable, DEFAULT.multisampleEnable), - std::make_shared(&GLBackend::do_setStateAntialiasedLineEnable, DEFAULT.antialisedLineEnable), - - // Depth bias has 2 fields in State but really one call in GLBackend - CommandPointer(depthBiasCommand), - CommandPointer(depthBiasCommand), - - std::make_shared(&GLBackend::do_setStateDepthTest, DEFAULT.depthTest), - - // Depth bias has 3 fields in State but really one call in GLBackend - CommandPointer(stencilCommand), - CommandPointer(stencilCommand), - CommandPointer(stencilCommand), - - std::make_shared(&GLBackend::do_setStateSampleMask, DEFAULT.sampleMask), - - std::make_shared(&GLBackend::do_setStateAlphaToCoverageEnable, DEFAULT.alphaToCoverageEnable), - - std::make_shared(&GLBackend::do_setStateBlend, DEFAULT.blendFunction), - - std::make_shared(&GLBackend::do_setStateColorWriteMask, DEFAULT.colorWriteMask) - }; -} - -const GLState::Commands GLState::_resetStateCommands = makeResetStateCommands(); - - -void generateFillMode(GLState::Commands& commands, State::FillMode fillMode) { - commands.push_back(std::make_shared(&GLBackend::do_setStateFillMode, int32(fillMode))); -} - -void generateCullMode(GLState::Commands& commands, State::CullMode cullMode) { - commands.push_back(std::make_shared(&GLBackend::do_setStateCullMode, int32(cullMode))); -} - -void generateFrontFaceClockwise(GLState::Commands& commands, bool isClockwise) { - commands.push_back(std::make_shared(&GLBackend::do_setStateFrontFaceClockwise, isClockwise)); -} - -void generateDepthClampEnable(GLState::Commands& commands, bool enable) { - commands.push_back(std::make_shared(&GLBackend::do_setStateDepthClampEnable, enable)); -} - -void generateScissorEnable(GLState::Commands& commands, bool enable) { - commands.push_back(std::make_shared(&GLBackend::do_setStateScissorEnable, enable)); -} - -void generateMultisampleEnable(GLState::Commands& commands, bool enable) { - commands.push_back(std::make_shared(&GLBackend::do_setStateMultisampleEnable, enable)); -} - -void generateAntialiasedLineEnable(GLState::Commands& commands, bool enable) { - commands.push_back(std::make_shared(&GLBackend::do_setStateAntialiasedLineEnable, enable)); -} - -void generateDepthBias(GLState::Commands& commands, const State& state) { - commands.push_back(std::make_shared(&GLBackend::do_setStateDepthBias, Vec2(state.getDepthBias(), state.getDepthBiasSlopeScale()))); -} - -void generateDepthTest(GLState::Commands& commands, const State::DepthTest& test) { - commands.push_back(std::make_shared(&GLBackend::do_setStateDepthTest, int32(test.getRaw()))); -} - -void generateStencil(GLState::Commands& commands, const State& state) { - commands.push_back(std::make_shared(&GLBackend::do_setStateStencil, state.getStencilActivation(), state.getStencilTestFront(), state.getStencilTestBack())); -} - -void generateAlphaToCoverageEnable(GLState::Commands& commands, bool enable) { - commands.push_back(std::make_shared(&GLBackend::do_setStateAlphaToCoverageEnable, enable)); -} - -void generateSampleMask(GLState::Commands& commands, uint32 mask) { - commands.push_back(std::make_shared(&GLBackend::do_setStateSampleMask, mask)); -} - -void generateBlend(GLState::Commands& commands, const State& state) { - commands.push_back(std::make_shared(&GLBackend::do_setStateBlend, state.getBlendFunction())); -} - -void generateColorWriteMask(GLState::Commands& commands, uint32 mask) { - commands.push_back(std::make_shared(&GLBackend::do_setStateColorWriteMask, mask)); -} - -GLState* GLState::sync(const State& state) { - GLState* object = Backend::getGPUObject(state); - - // If GPU object already created then good - if (object) { - return object; - } - - // Else allocate and create the GLState - if (!object) { - object = new GLState(); - Backend::setGPUObject(state, object); - } - - // here, we need to regenerate something so let's do it all - object->_commands.clear(); - object->_stamp = state.getStamp(); - object->_signature = state.getSignature(); - - bool depthBias = false; - bool stencilState = false; - - // go thorugh the list of state fields in the State and record the corresponding gl command - for (int i = 0; i < State::NUM_FIELDS; i++) { - if (state.getSignature()[i]) { - switch (i) { - case State::FILL_MODE: { - generateFillMode(object->_commands, state.getFillMode()); - break; - } - case State::CULL_MODE: { - generateCullMode(object->_commands, state.getCullMode()); - break; - } - case State::DEPTH_BIAS: - case State::DEPTH_BIAS_SLOPE_SCALE: { - depthBias = true; - break; - } - case State::FRONT_FACE_CLOCKWISE: { - generateFrontFaceClockwise(object->_commands, state.isFrontFaceClockwise()); - break; - } - case State::DEPTH_CLAMP_ENABLE: { - generateDepthClampEnable(object->_commands, state.isDepthClampEnable()); - break; - } - case State::SCISSOR_ENABLE: { - generateScissorEnable(object->_commands, state.isScissorEnable()); - break; - } - case State::MULTISAMPLE_ENABLE: { - generateMultisampleEnable(object->_commands, state.isMultisampleEnable()); - break; - } - case State::ANTIALISED_LINE_ENABLE: { - generateAntialiasedLineEnable(object->_commands, state.isAntialiasedLineEnable()); - break; - } - case State::DEPTH_TEST: { - generateDepthTest(object->_commands, state.getDepthTest()); - break; - } - - case State::STENCIL_ACTIVATION: - case State::STENCIL_TEST_FRONT: - case State::STENCIL_TEST_BACK: { - stencilState = true; - break; - } - - case State::SAMPLE_MASK: { - generateSampleMask(object->_commands, state.getSampleMask()); - break; - } - case State::ALPHA_TO_COVERAGE_ENABLE: { - generateAlphaToCoverageEnable(object->_commands, state.isAlphaToCoverageEnabled()); - break; - } - - case State::BLEND_FUNCTION: { - generateBlend(object->_commands, state); - break; - } - - case State::COLOR_WRITE_MASK: { - generateColorWriteMask(object->_commands, state.getColorWriteMask()); - break; - } - } - } - } - - if (depthBias) { - generateDepthBias(object->_commands, state); - } - - if (stencilState) { - generateStencil(object->_commands, state); - } - - return object; -} - diff --git a/libraries/gpu-gles/src/gpu/gl/GLState.h b/libraries/gpu-gles/src/gpu/gl/GLState.h deleted file mode 100644 index 82635db893..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLState.h +++ /dev/null @@ -1,73 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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_gpu_gl_GLState_h -#define hifi_gpu_gl_GLState_h - -#include "GLShared.h" - -#include - -namespace gpu { namespace gl { - -class GLBackend; -class GLState : public GPUObject { -public: - static GLState* sync(const State& state); - - class Command { - public: - virtual void run(GLBackend* backend) = 0; - Command() {} - virtual ~Command() {}; - }; - - template class Command1 : public Command { - public: - typedef void (GLBackend::*GLFunction)(T); - void run(GLBackend* backend) { (backend->*(_func))(_param); } - Command1(GLFunction func, T param) : _func(func), _param(param) {}; - GLFunction _func; - T _param; - }; - template class Command2 : public Command { - public: - typedef void (GLBackend::*GLFunction)(T, U); - void run(GLBackend* backend) { (backend->*(_func))(_param0, _param1); } - Command2(GLFunction func, T param0, U param1) : _func(func), _param0(param0), _param1(param1) {}; - GLFunction _func; - T _param0; - U _param1; - }; - - template class Command3 : public Command { - public: - typedef void (GLBackend::*GLFunction)(T, U, V); - void run(GLBackend* backend) { (backend->*(_func))(_param0, _param1, _param2); } - Command3(GLFunction func, T param0, U param1, V param2) : _func(func), _param0(param0), _param1(param1), _param2(param2) {}; - GLFunction _func; - T _param0; - U _param1; - V _param2; - }; - - typedef std::shared_ptr< Command > CommandPointer; - typedef std::vector< CommandPointer > Commands; - - Commands _commands; - Stamp _stamp; - State::Signature _signature; - - // The state commands to reset to default, - static const Commands _resetStateCommands; - - friend class GLBackend; -}; - -} } - -#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp deleted file mode 100644 index 2a39901ee7..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp +++ /dev/null @@ -1,652 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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 "GLTexelFormat.h" - -using namespace gpu; -using namespace gpu::gl; - -bool GLTexelFormat::isCompressed() const { - switch (internalFormat) { - case GL_COMPRESSED_R11_EAC: - case GL_COMPRESSED_SIGNED_R11_EAC: - case GL_COMPRESSED_RG11_EAC: - case GL_COMPRESSED_SIGNED_RG11_EAC: - case GL_COMPRESSED_RGB8_ETC2: - case GL_COMPRESSED_SRGB8_ETC2: - case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: - case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: - case GL_COMPRESSED_RGBA8_ETC2_EAC: - case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: - case GL_COMPRESSED_RGBA_ASTC_4x4: - case GL_COMPRESSED_RGBA_ASTC_5x4: - case GL_COMPRESSED_RGBA_ASTC_5x5: - case GL_COMPRESSED_RGBA_ASTC_6x5: - case GL_COMPRESSED_RGBA_ASTC_6x6: - case GL_COMPRESSED_RGBA_ASTC_8x5: - case GL_COMPRESSED_RGBA_ASTC_8x6: - case GL_COMPRESSED_RGBA_ASTC_8x8: - case GL_COMPRESSED_RGBA_ASTC_10x5: - case GL_COMPRESSED_RGBA_ASTC_10x6: - case GL_COMPRESSED_RGBA_ASTC_10x8: - case GL_COMPRESSED_RGBA_ASTC_10x10: - case GL_COMPRESSED_RGBA_ASTC_12x10: - case GL_COMPRESSED_RGBA_ASTC_12x12: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10: - case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12: - return true; - default: - return false; - } -} - -GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { - GLenum result = GL_RGBA8; - switch (dstFormat.getDimension()) { - case gpu::SCALAR: { - switch (dstFormat.getSemantic()) { - case gpu::RED: - case gpu::RGB: - case gpu::RGBA: - case gpu::SRGB: - case gpu::SRGBA: - switch (dstFormat.getType()) { - case gpu::UINT32: - result = GL_R32UI; - break; - case gpu::INT32: - result = GL_R32I; - break; - case gpu::NUINT32: - result = GL_R8; - break; - case gpu::NINT32: - result = GL_R8_SNORM; - break; - case gpu::FLOAT: - result = GL_R32F; - break; - case gpu::UINT16: - result = GL_R16UI; - break; - case gpu::INT16: - result = GL_R16I; - break; - case gpu::NUINT16: - //result = GL_R16_EXT; - break; - case gpu::NINT16: - //result = GL_R16_SNORM_EXT; - break; - case gpu::HALF: - result = GL_R16F; - break; - case gpu::UINT8: - result = GL_R8UI; - break; - case gpu::INT8: - result = GL_R8I; - break; - case gpu::NUINT8: - if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { - result = GL_SLUMINANCE8_NV; - } else { - result = GL_R8; - } - break; - case gpu::NINT8: - result = GL_R8_SNORM; - break; - default: - Q_UNREACHABLE(); - break; - } - break; - case gpu::R11G11B10: - // the type should be float - result = GL_R11F_G11F_B10F; - break; - case gpu::RGB9E5: - // the type should be float - result = GL_RGB9_E5; - break; - case gpu::DEPTH: - result = GL_DEPTH_COMPONENT16; - switch (dstFormat.getType()) { - case gpu::UINT32: - case gpu::INT32: - case gpu::NUINT32: - case gpu::NINT32: - result = GL_DEPTH_COMPONENT32_OES; - break; - case gpu::FLOAT: - result = GL_DEPTH_COMPONENT32F; - break; - case gpu::UINT16: - case gpu::INT16: - case gpu::NUINT16: - case gpu::NINT16: - case gpu::HALF: - result = GL_DEPTH_COMPONENT16; - break; - case gpu::UINT8: - case gpu::INT8: - case gpu::NUINT8: - case gpu::NINT8: - result = GL_DEPTH_COMPONENT24; - break; - default: - Q_UNREACHABLE(); - break; - } - break; - - case gpu::DEPTH_STENCIL: - result = GL_DEPTH24_STENCIL8; - break; - - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - break; - } - - case gpu::VEC2: { - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - case gpu::XY: - result = GL_RG8; - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - - break; - } - - case gpu::VEC3: { - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - result = GL_RGB8; - break; - case gpu::SRGB: - case gpu::SRGBA: - result = GL_SRGB8; // standard 2.2 gamma correction color - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - - break; - } - - case gpu::VEC4: { - switch (dstFormat.getSemantic()) { - case gpu::RGB: - result = GL_RGB8; - break; - case gpu::RGBA: - switch (dstFormat.getType()) { - case gpu::UINT32: - result = GL_RGBA32UI; - break; - case gpu::INT32: - result = GL_RGBA32I; - break; - case gpu::FLOAT: - result = GL_RGBA32F; - break; - case gpu::UINT16: - result = GL_RGBA16UI; - break; - case gpu::INT16: - result = GL_RGBA16I; - break; - case gpu::NUINT16: - //result = GL_RGBA16_EXT; - break; - case gpu::NINT16: - //result = GL_RGBA16_SNORM_EXT; - break; - case gpu::HALF: - result = GL_RGBA16F; - break; - case gpu::UINT8: - result = GL_RGBA8UI; - break; - case gpu::INT8: - result = GL_RGBA8I; - break; - case gpu::NUINT8: - result = GL_RGBA8; - break; - case gpu::NUINT2: - //result = GL_RGBA2; - break; - case gpu::NINT8: - result = GL_RGBA8_SNORM; - break; - case gpu::NINT2_10_10_10: - case gpu::NUINT32: - case gpu::NINT32: - case gpu::COMPRESSED: - case gpu::NUM_TYPES: // quiet compiler - Q_UNREACHABLE(); - } - break; - case gpu::SRGB: - result = GL_SRGB8; - break; - case gpu::SRGBA: - result = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - break; - } - default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; - } - - //qDebug() << "GLTexelFormat::evalGLTexelFormatInternal result " << result; - return result; -} - -GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) { - - if (dstFormat != srcFormat) { - GLTexelFormat texel = { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }; - - switch (dstFormat.getDimension()) { - case gpu::SCALAR: { - texel.format = GL_RED; - texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RED: - case gpu::RGB: - case gpu::RGBA: - texel.internalFormat = GL_R8; - break; - - case gpu::DEPTH: - texel.format = GL_DEPTH_COMPONENT; - texel.internalFormat = GL_DEPTH_COMPONENT32F; - break; - case gpu::DEPTH_STENCIL: - texel.type = GL_UNSIGNED_INT_24_8; - texel.format = GL_DEPTH_STENCIL; - texel.internalFormat = GL_DEPTH24_STENCIL8; - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - break; - } - - case gpu::VEC2: { - texel.format = GL_RG; - texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - case gpu::XY: - texel.internalFormat = GL_RG8; - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - - break; - } - - case gpu::VEC3: { - texel.format = GL_RGB; - - texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - texel.internalFormat = GL_RGB8; - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - - break; - } - - case gpu::VEC4: { - texel.format = GL_RGBA; - texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; - - switch (srcFormat.getSemantic()) { - case gpu::BGRA: - case gpu::SBGRA: - texel.format = GL_RGBA; // GL_BGRA_EXT; - break; - case gpu::RGB: - case gpu::RGBA: - case gpu::SRGB: - case gpu::SRGBA: - default: - break; - }; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - texel.internalFormat = GL_RGB8; - break; - case gpu::RGBA: - texel.internalFormat = GL_RGBA8; - break; - case gpu::SRGB: - texel.internalFormat = GL_SRGB8; - break; - case gpu::SRGBA: - texel.internalFormat = GL_SRGB8_ALPHA8; - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - break; - } - - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - return texel; - } else { - GLTexelFormat texel = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }; - - switch (dstFormat.getDimension()) { - case gpu::SCALAR: { - texel.format = GL_RED; - texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - - case gpu::RED: - case gpu::RGB: - case gpu::RGBA: - case gpu::SRGB: - case gpu::SRGBA: - texel.internalFormat = GL_RED; - switch (dstFormat.getType()) { - case gpu::UINT32: { - texel.internalFormat = GL_R32UI; - break; - } - case gpu::INT32: { - texel.internalFormat = GL_R32I; - break; - } - case gpu::NUINT32: { - texel.internalFormat = GL_R8; - break; - } - case gpu::NINT32: { - texel.internalFormat = GL_R8_SNORM; - break; - } - case gpu::FLOAT: { - texel.internalFormat = GL_R32F; - break; - } - case gpu::UINT16: { - texel.internalFormat = GL_R16UI; - break; - } - case gpu::INT16: { - texel.internalFormat = GL_R16I; - break; - } - case gpu::NUINT16: { - texel.internalFormat = GL_R16_EXT; - break; - } - case gpu::NINT16: { - texel.internalFormat = GL_R16_SNORM_EXT; - break; - } - case gpu::HALF: { - texel.internalFormat = GL_R16F; - break; - } - case gpu::UINT8: { - texel.internalFormat = GL_R8UI; - break; - } - case gpu::INT8: { - texel.internalFormat = GL_R8I; - break; - } - case gpu::NUINT8: { - if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { - texel.internalFormat = GL_SLUMINANCE8_NV; - } else { - texel.internalFormat = GL_R8; - } - break; - } - case gpu::NINT8: { - texel.internalFormat = GL_R8_SNORM; - break; - } - case gpu::COMPRESSED: - case gpu::NUINT2: - case gpu::NUM_TYPES: { // quiet compiler - Q_UNREACHABLE(); - } - - } - break; - - case gpu::R11G11B10: - texel.format = GL_RGB; - texel.type = GL_UNSIGNED_INT_10F_11F_11F_REV; - texel.internalFormat = GL_R11F_G11F_B10F; - break; - - case gpu::RGB9E5: - texel.format = GL_RGB; - texel.type = GL_UNSIGNED_INT_5_9_9_9_REV; - texel.internalFormat = GL_RGB9_E5; - break; - - case gpu::DEPTH: - texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it - texel.internalFormat = GL_DEPTH_COMPONENT32_OES; - switch (dstFormat.getType()) { - case gpu::UINT32: - case gpu::INT32: - case gpu::NUINT32: - case gpu::NINT32: { - texel.internalFormat = GL_DEPTH_COMPONENT32_OES; - break; - } - case gpu::FLOAT: { - texel.internalFormat = GL_DEPTH_COMPONENT32F; - break; - } - case gpu::UINT16: - case gpu::INT16: - case gpu::NUINT16: - case gpu::NINT16: - case gpu::HALF: { - texel.internalFormat = GL_DEPTH_COMPONENT16; - break; - } - case gpu::UINT8: - case gpu::INT8: - case gpu::NUINT8: - case gpu::NINT8: { - texel.internalFormat = GL_DEPTH_COMPONENT24; - break; - } - case gpu::COMPRESSED: - case gpu::NUINT2: - case gpu::NINT2_10_10_10: - case gpu::NUM_TYPES: { // quiet compiler - Q_UNREACHABLE(); - } - } - break; - case gpu::DEPTH_STENCIL: - texel.type = GL_UNSIGNED_INT_24_8; - texel.format = GL_DEPTH_STENCIL; - texel.internalFormat = GL_DEPTH24_STENCIL8; - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - - break; - } - - case gpu::VEC2: { - texel.format = GL_RG; - texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - case gpu::XY: - texel.internalFormat = GL_RG8; - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - - break; - } - - case gpu::VEC3: { - texel.format = GL_RGB; - - texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - case gpu::RGBA: - texel.internalFormat = GL_RGB8; - break; - case gpu::SRGB: - case gpu::SRGBA: - texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - break; - } - - case gpu::VEC4: { - texel.format = GL_RGBA; - texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; - - switch (dstFormat.getSemantic()) { - case gpu::RGB: - texel.internalFormat = GL_RGB8; - break; - case gpu::RGBA: - texel.internalFormat = GL_RGBA8; - switch (dstFormat.getType()) { - case gpu::UINT32: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA32UI; - break; - case gpu::INT32: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA32I; - break; - case gpu::FLOAT: - texel.internalFormat = GL_RGBA32F; - break; - case gpu::UINT16: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA16UI; - break; - case gpu::INT16: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA16I; - break; - case gpu::NUINT16: - texel.format = GL_RGBA; - //texel.internalFormat = GL_RGBA16_EXT; - break; - case gpu::NINT16: - texel.format = GL_RGBA; - //texel.internalFormat = GL_RGBA16_SNORM_EXT; - break; - case gpu::HALF: - texel.format = GL_RGBA; - texel.internalFormat = GL_RGBA16F; - break; - case gpu::UINT8: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA8UI; - break; - case gpu::INT8: - texel.format = GL_RGBA_INTEGER; - texel.internalFormat = GL_RGBA8I; - break; - case gpu::NUINT8: - texel.format = GL_RGBA; - texel.internalFormat = GL_RGBA8; - break; - case gpu::NINT8: - texel.format = GL_RGBA; - texel.internalFormat = GL_RGBA8_SNORM; - break; - case gpu::NUINT2: - texel.format = GL_RGBA; - texel.internalFormat = GL_RGBA8; - break; - case gpu::NUINT32: - case gpu::NINT32: - case gpu::NINT2_10_10_10: - case gpu::COMPRESSED: - case gpu::NUM_TYPES: // quiet compiler - Q_UNREACHABLE(); - } - break; - case gpu::SRGB: - texel.internalFormat = GL_SRGB8; - break; - case gpu::SRGBA: - texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color - break; - default: - qCWarning(gpugllogging) << "Unknown combination of texel format"; - } - break; - } - default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; - } - return texel; - } -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h deleted file mode 100644 index 8f37f6b604..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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_gpu_gl_GLTexelFormat_h -#define hifi_gpu_gl_GLTexelFormat_h - -#include "GLShared.h" - -namespace gpu { namespace gl { - -class GLTexelFormat { -public: - GLenum internalFormat; - GLenum format; - GLenum type; - - GLTexelFormat(GLenum glinternalFormat, GLenum glformat, GLenum gltype) : internalFormat(glinternalFormat), format(glformat), type(gltype) {} - GLTexelFormat(GLenum glinternalFormat) : internalFormat(glinternalFormat) {} - - bool isCompressed() const; - - static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) { - return evalGLTexelFormat(dstFormat, dstFormat); - } - static GLenum evalGLTexelFormatInternal(const Element& dstFormat); - - static GLTexelFormat evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat); -}; - -} } - - -#endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp deleted file mode 100644 index 08b3f87094..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp +++ /dev/null @@ -1,701 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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 "GLTexture.h" - -#include -#include - -#include "GLBackend.h" - -using namespace gpu; -using namespace gpu::gl; - - -const GLenum GLTexture::CUBE_FACE_LAYOUT[GLTexture::TEXTURE_CUBE_NUM_FACES] = { - GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, - GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, - GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z -}; - - -const GLenum GLTexture::WRAP_MODES[Sampler::NUM_WRAP_MODES] = { - GL_REPEAT, // WRAP_REPEAT, - GL_MIRRORED_REPEAT, // WRAP_MIRROR, - GL_CLAMP_TO_EDGE, // WRAP_CLAMP, - GL_CLAMP_TO_BORDER, // WRAP_BORDER, - GL_MIRRORED_REPEAT //GL_MIRROR_CLAMP_TO_EDGE_EXT // WRAP_MIRROR_ONCE, -}; - -const GLFilterMode GLTexture::FILTER_MODES[Sampler::NUM_FILTERS] = { - { GL_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_POINT, - { GL_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR, - { GL_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT, - { GL_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR, - - { GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT, - { GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_MAG_POINT_MIP_LINEAR, - { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, - { GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_POINT_MAG_MIP_LINEAR, - { GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_MIP_POINT, - { GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, - { GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR_MIP_POINT, - { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_MIP_LINEAR, - { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR } //FILTER_ANISOTROPIC, -}; - -GLenum GLTexture::getGLTextureType(const Texture& texture) { - switch (texture.getType()) { - case Texture::TEX_2D: - return GL_TEXTURE_2D; - break; - - case Texture::TEX_CUBE: - return GL_TEXTURE_CUBE_MAP; - break; - - default: - qFatal("Unsupported texture type"); - } - Q_UNREACHABLE(); - return GL_TEXTURE_2D; -} - -uint8_t GLTexture::getFaceCount(GLenum target) { - switch (target) { - case GL_TEXTURE_2D: - return TEXTURE_2D_NUM_FACES; - case GL_TEXTURE_CUBE_MAP: - return TEXTURE_CUBE_NUM_FACES; - default: - Q_UNREACHABLE(); - break; - } -} - -const std::vector& GLTexture::getFaceTargets(GLenum target) { - static std::vector cubeFaceTargets { - GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, - GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, - GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - }; - static std::vector faceTargets { - GL_TEXTURE_2D - }; - switch (target) { - case GL_TEXTURE_2D: - return faceTargets; - case GL_TEXTURE_CUBE_MAP: - return cubeFaceTargets; - default: - Q_UNREACHABLE(); - break; - } - Q_UNREACHABLE(); - return faceTargets; -} - -GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : - GLObject(backend, texture, id), - _source(texture.source()), - _target(getGLTextureType(texture)), - _texelFormat(GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())) -{ - Backend::setGPUObject(texture, this); -} - -GLTexture::~GLTexture() { - auto backend = _backend.lock(); - if (backend && _id) { - backend->releaseTexture(_id, 0); - } -} - -Size GLTexture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { - if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) { - return 0; - } - auto dim = _gpuObject.evalMipDimensions(sourceMip); - auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); - auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); - if (mipData) { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - return copyMipFaceLinesFromTexture(targetMip, face, dim, 0, texelFormat.internalFormat, texelFormat.format, texelFormat.type, mipSize, mipData->readData()); - } else { - qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); - } - return 0; -} - - -GLExternalTexture::GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) - : Parent(backend, texture, id) { - Backend::textureExternalCount.increment(); -} - -GLExternalTexture::~GLExternalTexture() { - auto backend = _backend.lock(); - if (backend) { - auto recycler = _gpuObject.getExternalRecycler(); - if (recycler) { - backend->releaseExternalTexture(_id, recycler); - } else { - qCWarning(gpugllogging) << "No recycler available for texture " << _id << " possible leak"; - } - const_cast(_id) = 0; - } - Backend::textureExternalCount.decrement(); -} - - -// Variable sized textures -using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState; -using WorkQueue = GLVariableAllocationSupport::WorkQueue; -using TransferJobPointer = GLVariableAllocationSupport::TransferJobPointer; - -std::list GLVariableAllocationSupport::_memoryManagedTextures; -MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle }; -std::atomic GLVariableAllocationSupport::_memoryPressureStateStale { false }; -const uvec3 GLVariableAllocationSupport::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; -WorkQueue GLVariableAllocationSupport::_transferQueue; -WorkQueue GLVariableAllocationSupport::_promoteQueue; -WorkQueue GLVariableAllocationSupport::_demoteQueue; -size_t GLVariableAllocationSupport::_frameTexturesCreated { 0 }; - -#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f -#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f -#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) - -static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); - -using TransferJob = GLVariableAllocationSupport::TransferJob; - -const uvec3 GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; -const size_t GLVariableAllocationSupport::MAX_TRANSFER_SIZE = GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.x * GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.y * 4; - -#if THREADED_TEXTURE_BUFFERING - -TexturePointer GLVariableAllocationSupport::_currentTransferTexture; -TransferJobPointer GLVariableAllocationSupport::_currentTransferJob; -QThreadPool* TransferJob::_bufferThreadPool { nullptr }; - -void TransferJob::startBufferingThread() { - static std::once_flag once; - std::call_once(once, [&] { - _bufferThreadPool = new QThreadPool(qApp); - _bufferThreadPool->setMaxThreadCount(1); - }); -} - -#endif - -TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) - : _parent(parent) { - - auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - GLenum format; - GLenum internalFormat; - GLenum type; - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat()); - format = texelFormat.format; - internalFormat = texelFormat.internalFormat; - type = texelFormat.type; - _transferSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face); - - // If we're copying a subsection of the mip, do additional calculations to find the size and offset of the segment - if (0 != lines) { - transferDimensions.y = lines; - auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - auto bytesPerLine = (uint32_t)_transferSize / dimensions.y; - _transferOffset = bytesPerLine * lineOffset; - _transferSize = bytesPerLine * lines; - } - - Backend::texturePendingGPUTransferMemSize.update(0, _transferSize); - - if (_transferSize > GLVariableAllocationSupport::MAX_TRANSFER_SIZE) { - qCWarning(gpugllogging) << "Transfer size of " << _transferSize << " exceeds theoretical maximum transfer size"; - } - - // Buffering can invoke disk IO, so it should be off of the main and render threads - _bufferingLambda = [=] { - auto mipStorage = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - if (mipStorage) { - _mipData = mipStorage->createView(_transferSize, _transferOffset); - } else { - qCWarning(gpugllogging) << "Buffering failed because mip could not be retrieved from texture " << _parent._source.c_str() ; - } - }; - - _transferLambda = [=] { - if (_mipData) { - _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _mipData->size(), _mipData->readData()); - _mipData.reset(); - } else { - qCWarning(gpugllogging) << "Transfer failed because mip could not be retrieved from texture " << _parent._source.c_str(); - } - }; -} - -TransferJob::TransferJob(const GLTexture& parent, std::function transferLambda) - : _parent(parent), _bufferingRequired(false), _transferLambda(transferLambda) { -} - -TransferJob::~TransferJob() { - Backend::texturePendingGPUTransferMemSize.update(_transferSize, 0); -} - -bool TransferJob::tryTransfer() { -#if THREADED_TEXTURE_BUFFERING - // Are we ready to transfer - if (!bufferingCompleted()) { - startBuffering(); - return false; - } -#else - if (_bufferingRequired) { - _bufferingLambda(); - } -#endif - _transferLambda(); - return true; -} - -#if THREADED_TEXTURE_BUFFERING -bool TransferJob::bufferingRequired() const { - if (!_bufferingRequired) { - return false; - } - - // The default state of a QFuture is with status Canceled | Started | Finished, - // so we have to check isCancelled before we check the actual state - if (_bufferingStatus.isCanceled()) { - return true; - } - - return !_bufferingStatus.isStarted(); -} - -bool TransferJob::bufferingCompleted() const { - if (!_bufferingRequired) { - return true; - } - - // The default state of a QFuture is with status Canceled | Started | Finished, - // so we have to check isCancelled before we check the actual state - if (_bufferingStatus.isCanceled()) { - return false; - } - - return _bufferingStatus.isFinished(); -} - -void TransferJob::startBuffering() { - if (bufferingRequired()) { - assert(_bufferingStatus.isCanceled()); - _bufferingStatus = QtConcurrent::run(_bufferThreadPool, [=] { - _bufferingLambda(); - }); - assert(!_bufferingStatus.isCanceled()); - assert(_bufferingStatus.isStarted()); - } -} -#endif - -GLVariableAllocationSupport::GLVariableAllocationSupport() { - _memoryPressureStateStale = true; -} - -GLVariableAllocationSupport::~GLVariableAllocationSupport() { - _memoryPressureStateStale = true; -} - -void GLVariableAllocationSupport::addMemoryManagedTexture(const TexturePointer& texturePointer) { - _memoryManagedTextures.push_back(texturePointer); - if (MemoryPressureState::Idle != _memoryPressureState) { - addToWorkQueue(texturePointer); - } -} - -void GLVariableAllocationSupport::addToWorkQueue(const TexturePointer& texturePointer) { - GLTexture* gltexture = Backend::getGPUObject(*texturePointer); - GLVariableAllocationSupport* vargltexture = dynamic_cast(gltexture); - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - if (vargltexture->canDemote()) { - // Demote largest first - _demoteQueue.push({ texturePointer, (float)gltexture->size() }); - } - break; - - case MemoryPressureState::Undersubscribed: - if (vargltexture->canPromote()) { - // Promote smallest first - _promoteQueue.push({ texturePointer, 1.0f / (float)gltexture->size() }); - } - break; - - case MemoryPressureState::Transfer: - if (vargltexture->hasPendingTransfers()) { - // Transfer priority given to smaller mips first - _transferQueue.push({ texturePointer, 1.0f / (float)gltexture->_gpuObject.evalMipSize(vargltexture->_populatedMip) }); - } - break; - - case MemoryPressureState::Idle: - Q_UNREACHABLE(); - break; - } -} - -WorkQueue& GLVariableAllocationSupport::getActiveWorkQueue() { - static WorkQueue empty; - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - return _demoteQueue; - - case MemoryPressureState::Undersubscribed: - return _promoteQueue; - - case MemoryPressureState::Transfer: - return _transferQueue; - - case MemoryPressureState::Idle: - Q_UNREACHABLE(); - break; - } - return empty; -} - -// FIXME hack for stats display -QString getTextureMemoryPressureModeString() { - switch (GLVariableAllocationSupport::_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - return "Oversubscribed"; - - case MemoryPressureState::Undersubscribed: - return "Undersubscribed"; - - case MemoryPressureState::Transfer: - return "Transfer"; - - case MemoryPressureState::Idle: - return "Idle"; - } - Q_UNREACHABLE(); - return "Unknown"; -} - -void GLVariableAllocationSupport::updateMemoryPressure() { - static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); - - size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); - if (0 == allowedMemoryAllocation) { - allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; - } - - // If the user explicitly changed the allowed memory usage, we need to mark ourselves stale - // so that we react - if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { - _memoryPressureStateStale = true; - lastAllowedMemoryAllocation = allowedMemoryAllocation; - } - - if (!_memoryPressureStateStale.exchange(false)) { - return; - } - - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - - // Clear any defunct textures (weak pointers that no longer have a valid texture) - _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { - return weakPointer.expired(); - }); - - // Convert weak pointers to strong. This new list may still contain nulls if a texture was - // deleted on another thread between the previous line and this one - std::vector strongTextures; { - strongTextures.reserve(_memoryManagedTextures.size()); - std::transform( - _memoryManagedTextures.begin(), _memoryManagedTextures.end(), - std::back_inserter(strongTextures), - [](const TextureWeakPointer& p) { return p.lock(); }); - } - - size_t totalVariableMemoryAllocation = 0; - size_t idealMemoryAllocation = 0; - bool canDemote = false; - bool canPromote = false; - bool hasTransfers = false; - for (const auto& texture : strongTextures) { - // Race conditions can still leave nulls in the list, so we need to check - if (!texture) { - continue; - } - GLTexture* gltexture = Backend::getGPUObject(*texture); - GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); - // Track how much the texture thinks it should be using - idealMemoryAllocation += texture->evalTotalSize(); - // Track how much we're actually using - totalVariableMemoryAllocation += gltexture->size(); - canDemote |= vartexture->canDemote(); - canPromote |= vartexture->canPromote(); - hasTransfers |= vartexture->hasPendingTransfers(); - } - - size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; - float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; - - auto newState = MemoryPressureState::Idle; - if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && (unallocated != 0 && canPromote)) { - newState = MemoryPressureState::Undersubscribed; - } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { - newState = MemoryPressureState::Oversubscribed; - } else if (hasTransfers) { - newState = MemoryPressureState::Transfer; - } - - if (newState != _memoryPressureState) { - _memoryPressureState = newState; - // Clear the existing queue - _transferQueue = WorkQueue(); - _promoteQueue = WorkQueue(); - _demoteQueue = WorkQueue(); - - // Populate the existing textures into the queue - if (_memoryPressureState != MemoryPressureState::Idle) { - for (const auto& texture : strongTextures) { - // Race conditions can still leave nulls in the list, so we need to check - if (!texture) { - continue; - } - addToWorkQueue(texture); - } - } - } -} - -TexturePointer GLVariableAllocationSupport::getNextWorkQueueItem(WorkQueue& workQueue) { - while (!workQueue.empty()) { - auto workTarget = workQueue.top(); - - auto texture = workTarget.first.lock(); - if (!texture) { - workQueue.pop(); - continue; - } - - // Check whether the resulting texture can actually have work performed - GLTexture* gltexture = Backend::getGPUObject(*texture); - GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - if (vartexture->canDemote()) { - return texture; - } - break; - - case MemoryPressureState::Undersubscribed: - if (vartexture->canPromote()) { - return texture; - } - break; - - case MemoryPressureState::Transfer: - if (vartexture->hasPendingTransfers()) { - return texture; - } - break; - - case MemoryPressureState::Idle: - Q_UNREACHABLE(); - break; - } - - // If we got here, then the texture has no work to do in the current state, - // so pop it off the queue and continue - workQueue.pop(); - } - - return TexturePointer(); -} - -void GLVariableAllocationSupport::processWorkQueue(WorkQueue& workQueue) { - if (workQueue.empty()) { - return; - } - - // Get the front of the work queue to perform work - auto texture = getNextWorkQueueItem(workQueue); - if (!texture) { - return; - } - - // Grab the first item off the demote queue - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - - GLTexture* gltexture = Backend::getGPUObject(*texture); - GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - vartexture->demote(); - workQueue.pop(); - addToWorkQueue(texture); - _memoryPressureStateStale = true; - break; - - case MemoryPressureState::Undersubscribed: - vartexture->promote(); - workQueue.pop(); - addToWorkQueue(texture); - _memoryPressureStateStale = true; - break; - - case MemoryPressureState::Transfer: - if (vartexture->executeNextTransfer(texture)) { - workQueue.pop(); - addToWorkQueue(texture); - -#if THREADED_TEXTURE_BUFFERING - // Eagerly start the next buffering job if possible - texture = getNextWorkQueueItem(workQueue); - if (texture) { - gltexture = Backend::getGPUObject(*texture); - vartexture = dynamic_cast(gltexture); - vartexture->executeNextBuffer(texture); - } -#endif - } - break; - - case MemoryPressureState::Idle: - Q_UNREACHABLE(); - break; - } -} - -void GLVariableAllocationSupport::processWorkQueues() { - if (MemoryPressureState::Idle == _memoryPressureState) { - return; - } - - auto& workQueue = getActiveWorkQueue(); - // Do work on the front of the queue - processWorkQueue(workQueue); - - if (workQueue.empty()) { - _memoryPressureState = MemoryPressureState::Idle; - _memoryPressureStateStale = true; - } -} - -void GLVariableAllocationSupport::manageMemory() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - updateMemoryPressure(); - processWorkQueues(); -} - -bool GLVariableAllocationSupport::executeNextTransfer(const TexturePointer& currentTexture) { -#if THREADED_TEXTURE_BUFFERING - // If a transfer job is active on the buffering thread, but has not completed it's buffering lambda, - // then we need to exit early, since we don't want to have the transfer job leave scope while it's - // being used in another thread -- See https://highfidelity.fogbugz.com/f/cases/4626 - if (_currentTransferJob && !_currentTransferJob->bufferingCompleted()) { - return false; - } -#endif - - if (_populatedMip <= _allocatedMip) { -#if THREADED_TEXTURE_BUFFERING - _currentTransferJob.reset(); - _currentTransferTexture.reset(); -#endif - return true; - } - - // If the transfer queue is empty, rebuild it - if (_pendingTransfers.empty()) { - populateTransferQueue(); - } - - bool result = false; - if (!_pendingTransfers.empty()) { -#if THREADED_TEXTURE_BUFFERING - // If there is a current transfer, but it's not the top of the pending transfer queue, then it's an orphan, so we want to abandon it. - if (_currentTransferJob && _currentTransferJob != _pendingTransfers.front()) { - _currentTransferJob.reset(); - } - - if (!_currentTransferJob) { - // Keeping hold of a strong pointer to the transfer job ensures that if the pending transfer queue is rebuilt, the transfer job - // doesn't leave scope, causing a crash in the buffering thread - _currentTransferJob = _pendingTransfers.front(); - - // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture - _currentTransferTexture = currentTexture; - } - - // transfer jobs use asynchronous buffering of the texture data because it may involve disk IO, so we execute a try here to determine if the buffering - // is complete - if (_currentTransferJob->tryTransfer()) { - _pendingTransfers.pop(); - // Once a given job is finished, release the shared pointers keeping them alive - _currentTransferTexture.reset(); - _currentTransferJob.reset(); - result = true; - } -#else - if (_pendingTransfers.front()->tryTransfer()) { - _pendingTransfers.pop(); - result = true; - } -#endif - } - return result; -} - -#if THREADED_TEXTURE_BUFFERING -void GLVariableAllocationSupport::executeNextBuffer(const TexturePointer& currentTexture) { - if (_currentTransferJob && !_currentTransferJob->bufferingCompleted()) { - return; - } - - // If the transfer queue is empty, rebuild it - if (_pendingTransfers.empty()) { - populateTransferQueue(); - } - - if (!_pendingTransfers.empty()) { - if (!_currentTransferJob) { - _currentTransferJob = _pendingTransfers.front(); - _currentTransferTexture = currentTexture; - } - - _currentTransferJob->startBuffering(); - } -} -#endif - -void GLVariableAllocationSupport::incrementPopulatedSize(Size delta) const { - _populatedSize += delta; - // Keep the 2 code paths to be able to debug - if (_size < _populatedSize) { - Backend::textureResourcePopulatedGPUMemSize.update(0, delta); - } else { - Backend::textureResourcePopulatedGPUMemSize.update(0, delta); - } -} -void GLVariableAllocationSupport::decrementPopulatedSize(Size delta) const { - _populatedSize -= delta; - // Keep the 2 code paths to be able to debug - if (_size < _populatedSize) { - Backend::textureResourcePopulatedGPUMemSize.update(delta, 0); - } else { - Backend::textureResourcePopulatedGPUMemSize.update(delta, 0); - } -} \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexture.h b/libraries/gpu-gles/src/gpu/gl/GLTexture.h deleted file mode 100644 index ce27d02033..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTexture.h +++ /dev/null @@ -1,211 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/05/15 -// Copyright 2013-2016 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_gpu_gl_GLTexture_h -#define hifi_gpu_gl_GLTexture_h - -#include -#include - -#include "GLShared.h" -#include "GLBackend.h" -#include "GLTexelFormat.h" -#include - -#define THREADED_TEXTURE_BUFFERING 1 - -namespace gpu { namespace gl { - -struct GLFilterMode { - GLint minFilter; - GLint magFilter; -}; - -class GLVariableAllocationSupport { - friend class GLBackend; - -public: - GLVariableAllocationSupport(); - virtual ~GLVariableAllocationSupport(); - - enum class MemoryPressureState { - Idle, - Transfer, - Oversubscribed, - Undersubscribed, - }; - - using QueuePair = std::pair; - struct QueuePairLess { - bool operator()(const QueuePair& a, const QueuePair& b) { - return a.second < b.second; - } - }; - using WorkQueue = std::priority_queue, QueuePairLess>; - - class TransferJob { - using VoidLambda = std::function; - using VoidLambdaQueue = std::queue; - const GLTexture& _parent; - Texture::PixelsPointer _mipData; - size_t _transferOffset { 0 }; - size_t _transferSize { 0 }; - - bool _bufferingRequired { true }; - VoidLambda _transferLambda; - VoidLambda _bufferingLambda; - -#if THREADED_TEXTURE_BUFFERING - // Indicates if a transfer from backing storage to interal storage has started - QFuture _bufferingStatus; - static QThreadPool* _bufferThreadPool; -#endif - - public: - TransferJob(const TransferJob& other) = delete; - TransferJob(const GLTexture& parent, std::function transferLambda); - TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); - ~TransferJob(); - bool tryTransfer(); - -#if THREADED_TEXTURE_BUFFERING - void startBuffering(); - bool bufferingRequired() const; - bool bufferingCompleted() const; - static void startBufferingThread(); -#endif - - private: - void transfer(); - }; - - using TransferJobPointer = std::shared_ptr; - using TransferQueue = std::queue; - static MemoryPressureState _memoryPressureState; - -public: - static void addMemoryManagedTexture(const TexturePointer& texturePointer); - -protected: - static size_t _frameTexturesCreated; - static std::atomic _memoryPressureStateStale; - static std::list _memoryManagedTextures; - static WorkQueue _transferQueue; - static WorkQueue _promoteQueue; - static WorkQueue _demoteQueue; -#if THREADED_TEXTURE_BUFFERING - static TexturePointer _currentTransferTexture; - static TransferJobPointer _currentTransferJob; -#endif - static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; - static const uvec3 MAX_TRANSFER_DIMENSIONS; - static const size_t MAX_TRANSFER_SIZE; - - - static void updateMemoryPressure(); - static void processWorkQueues(); - static void processWorkQueue(WorkQueue& workQueue); - static TexturePointer getNextWorkQueueItem(WorkQueue& workQueue); - static void addToWorkQueue(const TexturePointer& texture); - static WorkQueue& getActiveWorkQueue(); - - static void manageMemory(); - - //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } - bool canPromote() const { return _allocatedMip > _minAllocatedMip; } - bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } - bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } -#if THREADED_TEXTURE_BUFFERING - void executeNextBuffer(const TexturePointer& currentTexture); -#endif - bool executeNextTransfer(const TexturePointer& currentTexture); - virtual void populateTransferQueue() = 0; - virtual void promote() = 0; - virtual void demote() = 0; - - // THe amount of memory currently allocated - Size _size { 0 }; - - // The amount of memory currnently populated - void incrementPopulatedSize(Size delta) const; - void decrementPopulatedSize(Size delta) const; - mutable Size _populatedSize { 0 }; - - // The allocated mip level, relative to the number of mips in the gpu::Texture object - // The relationship between a given glMip to the original gpu::Texture mip is always - // glMip + _allocatedMip - uint16 _allocatedMip { 0 }; - // The populated mip level, relative to the number of mips in the gpu::Texture object - // This must always be >= the allocated mip - uint16 _populatedMip { 0 }; - // The highest (lowest resolution) mip that we will support, relative to the number - // of mips in the gpu::Texture object - uint16 _maxAllocatedMip { 0 }; - // The lowest (highest resolution) mip that we will support, relative to the number - // of mips in the gpu::Texture object - uint16 _minAllocatedMip { 0 }; - // Contains a series of lambdas that when executed will transfer data to the GPU, modify - // the _populatedMip and update the sampler in order to fully populate the allocated texture - // until _populatedMip == _allocatedMip - TransferQueue _pendingTransfers; -}; - -class GLTexture : public GLObject { - using Parent = GLObject; - friend class GLBackend; - friend class GLVariableAllocationSupport; -public: - static const uint16_t INVALID_MIP { (uint16_t)-1 }; - static const uint8_t INVALID_FACE { (uint8_t)-1 }; - - ~GLTexture(); - - const GLuint& _texture { _id }; - const std::string _source; - const GLenum _target; - GLTexelFormat _texelFormat; - - static const std::vector& getFaceTargets(GLenum textureType); - static uint8_t getFaceCount(GLenum textureType); - static GLenum getGLTextureType(const Texture& texture); - - static const uint8_t TEXTURE_2D_NUM_FACES = 1; - static const uint8_t TEXTURE_CUBE_NUM_FACES = 6; - static const GLenum CUBE_FACE_LAYOUT[TEXTURE_CUBE_NUM_FACES]; - static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS]; - static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES]; - -protected: - virtual Size size() const = 0; - virtual void generateMips() const = 0; - virtual void syncSampler() const = 0; - - virtual Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const = 0; - virtual Size copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const final; - virtual void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {} // Only relevant for Variable Allocation textures - - GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); -}; - -class GLExternalTexture : public GLTexture { - using Parent = GLTexture; - friend class GLBackend; -public: - ~GLExternalTexture(); -protected: - GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); - void generateMips() const override {} - void syncSampler() const override {} - Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override { return 0;} - - Size size() const override { return 0; } -}; - - -} } - -#endif diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h index aeda054e72..38e28e630a 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h @@ -13,8 +13,8 @@ #include -#include "../gl/GLBackend.h" -#include "../gl/GLTexture.h" +#include +#include namespace gpu { namespace gles { diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp index 05bda34d7f..17fdad8377 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GLESBackend.h" -#include "../gl/GLBuffer.h" +#include namespace gpu { namespace gles { diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp index dc4025247e..0bf1548a4b 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp @@ -12,8 +12,8 @@ #include -#include "../gl/GLFramebuffer.h" -#include "../gl/GLTexture.h" +#include +#include namespace gpu { namespace gles { diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp index db541b07bc..434fbcb04f 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendQuery.cpp @@ -10,7 +10,7 @@ // #include "GLESBackend.h" -#include "../gl/GLQuery.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp index 01a87978c2..16cf1559dd 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GLESBackend.h" -#include "../gl/GLShader.h" +#include using namespace gpu; using namespace gpu::gl; diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp index d2fa2aabab..6bc55a23d4 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp @@ -13,7 +13,7 @@ #include #include -#include "../gl/GLTexelFormat.h" +#include using namespace gpu; using namespace gpu::gl; @@ -251,9 +251,6 @@ void GLESFixedAllocationTexture::allocateStorage() const { void GLESFixedAllocationTexture::syncSampler() const { Parent::syncSampler(); const Sampler& sampler = _gpuObject.getSampler(); - auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); - - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, baseMip); glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip())); } @@ -459,7 +456,6 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize; sourceMip._offset = bufferOffset; bufferOffset += sourceMip._size; - gpu::gl::checkGLError(); } (void)CHECK_GL_ERROR(); @@ -505,7 +501,6 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui #endif glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat, sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize)); - gpu::gl::checkGLError(); } } diff --git a/libraries/graphics/src/graphics/Geometry.h b/libraries/graphics/src/graphics/Geometry.h index a75fb1bf62..485542d26b 100755 --- a/libraries/graphics/src/graphics/Geometry.h +++ b/libraries/graphics/src/graphics/Geometry.h @@ -74,7 +74,7 @@ public: size_t getNumIndices() const { return _indexBuffer.getNumElements(); } // Access vertex position value - const Vec3& getPos3(Index index) const { return _vertexBuffer.get(index); } + const Vec3& getPos(Index index) const { return _vertexBuffer.get(index); } enum Topology { POINTS = 0, diff --git a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp index 96f1daba8c..6f94e7592c 100644 --- a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp @@ -20,9 +20,10 @@ InputPluginList getInputPlugins() { InputPlugin* PLUGIN_POOL[] = { new KeyboardMouseDevice(), - new TouchscreenDevice(), #if defined(Q_OS_ANDROID) new TouchscreenVirtualPadDevice(), +#else + new TouchscreenDevice(), // Touchscreen and Controller Scripts take care on Android #endif nullptr }; diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp index c6ce179482..0a28368e9e 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp @@ -60,6 +60,8 @@ void TouchscreenVirtualPadDevice::init() { if (_fixedPosition) { virtualPadManager.getLeftVirtualPad()->setShown(virtualPadManager.isEnabled() && !virtualPadManager.isHidden()); // Show whenever it's enabled } + + KeyboardMouseDevice::enableTouch(false); // Touch for view controls is managed by this plugin } void TouchscreenVirtualPadDevice::setupFixedCenter(VirtualPad::Manager& virtualPadManager, bool force) { @@ -73,8 +75,8 @@ void TouchscreenVirtualPadDevice::setupFixedCenter(VirtualPad::Manager& virtualP QScreen* eventScreen = qApp->primaryScreen(); // do not call every time _fixedCenterPosition = glm::vec2( _fixedRadius + margin, eventScreen->size().height() - margin - _fixedRadius - _extraBottomMargin); - _firstTouchLeftPoint = _fixedCenterPosition; - virtualPadManager.getLeftVirtualPad()->setFirstTouch(_firstTouchLeftPoint); + _moveRefTouchPoint = _fixedCenterPosition; + virtualPadManager.getLeftVirtualPad()->setFirstTouch(_moveRefTouchPoint); } float clip(float n, float lower, float upper) { @@ -116,42 +118,35 @@ glm::vec2 TouchscreenVirtualPadDevice::clippedPointInCircle(float radius, glm::v return vec2(finalX, finalY); } -void TouchscreenVirtualPadDevice::processInputUseCircleMethod(VirtualPad::Manager& virtualPadManager) { - vec2 clippedPoint = clippedPointInCircle(_fixedRadiusForCalc, _firstTouchLeftPoint, _currentTouchLeftPoint); +void TouchscreenVirtualPadDevice::processInputDeviceForMove(VirtualPad::Manager& virtualPadManager) { + vec2 clippedPoint = clippedPointInCircle(_fixedRadiusForCalc, _moveRefTouchPoint, _moveCurrentTouchPoint); - _inputDevice->_axisStateMap[controller::LX] = (clippedPoint.x - _firstTouchLeftPoint.x) / _fixedRadiusForCalc; - _inputDevice->_axisStateMap[controller::LY] = (clippedPoint.y - _firstTouchLeftPoint.y) / _fixedRadiusForCalc; + _inputDevice->_axisStateMap[controller::LX] = (clippedPoint.x - _moveRefTouchPoint.x) / _fixedRadiusForCalc; + _inputDevice->_axisStateMap[controller::LY] = (clippedPoint.y - _moveRefTouchPoint.y) / _fixedRadiusForCalc; - virtualPadManager.getLeftVirtualPad()->setFirstTouch(_firstTouchLeftPoint); + virtualPadManager.getLeftVirtualPad()->setFirstTouch(_moveRefTouchPoint); virtualPadManager.getLeftVirtualPad()->setCurrentTouch(clippedPoint); virtualPadManager.getLeftVirtualPad()->setBeingTouched(true); virtualPadManager.getLeftVirtualPad()->setShown(true); // If touched, show in any mode (fixed joystick position or non-fixed) } -void TouchscreenVirtualPadDevice::processInputUseSquareMethod(VirtualPad::Manager& virtualPadManager) { - float leftDistanceScaleX, leftDistanceScaleY; - leftDistanceScaleX = (_currentTouchLeftPoint.x - _firstTouchLeftPoint.x) / _screenDPIScale.x; - leftDistanceScaleY = (_currentTouchLeftPoint.y - _firstTouchLeftPoint.y) / _screenDPIScale.y; +void TouchscreenVirtualPadDevice::processInputDeviceForView() { + float rightDistanceScaleX, rightDistanceScaleY; + rightDistanceScaleX = (_viewCurrentTouchPoint.x - _viewRefTouchPoint.x) / _screenDPIScale.x; + rightDistanceScaleY = (_viewCurrentTouchPoint.y - _viewRefTouchPoint.y) / _screenDPIScale.y; - leftDistanceScaleX = clip(leftDistanceScaleX, -STICK_RADIUS_INCHES, STICK_RADIUS_INCHES); - leftDistanceScaleY = clip(leftDistanceScaleY, -STICK_RADIUS_INCHES, STICK_RADIUS_INCHES); + rightDistanceScaleX = clip(rightDistanceScaleX, -_viewStickRadiusInches, _viewStickRadiusInches); + rightDistanceScaleY = clip(rightDistanceScaleY, -_viewStickRadiusInches, _viewStickRadiusInches); // NOW BETWEEN -1 1 - leftDistanceScaleX /= STICK_RADIUS_INCHES; - leftDistanceScaleY /= STICK_RADIUS_INCHES; + rightDistanceScaleX /= _viewStickRadiusInches; + rightDistanceScaleY /= _viewStickRadiusInches; - _inputDevice->_axisStateMap[controller::LX] = leftDistanceScaleX; - _inputDevice->_axisStateMap[controller::LY] = leftDistanceScaleY; + _inputDevice->_axisStateMap[controller::RX] = rightDistanceScaleX; + _inputDevice->_axisStateMap[controller::RY] = rightDistanceScaleY; - /* Shared variables for stick rendering (clipped to the stick radius)*/ - // Prevent this for being done when not in first person view - virtualPadManager.getLeftVirtualPad()->setFirstTouch(_firstTouchLeftPoint); - virtualPadManager.getLeftVirtualPad()->setCurrentTouch( - glm::vec2(clip(_currentTouchLeftPoint.x, -STICK_RADIUS_INCHES * _screenDPIScale.x + _firstTouchLeftPoint.x, STICK_RADIUS_INCHES * _screenDPIScale.x + _firstTouchLeftPoint.x), - clip(_currentTouchLeftPoint.y, -STICK_RADIUS_INCHES * _screenDPIScale.y + _firstTouchLeftPoint.y, STICK_RADIUS_INCHES * _screenDPIScale.y + _firstTouchLeftPoint.y)) - ); - virtualPadManager.getLeftVirtualPad()->setBeingTouched(true); - virtualPadManager.getLeftVirtualPad()->setShown(true); // If touched, show in any mode (fixed joystick position or non-fixed) + // after use, save last touch point as ref + _viewRefTouchPoint = _viewCurrentTouchPoint; } void TouchscreenVirtualPadDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { @@ -163,8 +158,8 @@ void TouchscreenVirtualPadDevice::pluginUpdate(float deltaTime, const controller auto& virtualPadManager = VirtualPad::Manager::instance(); setupFixedCenter(virtualPadManager); - if (_validTouchLeft) { - processInputUseCircleMethod(virtualPadManager); + if (_moveHasValidTouch) { + processInputDeviceForMove(virtualPadManager); } else { virtualPadManager.getLeftVirtualPad()->setBeingTouched(false); if (_fixedPosition) { @@ -175,20 +170,8 @@ void TouchscreenVirtualPadDevice::pluginUpdate(float deltaTime, const controller } } - if (_validTouchRight) { - float rightDistanceScaleX, rightDistanceScaleY; - rightDistanceScaleX = (_currentTouchRightPoint.x - _firstTouchRightPoint.x) / _screenDPIScale.x; - rightDistanceScaleY = (_currentTouchRightPoint.y - _firstTouchRightPoint.y) / _screenDPIScale.y; - - rightDistanceScaleX = clip(rightDistanceScaleX, -STICK_RADIUS_INCHES, STICK_RADIUS_INCHES); - rightDistanceScaleY = clip(rightDistanceScaleY, -STICK_RADIUS_INCHES, STICK_RADIUS_INCHES); - - // NOW BETWEEN -1 1 - rightDistanceScaleX /= STICK_RADIUS_INCHES; - rightDistanceScaleY /= STICK_RADIUS_INCHES; - - _inputDevice->_axisStateMap[controller::RX] = rightDistanceScaleX; - _inputDevice->_axisStateMap[controller::RY] = rightDistanceScaleY; + if (_viewHasValidTouch) { + processInputDeviceForView(); } } @@ -228,70 +211,133 @@ void TouchscreenVirtualPadDevice::touchBeginEvent(const QTouchEvent* event) { // touch begin here is a big begin -> begins both pads? maybe it does nothing debugPoints(event, " BEGIN ++++++++++++++++"); auto& virtualPadManager = VirtualPad::Manager::instance(); - if (!virtualPadManager.isEnabled()) { + if (!virtualPadManager.isEnabled() && !virtualPadManager.isHidden()) { return; } - KeyboardMouseDevice::enableTouch(false); } void TouchscreenVirtualPadDevice::touchEndEvent(const QTouchEvent* event) { auto& virtualPadManager = VirtualPad::Manager::instance(); - if (!virtualPadManager.isEnabled()) { - touchLeftEnd(); - touchRightEnd(); + if (!virtualPadManager.isEnabled() && !virtualPadManager.isHidden()) { + moveTouchEnd(); + viewTouchEnd(); return; } // touch end here is a big reset -> resets both pads _touchPointCount = 0; - KeyboardMouseDevice::enableTouch(true); + _unusedTouches.clear(); debugPoints(event, " END ----------------"); - touchLeftEnd(); - touchRightEnd(); + moveTouchEnd(); + viewTouchEnd(); _inputDevice->_axisStateMap.clear(); } +void TouchscreenVirtualPadDevice::processUnusedTouches(std::map unusedTouchesInEvent) { + std::vector touchesToDelete; + for (auto const& touchEntry : _unusedTouches) { + if (!unusedTouchesInEvent.count(touchEntry.first)) { + touchesToDelete.push_back(touchEntry.first); + } + } + for (int touchToDelete : touchesToDelete) { + _unusedTouches.erase(touchToDelete); + } + + for (auto const& touchEntry : unusedTouchesInEvent) { + if (!_unusedTouches.count(touchEntry.first)) { + _unusedTouches[touchEntry.first] = touchEntry.second; + } + } + +} + void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { auto& virtualPadManager = VirtualPad::Manager::instance(); - if (!virtualPadManager.isEnabled()) { - touchLeftEnd(); - touchRightEnd(); + if (!virtualPadManager.isEnabled() && !virtualPadManager.isHidden()) { + moveTouchEnd(); + viewTouchEnd(); return; } _touchPointCount = event->touchPoints().count(); const QList& tPoints = event->touchPoints(); - bool leftTouchFound = false; - bool rightTouchFound = false; + bool moveTouchFound = false; + bool viewTouchFound = false; + + int idxMoveStartingPointCandidate = -1; + int idxViewStartingPointCandidate = -1; + + glm::vec2 thisPoint; + int thisPointId; + std::map unusedTouchesInEvent; + for (int i = 0; i < _touchPointCount; ++i) { - glm::vec2 thisPoint(tPoints[i].pos().x(), tPoints[i].pos().y()); - if (_validTouchLeft) { - leftTouchFound = true; - touchLeftUpdate(thisPoint); - } else if (touchLeftBeginPointIsValid(thisPoint)) { - if (!leftTouchFound) { - leftTouchFound = true; - touchLeftBegin(thisPoint); - } + thisPoint.x = tPoints[i].pos().x(); + thisPoint.y = tPoints[i].pos().y(); + thisPointId = tPoints[i].id(); + + if (!moveTouchFound && _moveHasValidTouch && _moveCurrentTouchId == thisPointId) { + // valid if it's an ongoing touch + moveTouchFound = true; + moveTouchUpdate(thisPoint); + continue; + } + + if (!viewTouchFound && _viewHasValidTouch && _viewCurrentTouchId == thisPointId) { + // valid if it's an ongoing touch + viewTouchFound = true; + viewTouchUpdate(thisPoint); + continue; + } + + if (!moveTouchFound && idxMoveStartingPointCandidate == -1 && moveTouchBeginIsValid(thisPoint) && + (!_unusedTouches.count(thisPointId) || _unusedTouches[thisPointId] == MOVE )) { + idxMoveStartingPointCandidate = i; + continue; + } + + if (!viewTouchFound && idxViewStartingPointCandidate == -1 && viewTouchBeginIsValid(thisPoint) && + (!_unusedTouches.count(thisPointId) || _unusedTouches[thisPointId] == VIEW )) { + idxViewStartingPointCandidate = i; + continue; + } + + if (moveTouchBeginIsValid(thisPoint)) { + unusedTouchesInEvent[thisPointId] = MOVE; + } else if (viewTouchBeginIsValid(thisPoint)) { + unusedTouchesInEvent[thisPointId] = VIEW; + } + + } + + processUnusedTouches(unusedTouchesInEvent); + + if (!moveTouchFound) { + if (idxMoveStartingPointCandidate != -1) { + _moveCurrentTouchId = tPoints[idxMoveStartingPointCandidate].id(); + _unusedTouches.erase(_moveCurrentTouchId); + moveTouchBegin(thisPoint); } else { - if (!rightTouchFound) { - rightTouchFound = true; - if (!_validTouchRight) { - touchRightBegin(thisPoint); - } else { - touchRightUpdate(thisPoint); - } - } + moveTouchEnd(); } } - if (!leftTouchFound) { - touchLeftEnd(); - } - if (!rightTouchFound) { - touchRightEnd(); + if (!viewTouchFound) { + if (idxViewStartingPointCandidate != -1) { + _viewCurrentTouchId = tPoints[idxViewStartingPointCandidate].id(); + _unusedTouches.erase(_viewCurrentTouchId); + viewTouchBegin(thisPoint); + } else { + viewTouchEnd(); + } } + } -bool TouchscreenVirtualPadDevice::touchLeftBeginPointIsValid(glm::vec2 touchPoint) { +bool TouchscreenVirtualPadDevice::viewTouchBeginIsValid(glm::vec2 touchPoint) { + return !moveTouchBeginIsValid(touchPoint); +} + +bool TouchscreenVirtualPadDevice::moveTouchBeginIsValid(glm::vec2 touchPoint) { if (_fixedPosition) { // inside circle return pow(touchPoint.x - _fixedCenterPosition.x,2.0) + pow(touchPoint.y - _fixedCenterPosition.y, 2.0) < pow(_fixedRadius, 2.0); @@ -301,45 +347,46 @@ bool TouchscreenVirtualPadDevice::touchLeftBeginPointIsValid(glm::vec2 touchPoin } } -void TouchscreenVirtualPadDevice::touchLeftBegin(glm::vec2 touchPoint) { +void TouchscreenVirtualPadDevice::moveTouchBegin(glm::vec2 touchPoint) { auto& virtualPadManager = VirtualPad::Manager::instance(); - if (virtualPadManager.isEnabled()) { + if (virtualPadManager.isEnabled() && !virtualPadManager.isHidden()) { if (_fixedPosition) { - _firstTouchLeftPoint = _fixedCenterPosition; + _moveRefTouchPoint = _fixedCenterPosition; } else { - _firstTouchLeftPoint = touchPoint; + _moveRefTouchPoint = touchPoint; } - _validTouchLeft = true; + _moveHasValidTouch = true; } } -void TouchscreenVirtualPadDevice::touchLeftUpdate(glm::vec2 touchPoint) { - _currentTouchLeftPoint = touchPoint; +void TouchscreenVirtualPadDevice::moveTouchUpdate(glm::vec2 touchPoint) { + _moveCurrentTouchPoint = touchPoint; } -void TouchscreenVirtualPadDevice::touchLeftEnd() { - if (_validTouchLeft) { // do stuff once - _validTouchLeft = false; +void TouchscreenVirtualPadDevice::moveTouchEnd() { + if (_moveHasValidTouch) { // do stuff once + _moveHasValidTouch = false; _inputDevice->_axisStateMap[controller::LX] = 0; _inputDevice->_axisStateMap[controller::LY] = 0; } } -void TouchscreenVirtualPadDevice::touchRightBegin(glm::vec2 touchPoint) { +void TouchscreenVirtualPadDevice::viewTouchBegin(glm::vec2 touchPoint) { auto& virtualPadManager = VirtualPad::Manager::instance(); - if (virtualPadManager.isEnabled()) { - _firstTouchRightPoint = touchPoint; - _validTouchRight = true; + if (virtualPadManager.isEnabled() && !virtualPadManager.isHidden()) { + _viewRefTouchPoint = touchPoint; + _viewCurrentTouchPoint = touchPoint; + _viewHasValidTouch = true; } } -void TouchscreenVirtualPadDevice::touchRightUpdate(glm::vec2 touchPoint) { - _currentTouchRightPoint = touchPoint; +void TouchscreenVirtualPadDevice::viewTouchUpdate(glm::vec2 touchPoint) { + _viewCurrentTouchPoint = touchPoint; } -void TouchscreenVirtualPadDevice::touchRightEnd() { - if (_validTouchRight) { // do stuff once - _validTouchRight = false; +void TouchscreenVirtualPadDevice::viewTouchEnd() { + if (_viewHasValidTouch) { // do stuff once + _viewHasValidTouch = false; _inputDevice->_axisStateMap[controller::RX] = 0; _inputDevice->_axisStateMap[controller::RY] = 0; } @@ -347,7 +394,7 @@ void TouchscreenVirtualPadDevice::touchRightEnd() { void TouchscreenVirtualPadDevice::touchGestureEvent(const QGestureEvent* event) { auto& virtualPadManager = VirtualPad::Manager::instance(); - if (!virtualPadManager.isEnabled()) { + if (!virtualPadManager.isEnabled() && !virtualPadManager.isHidden()) { return; } if (QGesture* gesture = event->gesture(Qt::PinchGesture)) { diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h index d5019da805..3540c6d909 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.h @@ -20,8 +20,6 @@ class QTouchEvent; class QGestureEvent; -const float STICK_RADIUS_INCHES = .3f; - class TouchscreenVirtualPadDevice : public InputPlugin { Q_OBJECT public: @@ -62,17 +60,30 @@ public: const std::shared_ptr& getInputDevice() const { return _inputDevice; } protected: + + enum TouchType { + MOVE = 1, + VIEW + }; + float _lastPinchScale; float _pinchScale; float _screenDPI; qreal _screenDPIProvided; glm::vec2 _screenDPIScale; - bool _validTouchLeft; - glm::vec2 _firstTouchLeftPoint; - glm::vec2 _currentTouchLeftPoint; - bool _validTouchRight; - glm::vec2 _firstTouchRightPoint; - glm::vec2 _currentTouchRightPoint; + + bool _moveHasValidTouch; + glm::vec2 _moveRefTouchPoint; + glm::vec2 _moveCurrentTouchPoint; + int _moveCurrentTouchId; + + bool _viewHasValidTouch; + glm::vec2 _viewRefTouchPoint; + glm::vec2 _viewCurrentTouchPoint; + int _viewCurrentTouchId; + + std::map _unusedTouches; + int _touchPointCount; int _screenWidthCenter; std::shared_ptr _inputDevice { std::make_shared() }; @@ -83,18 +94,26 @@ protected: float _fixedRadiusForCalc; int _extraBottomMargin {0}; - void touchLeftBegin(glm::vec2 touchPoint); - void touchLeftUpdate(glm::vec2 touchPoint); - void touchLeftEnd(); - bool touchLeftBeginPointIsValid(glm::vec2 touchPoint); - void touchRightBegin(glm::vec2 touchPoint); - void touchRightUpdate(glm::vec2 touchPoint); - void touchRightEnd(); + float _viewStickRadiusInches {0.1333f}; // agreed default + + void moveTouchBegin(glm::vec2 touchPoint); + void moveTouchUpdate(glm::vec2 touchPoint); + void moveTouchEnd(); + bool moveTouchBeginIsValid(glm::vec2 touchPoint); + + void viewTouchBegin(glm::vec2 touchPoint); + void viewTouchUpdate(glm::vec2 touchPoint); + void viewTouchEnd(); + bool viewTouchBeginIsValid(glm::vec2 touchPoint); + void setupFixedCenter(VirtualPad::Manager& virtualPadManager, bool force = false); - void processInputUseCircleMethod(VirtualPad::Manager& virtualPadManager); - void processInputUseSquareMethod(VirtualPad::Manager& virtualPadManager); + void processInputDeviceForMove(VirtualPad::Manager& virtualPadManager); glm::vec2 clippedPointInCircle(float radius, glm::vec2 origin, glm::vec2 touchPoint); + + void processUnusedTouches(std::map unusedTouchesInEvent); + + void processInputDeviceForView(); // just for debug private: void debugPoints(const QTouchEvent* event, QString who); diff --git a/libraries/model-networking/src/model-networking/MaterialCache.cpp b/libraries/model-networking/src/model-networking/MaterialCache.cpp index cf3e255e0c..f0cbfc914a 100644 --- a/libraries/model-networking/src/model-networking/MaterialCache.cpp +++ b/libraries/model-networking/src/model-networking/MaterialCache.cpp @@ -20,7 +20,7 @@ void NetworkMaterialResource::downloadFinished(const QByteArray& data) { parsedMaterials.reset(); if (_url.toString().contains(".json")) { - parsedMaterials = parseJSONMaterials(QJsonDocument::fromJson(data)); + parsedMaterials = parseJSONMaterials(QJsonDocument::fromJson(data), _url); } // TODO: parse other material types @@ -75,7 +75,7 @@ bool NetworkMaterialResource::parseJSONColor(const QJsonValue& array, glm::vec3& * @property {number} materialVersion=1 - The version of the material. Currently not used. * @property {Material|Material[]} materials - The details of the material or materials. */ -NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMaterials(const QJsonDocument& materialJSON) { +NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl) { ParsedMaterials toReturn; if (!materialJSON.isNull() && materialJSON.isObject()) { QJsonObject materialJSONObject = materialJSON.object(); @@ -91,13 +91,13 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater QJsonArray materials = materialsValue.toArray(); for (auto material : materials) { if (!material.isNull() && material.isObject()) { - auto parsedMaterial = parseJSONMaterial(material.toObject()); + auto parsedMaterial = parseJSONMaterial(material.toObject(), baseUrl); toReturn.networkMaterials[parsedMaterial.first] = parsedMaterial.second; toReturn.names.push_back(parsedMaterial.first); } } } else if (materialsValue.isObject()) { - auto parsedMaterial = parseJSONMaterial(materialsValue.toObject()); + auto parsedMaterial = parseJSONMaterial(materialsValue.toObject(), baseUrl); toReturn.networkMaterials[parsedMaterial.first] = parsedMaterial.second; toReturn.names.push_back(parsedMaterial.first); } @@ -138,7 +138,7 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * @property {string} lightMap - URL of light map texture image. Currently not used. */ // Note: See MaterialEntityItem.h for default values used in practice. -std::pair> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON) { +std::pair> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl) { std::string name = ""; std::shared_ptr material = std::make_shared(); for (auto& key : materialJSON.keys()) { @@ -199,57 +199,58 @@ std::pair> NetworkMaterialResource } else if (key == "albedoMap") { auto value = materialJSON.value(key); if (value.isString()) { + QString urlString = value.toString(); bool useAlphaChannel = false; auto opacityMap = materialJSON.find("opacityMap"); - if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == value.toString()) { + if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == urlString) { useAlphaChannel = true; } - material->setAlbedoMap(value.toString(), useAlphaChannel); + material->setAlbedoMap(baseUrl.resolved(urlString), useAlphaChannel); } } else if (key == "roughnessMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setRoughnessMap(value.toString(), false); + material->setRoughnessMap(baseUrl.resolved(value.toString()), false); } } else if (key == "glossMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setRoughnessMap(value.toString(), true); + material->setRoughnessMap(baseUrl.resolved(value.toString()), true); } } else if (key == "metallicMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setMetallicMap(value.toString(), false); + material->setMetallicMap(baseUrl.resolved(value.toString()), false); } } else if (key == "specularMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setMetallicMap(value.toString(), true); + material->setMetallicMap(baseUrl.resolved(value.toString()), true); } } else if (key == "normalMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setNormalMap(value.toString(), false); + material->setNormalMap(baseUrl.resolved(value.toString()), false); } } else if (key == "bumpMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setNormalMap(value.toString(), true); + material->setNormalMap(baseUrl.resolved(value.toString()), true); } } else if (key == "occlusionMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setOcclusionMap(value.toString()); + material->setOcclusionMap(baseUrl.resolved(value.toString())); } } else if (key == "scatteringMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setScatteringMap(value.toString()); + material->setScatteringMap(baseUrl.resolved(value.toString())); } } else if (key == "lightMap") { auto value = materialJSON.value(key); if (value.isString()) { - material->setLightmapMap(value.toString()); + material->setLightmapMap(baseUrl.resolved(value.toString())); } } } diff --git a/libraries/model-networking/src/model-networking/MaterialCache.h b/libraries/model-networking/src/model-networking/MaterialCache.h index 468a12c677..074cd6c98d 100644 --- a/libraries/model-networking/src/model-networking/MaterialCache.h +++ b/libraries/model-networking/src/model-networking/MaterialCache.h @@ -37,8 +37,8 @@ public: ParsedMaterials parsedMaterials; - static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON); - static std::pair> parseJSONMaterial(const QJsonObject& materialJSON); + static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl); + static std::pair> parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl); private: static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB); diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index d21e942581..f17cdbb7e8 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -190,11 +190,11 @@ void GeometryReader::run() { throw QString("empty geometry, possibly due to an unsupported FBX version"); } } else if (_url.path().toLower().endsWith(".obj")) { - fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _combineParts, _url)); + fbxGeometry = OBJReader().readOBJ(_data, _mapping, _combineParts, _url); } else if (_url.path().toLower().endsWith(".obj.gz")) { QByteArray uncompressedData; - if (gunzip(_data, uncompressedData)) { - fbxGeometry.reset(OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url)); + if (gunzip(_data, uncompressedData)){ + fbxGeometry = OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url); } else { throw QString("failed to decompress .obj.gz"); } @@ -556,58 +556,58 @@ graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, im return nullptr; } -void NetworkMaterial::setAlbedoMap(const QString& url, bool useAlphaChannel) { - auto map = fetchTextureMap(QUrl(url), image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); +void NetworkMaterial::setAlbedoMap(const QUrl& url, bool useAlphaChannel) { + auto map = fetchTextureMap(url, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); if (map) { map->setUseAlphaChannel(useAlphaChannel); setTextureMap(MapChannel::ALBEDO_MAP, map); } } -void NetworkMaterial::setNormalMap(const QString& url, bool isBumpmap) { - auto map = fetchTextureMap(QUrl(url), isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP); +void NetworkMaterial::setNormalMap(const QUrl& url, bool isBumpmap) { + auto map = fetchTextureMap(url, isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE, MapChannel::NORMAL_MAP); if (map) { setTextureMap(MapChannel::NORMAL_MAP, map); } } -void NetworkMaterial::setRoughnessMap(const QString& url, bool isGloss) { - auto map = fetchTextureMap(QUrl(url), isGloss ? image::TextureUsage::GLOSS_TEXTURE : image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); +void NetworkMaterial::setRoughnessMap(const QUrl& url, bool isGloss) { + auto map = fetchTextureMap(url, isGloss ? image::TextureUsage::GLOSS_TEXTURE : image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); if (map) { setTextureMap(MapChannel::ROUGHNESS_MAP, map); } } -void NetworkMaterial::setMetallicMap(const QString& url, bool isSpecular) { - auto map = fetchTextureMap(QUrl(url), isSpecular ? image::TextureUsage::SPECULAR_TEXTURE : image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP); +void NetworkMaterial::setMetallicMap(const QUrl& url, bool isSpecular) { + auto map = fetchTextureMap(url, isSpecular ? image::TextureUsage::SPECULAR_TEXTURE : image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP); if (map) { setTextureMap(MapChannel::METALLIC_MAP, map); } } -void NetworkMaterial::setOcclusionMap(const QString& url) { - auto map = fetchTextureMap(QUrl(url), image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); +void NetworkMaterial::setOcclusionMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); if (map) { setTextureMap(MapChannel::OCCLUSION_MAP, map); } } -void NetworkMaterial::setEmissiveMap(const QString& url) { - auto map = fetchTextureMap(QUrl(url), image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); +void NetworkMaterial::setEmissiveMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); if (map) { setTextureMap(MapChannel::EMISSIVE_MAP, map); } } -void NetworkMaterial::setScatteringMap(const QString& url) { - auto map = fetchTextureMap(QUrl(url), image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP); +void NetworkMaterial::setScatteringMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP); if (map) { setTextureMap(MapChannel::SCATTERING_MAP, map); } } -void NetworkMaterial::setLightmapMap(const QString& url) { - auto map = fetchTextureMap(QUrl(url), image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); +void NetworkMaterial::setLightmapMap(const QUrl& url) { + auto map = fetchTextureMap(url, image::TextureUsage::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); if (map) { //map->setTextureTransform(_lightmapTransform); //map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index bbb00d72eb..4dfa8b17ea 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -164,14 +164,14 @@ public: NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl); NetworkMaterial(const NetworkMaterial& material); - void setAlbedoMap(const QString& url, bool useAlphaChannel); - void setNormalMap(const QString& url, bool isBumpmap); - void setRoughnessMap(const QString& url, bool isGloss); - void setMetallicMap(const QString& url, bool isSpecular); - void setOcclusionMap(const QString& url); - void setEmissiveMap(const QString& url); - void setScatteringMap(const QString& url); - void setLightmapMap(const QString& url); + void setAlbedoMap(const QUrl& url, bool useAlphaChannel); + void setNormalMap(const QUrl& url, bool isBumpmap); + void setRoughnessMap(const QUrl& url, bool isGloss); + void setMetallicMap(const QUrl& url, bool isSpecular); + void setOcclusionMap(const QUrl& url); + void setEmissiveMap(const QUrl& url); + void setScatteringMap(const QUrl& url); + void setLightmapMap(const QUrl& url); protected: friend class Geometry; diff --git a/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp b/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp index 741478789e..8f3d1ffec1 100644 --- a/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp +++ b/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp @@ -21,7 +21,7 @@ int SimpleMeshProxy::getNumVertices() const { return (int)_mesh->getNumVertices(); } -glm::vec3 SimpleMeshProxy::getPos3(int index) const { - return _mesh->getPos3(index); +glm::vec3 SimpleMeshProxy::getPos(int index) const { + return _mesh->getPos(index); } diff --git a/libraries/model-networking/src/model-networking/SimpleMeshProxy.h b/libraries/model-networking/src/model-networking/SimpleMeshProxy.h index 24c3fca27e..073eb1c00f 100644 --- a/libraries/model-networking/src/model-networking/SimpleMeshProxy.h +++ b/libraries/model-networking/src/model-networking/SimpleMeshProxy.h @@ -26,8 +26,8 @@ public: int getNumVertices() const override; - glm::vec3 getPos3(int index) const override; - + glm::vec3 getPos(int index) const override; + glm::vec3 getPos3(int index) const override { return getPos(index); } // deprecated protected: const MeshPointer _mesh; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index b03195041b..1954f40b44 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -38,11 +38,13 @@ #include #include #include +#include #include #include #include "NetworkLogging.h" #include "ModelNetworkingLogging.h" +#include "NetworkingConstants.h" #include #include @@ -467,7 +469,7 @@ void NetworkTexture::makeLocalRequest() { const QString scheme = _url.scheme(); QString path; if (scheme == URL_SCHEME_FILE) { - path = _url.toLocalFile(); + path = PathUtils::expandToLocalDataAbsolutePath(_url).toLocalFile(); } else { path = ":" + _url.path(); } diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index d0f175d3c2..7d7c2e682b 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "AddressManager.h" #include "NodeList.h" @@ -29,38 +30,20 @@ #include "UserActivityLogger.h" #include "udt/PacketHeaders.h" -#if USE_STABLE_GLOBAL_SERVICES -const QString DEFAULT_HIFI_ADDRESS = "hifi://welcome/hello"; -#else -const QString DEFAULT_HIFI_ADDRESS = "hifi://dev-welcome/hello"; -#endif - +const QString DEFAULT_HIFI_ADDRESS = "file:///~/serverless/tutorial.json"; const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; Setting::Handle currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_HIFI_ADDRESS); -AddressManager::AddressManager() : - _port(0) -{ - -} - bool AddressManager::isConnected() { return DependencyManager::get()->getDomainHandler().isConnected(); } QUrl AddressManager::currentAddress(bool domainOnly) const { - QUrl hifiURL; + QUrl hifiURL = _domainURL; - hifiURL.setScheme(HIFI_URL_SCHEME); - hifiURL.setHost(_host); - - if (_port != 0 && _port != DEFAULT_DOMAIN_SERVER_PORT) { - hifiURL.setPort(_port); - } - - if (!domainOnly) { + if (!domainOnly && hifiURL.scheme() == URL_SCHEME_HIFI) { hifiURL.setPath(currentPath()); } @@ -69,7 +52,9 @@ QUrl AddressManager::currentAddress(bool domainOnly) const { QUrl AddressManager::currentFacingAddress() const { auto hifiURL = currentAddress(); - hifiURL.setPath(currentFacingPath()); + if (hifiURL.scheme() == URL_SCHEME_HIFI) { + hifiURL.setPath(currentFacingPath()); + } return hifiURL; } @@ -79,7 +64,7 @@ QUrl AddressManager::currentShareableAddress(bool domainOnly) const { // if we have a shareable place name use that instead of whatever the current host is QUrl hifiURL; - hifiURL.setScheme(HIFI_URL_SCHEME); + hifiURL.setScheme(URL_SCHEME_HIFI); hifiURL.setHost(_shareablePlaceName); if (!domainOnly) { @@ -94,7 +79,9 @@ QUrl AddressManager::currentShareableAddress(bool domainOnly) const { QUrl AddressManager::currentFacingShareableAddress() const { auto hifiURL = currentShareableAddress(); - hifiURL.setPath(currentFacingPath()); + if (hifiURL.scheme() == URL_SCHEME_HIFI) { + hifiURL.setPath(currentFacingPath()); + } return hifiURL; } @@ -139,11 +126,16 @@ void AddressManager::goForward() { void AddressManager::storeCurrentAddress() { auto url = currentAddress(); - - if (!url.host().isEmpty()) { + + if (url.scheme() == 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 || currentAddressHandle.set(url); } else { - qCWarning(networking) << "Ignoring attempt to save current address with an empty host" << url; + qCWarning(networking) << "Ignoring attempt to save current address with an invalid url:" << url; } } @@ -209,7 +201,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { static QString URL_TYPE_DOMAIN_ID = "domain_id"; static QString URL_TYPE_PLACE = "place"; static QString URL_TYPE_NETWORK_ADDRESS = "network_address"; - if (lookupUrl.scheme() == HIFI_URL_SCHEME) { + if (lookupUrl.scheme() == URL_SCHEME_HIFI) { qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); @@ -289,12 +281,36 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { handlePath(lookupUrl.path(), trigger, true); emit lookupResultsFinished(); + return true; + } else if (lookupUrl.scheme() == 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 || + _previousLookup.clear(); + QUrl domainURL = PathUtils::expandToLocalDataAbsolutePath(lookupUrl); + setDomainInfo(domainURL, trigger); + emit lookupResultsFinished(); + handlePath(DOMAIN_SPAWNING_POINT, LookupTrigger::Internal, false); return true; } return false; } +bool isPossiblePlaceName(QString possiblePlaceName) { + bool result { false }; + int length = possiblePlaceName.length(); + static const int MINIMUM_PLACENAME_LENGTH = 1; + static const int MAXIMUM_PLACENAME_LENGTH = 64; + if (possiblePlaceName.toLower() != "localhost" && + length >= MINIMUM_PLACENAME_LENGTH && length <= MAXIMUM_PLACENAME_LENGTH) { + const QRegExp PLACE_NAME_REGEX = QRegExp("^[0-9A-Za-z](([0-9A-Za-z]|-(?!-))*[^\\W_]$|$)"); + result = PLACE_NAME_REGEX.indexIn(possiblePlaceName) == 0; + } + return result; +} + void AddressManager::handleLookupString(const QString& lookupString, bool fromSuggestions) { if (!lookupString.isEmpty()) { // make this a valid hifi URL and handle it off to handleUrl @@ -302,12 +318,16 @@ void AddressManager::handleLookupString(const QString& lookupString, bool fromSu QUrl lookupURL; if (!lookupString.startsWith('/')) { - const QRegExp HIFI_SCHEME_REGEX = QRegExp(HIFI_URL_SCHEME + ":\\/{1,2}", Qt::CaseInsensitive); + // sometimes we need to handle lookupStrings like hifi:/somewhere + const QRegExp HIFI_SCHEME_REGEX = QRegExp(URL_SCHEME_HIFI + ":\\/{1,2}", Qt::CaseInsensitive); sanitizedString = sanitizedString.remove(HIFI_SCHEME_REGEX); - lookupURL = QUrl(HIFI_URL_SCHEME + "://" + sanitizedString); + lookupURL = QUrl(sanitizedString); + if (lookupURL.scheme().isEmpty()) { + lookupURL = QUrl("hifi://" + sanitizedString); + } } else { - lookupURL = QUrl(lookupString); + lookupURL = QUrl(sanitizedString); } handleUrl(lookupURL, fromSuggestions ? Suggestions : UserInput); @@ -385,7 +405,11 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const qCDebug(networking) << "Possible domain change required to connect to" << domainHostname << "on" << domainPort; - emit possibleDomainChangeRequired(domainHostname, domainPort, domainID); + QUrl domainURL; + domainURL.setScheme(URL_SCHEME_HIFI); + domainURL.setHost(domainHostname); + domainURL.setPort(domainPort); + emit possibleDomainChangeRequired(domainURL, domainID); } else { QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString(); @@ -422,15 +446,10 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const if (setHost(placeName, trigger)) { trigger = LookupTrigger::Internal; } - - _placeName = placeName; } else { if (setHost(domainIDString, trigger)) { trigger = LookupTrigger::Internal; } - - // this isn't a place, so clear the place name - _placeName.clear(); } // check if we had a path to override the path returned @@ -551,13 +570,17 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTri if (ipAddressRegex.indexIn(lookupString) != -1) { QString domainIPString = ipAddressRegex.cap(1); - qint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT; + quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT; if (!ipAddressRegex.cap(2).isEmpty()) { - domainPort = (qint16) ipAddressRegex.cap(2).toInt(); + domainPort = (quint16) ipAddressRegex.cap(2).toInt(); } emit lookupResultsFinished(); - hostChanged = setDomainInfo(domainIPString, domainPort, trigger); + QUrl domainURL; + domainURL.setScheme(URL_SCHEME_HIFI); + domainURL.setHost(domainIPString); + domainURL.setPort(domainPort); + hostChanged = setDomainInfo(domainURL, trigger); return true; } @@ -570,11 +593,15 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTri quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT; if (!hostnameRegex.cap(2).isEmpty()) { - domainPort = (qint16)hostnameRegex.cap(2).toInt(); + domainPort = (quint16)hostnameRegex.cap(2).toInt(); } emit lookupResultsFinished(); - hostChanged = setDomainInfo(domainHostname, domainPort, trigger); + QUrl domainURL; + domainURL.setScheme(URL_SCHEME_HIFI); + domainURL.setHost(domainHostname); + domainURL.setPort(domainPort); + hostChanged = setDomainInfo(domainURL, trigger); return true; } @@ -643,7 +670,7 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should addCurrentAddressToHistory(trigger); } - if (!isNaN(newPosition.x) && !isNaN(newPosition.y) && !isNaN(newPosition.z)) { + if (!isNaN(newPosition)) { glm::quat newOrientation; QRegExp orientationRegex(QUAT_REGEX_STRING); @@ -663,11 +690,11 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should && !isNaN(newOrientation.w)) { orientationChanged = true; } else { - qCDebug(networking) << "Orientation parsed from lookup string is invalid. Will not use for location change."; + qCDebug(networking) << "Orientation parsed from lookup string is invalid. Won't use for location change."; } } - - emit locationChangeRequired(newPosition, orientationChanged, + + emit locationChangeRequired(newPosition, orientationChanged, trigger == LookupTrigger::VisitUserFromPAL ? cancelOutRollAndPitch(newOrientation): newOrientation, shouldFace ); @@ -698,18 +725,20 @@ bool AddressManager::handleUsername(const QString& lookupString) { } bool AddressManager::setHost(const QString& host, LookupTrigger trigger, quint16 port) { - if (host != _host || port != _port) { - + if (host != _domainURL.host() || port != _domainURL.port()) { addCurrentAddressToHistory(trigger); - _port = port; + bool emitHostChanged = host != _domainURL.host(); + _domainURL = QUrl(); + _domainURL.setScheme(URL_SCHEME_HIFI); + _domainURL.setHost(host); + _domainURL.setPort(port); // any host change should clear the shareable place name _shareablePlaceName.clear(); - if (host != _host) { - _host = host; - emit hostChanged(_host); + if (emitHostChanged) { + emit hostChanged(host); } return true; @@ -718,20 +747,43 @@ bool AddressManager::setHost(const QString& host, LookupTrigger trigger, quint16 return false; } -bool AddressManager::setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger) { - bool hostChanged = setHost(hostname, trigger, port); +QString AddressManager::getHost() const { + if (isPossiblePlaceName(_domainURL.host())) { + return QString(); + } + + return _domainURL.host(); +} + +bool AddressManager::setDomainInfo(const QUrl& domainURL, LookupTrigger trigger) { + const QString hostname = domainURL.host(); + quint16 port = domainURL.port(); + bool emitHostChanged { false }; + + if (domainURL != _domainURL) { + addCurrentAddressToHistory(trigger); + emitHostChanged = true; + } + + _domainURL = domainURL; // clear any current place information _rootPlaceID = QUuid(); - _placeName.clear(); - qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port; + if (_domainURL.scheme() == URL_SCHEME_HIFI) { + qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port; + } else { + qCDebug(networking) << "Possible domain change required to serverless domain: " << domainURL.toString(); + } DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); - emit possibleDomainChangeRequired(hostname, port, QUuid()); + if (emitHostChanged) { + emit hostChanged(domainURL.host()); + } + emit possibleDomainChangeRequired(_domainURL, QUuid()); - return hostChanged; + return emitHostChanged; } void AddressManager::goToUser(const QString& username, bool shouldMatchOrientation) { @@ -820,7 +872,7 @@ void AddressManager::lookupShareableNameForDomainID(const QUuid& domainID) { // then use that for Steam join/invite or copiable address // it only makes sense to lookup a shareable default name if we don't have a place name - if (_placeName.isEmpty()) { + if (getPlaceName().isEmpty()) { JSONCallbackParameters callbackParams; // no error callback handling @@ -872,3 +924,12 @@ void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) { } } +QString AddressManager::getPlaceName() const { + if (!_shareablePlaceName.isEmpty()) { + return _shareablePlaceName; + } + if (isPossiblePlaceName(_domainURL.host())) { + return _domainURL.host(); + } + return QString(); +} diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 1b550df693..dc1046bf51 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -22,8 +22,6 @@ #include "AccountManager.h" -const QString HIFI_URL_SCHEME = "hifi"; - extern const QString DEFAULT_HIFI_ADDRESS; const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost"; @@ -147,7 +145,7 @@ public: }; bool isConnected(); - const QString& getProtocol() { return HIFI_URL_SCHEME; }; + const QString& getProtocol() { return URL_SCHEME_HIFI; }; QUrl currentAddress(bool domainOnly = false) const; QUrl currentFacingAddress() const; @@ -157,10 +155,10 @@ public: QString currentFacingPath() const; const QUuid& getRootPlaceID() const { return _rootPlaceID; } - const QString& getPlaceName() const { return _shareablePlaceName.isEmpty() ? _placeName : _shareablePlaceName; } + QString getPlaceName() const; QString getDomainID() const; - const QString& getHost() const { return _host; } + QString getHost() const; void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } @@ -170,6 +168,8 @@ public: const QStack& getBackStack() const { return _backStack; } const QStack& getForwardStack() const { return _forwardStack; } + QUrl getDomainURL() { return _domainURL; } + public slots: /**jsdoc * Go to a specified metaverse address. @@ -302,13 +302,12 @@ signals: /**jsdoc * Triggered when a request is made to go to an IP address. * @function location.possibleDomainChangeRequired - * @param {string} hostName - The name of the domain to go do. - * @param {number} port - The integer number of the network port to connect to. + * @param {Url} domainURL - URL for domain * @param {Uuid} domainID - The UUID of the domain to go to. * @returns {Signal} */ // No example because this function isn't typically used in scripts. - void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort, const QUuid& domainID); + void possibleDomainChangeRequired(QUrl domainURL, QUuid domainID); /**jsdoc * Triggered when a request is made to go to a named domain or user. @@ -360,7 +359,7 @@ signals: * location.pathChangeRequired.connect(onPathChangeRequired); */ void pathChangeRequired(const QString& newPath); - + /**jsdoc * Triggered when you navigate to a new domain. * @function location.hostChanged @@ -392,7 +391,7 @@ signals: void goBackPossible(bool isPossible); /**jsdoc - * Triggered when there's a change in whether or not there's a forward location that can be navigated to using + * Triggered when there's a change in whether or not there's a forward location that can be navigated to using * {@link location.goForward|goForward}. (Reflects changes in the state of the "Goto" dialog's forward arrow.) * @function location.goForwardPossible * @param {boolean} isPossible - true if there's a forward location to navigate to, otherwise @@ -407,8 +406,6 @@ signals: */ void goForwardPossible(bool isPossible); -protected: - AddressManager(); private slots: void handleAPIResponse(QNetworkReply& requestReply); void handleAPIError(QNetworkReply& errorReply); @@ -420,7 +417,7 @@ private: // Set host and port, and return `true` if it was changed. bool setHost(const QString& host, LookupTrigger trigger, quint16 port = 0); - bool setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger); + bool setDomainInfo(const QUrl& domainURL, LookupTrigger trigger); const JSONCallbackParameters& apiCallbackParameters(); @@ -438,9 +435,8 @@ private: void addCurrentAddressToHistory(LookupTrigger trigger); - QString _host; - quint16 _port; - QString _placeName; + QUrl _domainURL; + QUuid _rootPlaceID; PositionGetter _positionGetter; OrientationGetter _orientationGetter; @@ -452,7 +448,7 @@ private: quint64 _lastBackPush = 0; QString _newHostLookupPath; - + QUrl _previousLookup; }; diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index d302c6fac6..8d3d313ff9 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -20,6 +20,7 @@ #include "NetworkAccessManager.h" #include "NetworkLogging.h" +#include "NetworkingConstants.h" #include "ResourceManager.h" diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 411f8f5be2..7a5ecb2602 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -38,7 +38,7 @@ DomainHandler::DomainHandler(QObject* parent) : // if we get a socket that make sure our NetworkPeer ping timer stops connect(this, &DomainHandler::completedSocketDiscovery, &_icePeer, &NetworkPeer::stopPingTimer); - + // setup a timeout for failure on settings requests static const int DOMAIN_SETTINGS_TIMEOUT_MS = 5000; _settingsTimer.setInterval(DOMAIN_SETTINGS_TIMEOUT_MS); // 5s, Qt::CoarseTimer acceptable @@ -60,11 +60,11 @@ void DomainHandler::disconnect() { if (_isConnected) { sendDisconnectPacket(); } - + // clear member variables that hold the connection state to a domain _uuid = QUuid(); _connectionToken = QUuid(); - + _icePeer.reset(); if (requiresICE()) { @@ -78,10 +78,10 @@ void DomainHandler::disconnect() { void DomainHandler::sendDisconnectPacket() { // The DomainDisconnect packet is not verified - we're relying on the eventual addition of DTLS to the // domain-server connection to stop greifing here - + // construct the disconnect packet once (an empty packet but sourced with our current session UUID) static auto disconnectPacket = NLPacket::create(PacketType::DomainDisconnectRequest, 0); - + // send the disconnect packet to the current domain server auto nodeList = DependencyManager::get(); nodeList->sendUnreliablePacket(*disconnectPacket, _sockAddr); @@ -94,7 +94,7 @@ void DomainHandler::clearSettings() { void DomainHandler::softReset() { qCDebug(networking) << "Resetting current domain connection information."; disconnect(); - + clearSettings(); _connectionDenialsSinceKeypairRegen = 0; @@ -115,8 +115,8 @@ void DomainHandler::hardReset() { qCDebug(networking) << "Hard reset in NodeList DomainHandler."; _pendingDomainID = QUuid(); _iceServerSockAddr = HifiSockAddr(); - _hostname = QString(); _sockAddr.clear(); + _domainURL = QUrl(); _domainConnectionRefusals.clear(); @@ -139,7 +139,10 @@ void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hos } // some callers may pass a hostname, this is not to be used for lookup but for DTLS certificate verification - _hostname = hostname; + _domainURL = QUrl(); + _domainURL.setScheme(URL_SCHEME_HIFI); + _domainURL.setHost(hostname); + _domainURL.setPort(_sockAddr.getPort()); } void DomainHandler::setUUID(const QUuid& uuid) { @@ -149,36 +152,45 @@ void DomainHandler::setUUID(const QUuid& uuid) { } } -void DomainHandler::setSocketAndID(const QString& hostname, quint16 port, const QUuid& domainID) { - +void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) { _pendingDomainID = domainID; - if (hostname != _hostname || _sockAddr.getPort() != port) { + if (domainURL.scheme() != URL_SCHEME_HIFI) { + _sockAddr.clear(); + } + + if (_domainURL != domainURL || _sockAddr.getPort() != domainURL.port()) { // re-set the domain info so that auth information is reloaded hardReset(); - if (hostname != _hostname) { - // set the new hostname - _hostname = hostname; + QString previousHost = _domainURL.host(); + _domainURL = domainURL; - qCDebug(networking) << "Updated domain hostname to" << _hostname; + if (domainURL.scheme() != URL_SCHEME_HIFI) { + setIsConnected(true); + } else if (previousHost != domainURL.host()) { + qCDebug(networking) << "Updated domain hostname to" << domainURL.host(); - // re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname - qCDebug(networking, "Looking up DS hostname %s.", _hostname.toLocal8Bit().constData()); - QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&))); + if (!domainURL.host().isEmpty()) { + // re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname + qCDebug(networking, "Looking up DS hostname %s.", domainURL.host().toLocal8Bit().constData()); + QHostInfo::lookupHost(domainURL.host(), this, SLOT(completedHostnameLookup(const QHostInfo&))); - DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainHostname); + DependencyManager::get()->flagTimeForConnectionStep( + LimitedNodeList::ConnectionStep::SetDomainHostname); - UserActivityLogger::getInstance().changedDomain(_hostname); - emit hostnameChanged(_hostname); + UserActivityLogger::getInstance().changedDomain(domainURL.host()); + } } - if (_sockAddr.getPort() != port) { - qCDebug(networking) << "Updated domain port to" << port; + emit domainURLChanged(_domainURL); + + if (_sockAddr.getPort() != domainURL.port()) { + qCDebug(networking) << "Updated domain port to" << domainURL.port(); } // grab the port by reading the string after the colon - _sockAddr.setPort(port); + _sockAddr.setPort(domainURL.port()); } } @@ -187,10 +199,10 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, if (_iceServerSockAddr.getAddress().toString() != iceServerHostname || id != _pendingDomainID) { // re-set the domain info to connect to new domain hardReset(); - + // refresh our ICE client UUID to something new _iceClientID = QUuid::createUuid(); - + _pendingDomainID = id; HifiSockAddr* replaceableSockAddr = &_iceServerSockAddr; @@ -216,14 +228,18 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, void DomainHandler::activateICELocalSocket() { DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket); _sockAddr = _icePeer.getLocalSocket(); - _hostname = _sockAddr.getAddress().toString(); + _domainURL.setScheme(URL_SCHEME_HIFI); + _domainURL.setHost(_sockAddr.getAddress().toString()); + emit domainURLChanged(_domainURL); emit completedSocketDiscovery(); } void DomainHandler::activateICEPublicSocket() { DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket); _sockAddr = _icePeer.getPublicSocket(); - _hostname = _sockAddr.getAddress().toString(); + _domainURL.setScheme(URL_SCHEME_HIFI); + _domainURL.setHost(_sockAddr.getAddress().toString()); + emit domainURLChanged(_domainURL); emit completedSocketDiscovery(); } @@ -234,7 +250,7 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket); - qCDebug(networking, "DS at %s is at %s", _hostname.toLocal8Bit().constData(), + qCDebug(networking, "DS at %s is at %s", _domainURL.host().toLocal8Bit().constData(), _sockAddr.getAddress().toString().toLocal8Bit().constData()); emit completedSocketDiscovery(); @@ -261,10 +277,12 @@ void DomainHandler::setIsConnected(bool isConnected) { _isConnected = isConnected; if (_isConnected) { - emit connectedToDomain(_hostname); + emit connectedToDomain(_domainURL); - // we've connected to new domain - time to ask it for global settings - requestDomainSettings(); + if (_domainURL.scheme() == URL_SCHEME_HIFI && !_domainURL.host().isEmpty()) { + // we've connected to new domain - time to ask it for global settings + requestDomainSettings(); + } } else { emit disconnectedFromDomain(); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 78f9798089..fbc60e2492 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -25,6 +25,7 @@ #include "NLPacketList.h" #include "Node.h" #include "ReceivedMessage.h" +#include "NetworkingConstants.h" const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102; const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103; @@ -37,14 +38,14 @@ class DomainHandler : public QObject { Q_OBJECT public: DomainHandler(QObject* parent = 0); - + void disconnect(); void clearSettings(); const QUuid& getUUID() const { return _uuid; } void setUUID(const QUuid& uuid); - const QString& getHostname() const { return _hostname; } + QString getHostname() const { return _domainURL.host(); } const QHostAddress& getIP() const { return _sockAddr.getAddress(); } void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); } @@ -57,7 +58,7 @@ public: const QUuid& getConnectionToken() const { return _connectionToken; } void setConnectionToken(const QUuid& connectionToken) { _connectionToken = connectionToken; } - + const QUuid& getAssignmentUUID() const { return _assignmentUUID; } void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } @@ -73,11 +74,12 @@ public: bool isConnected() const { return _isConnected; } void setIsConnected(bool isConnected); + bool isServerless() const { return _domainURL.scheme() != URL_SCHEME_HIFI; } bool hasSettings() const { return !_settingsObject.isEmpty(); } void requestDomainSettings(); const QJsonObject& getSettingsObject() const { return _settingsObject; } - + void setPendingPath(const QString& pendingPath) { _pendingPath = pendingPath; } const QString& getPendingPath() { return _pendingPath; } void clearPendingPath() { _pendingPath.clear(); } @@ -139,7 +141,7 @@ public: }; public slots: - void setSocketAndID(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT, const QUuid& id = QUuid()); + void setURLAndID(QUrl domainURL, QUuid id); void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id); void processSettingsPacketList(QSharedPointer packetList); @@ -153,14 +155,14 @@ private slots: void completedIceServerHostnameLookup(); signals: - void hostnameChanged(const QString& hostname); + void domainURLChanged(QUrl domainURL); // NOTE: the emission of completedSocketDiscovery does not mean a connection to DS is established // It means that, either from DNS lookup or ICE, we think we have a socket we can talk to DS on void completedSocketDiscovery(); void resetting(); - void connectedToDomain(const QString& hostname); + void connectedToDomain(QUrl domainURL); void disconnectedFromDomain(); void iceSocketAndIDReceived(); @@ -179,7 +181,7 @@ private: void hardReset(); QUuid _uuid; - QString _hostname; + QUrl _domainURL; HifiSockAddr _sockAddr; QUuid _assignmentUUID; QUuid _connectionToken; @@ -200,4 +202,7 @@ private: QTimer _apiRefreshTimer; }; +const QString DOMAIN_SPAWNING_POINT { "/0, -10, 0" }; + + #endif // hifi_DomainHandler_h diff --git a/libraries/networking/src/FileResourceRequest.cpp b/libraries/networking/src/FileResourceRequest.cpp index 7cbb401f7f..1a4096bc02 100644 --- a/libraries/networking/src/FileResourceRequest.cpp +++ b/libraries/networking/src/FileResourceRequest.cpp @@ -17,9 +17,11 @@ #include #include +#include #include "NetworkLogging.h" #include "ResourceManager.h" +#include "NetworkingConstants.h" void FileResourceRequest::doSend() { auto statTracker = DependencyManager::get(); @@ -29,7 +31,7 @@ void FileResourceRequest::doSend() { if (_url.scheme() == URL_SCHEME_QRC) { filename = ":/" + _url.path(); } else { - filename = _url.toLocalFile(); + filename = PathUtils::expandToLocalDataAbsolutePath(_url).toLocalFile(); // sometimes on windows, we see the toLocalFile() return null, // in this case we will attempt to simply use the url as a string if (filename.isEmpty()) { diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h index 6d0483fe9d..6ef3777d8c 100644 --- a/libraries/networking/src/MessagesClient.h +++ b/libraries/networking/src/MessagesClient.h @@ -23,6 +23,21 @@ #include "Node.h" #include "ReceivedMessage.h" +/**jsdoc + *

The Messages API enables text and data to be sent between scripts over named "channels". A channel can have an arbitrary + * name to help separate messaging between different sets of scripts.

+ * + *

Note: If you want to call a function in another script, you should use one of the following rather than + * sending a message:

+ *
    + *
  • {@link Entities.callEntityClientMethod}
  • + *
  • {@link Entities.callEntityMethod}
  • + *
  • {@link Entities.callEntityServerMethod}
  • + *
  • {@link Script.callEntityScriptMethod}
  • + *
+ * + * @namespace Messages + */ class MessagesClient : public QObject, public Dependency { Q_OBJECT public: @@ -30,10 +45,115 @@ public: void startThread(); + /**jsdoc + * Send a text message on a channel. + * @function Messages.sendMessage + * @param {string} channel - The channel to send the message on. + * @param {string} message - The message to send. + * @param {boolean} [localOnly=false] - If false then the message is sent to all Interface, client entity, + * server entity, and assignment client scripts in the domain.
+ * If true then: if sent from an Interface or client entity script it is received by all Interface and + * client entity scripts; if sent from a server entity script it is received by all entity server scripts; and if sent + * from an assignment client script it is received only by that same assignment client script. + * @example Send and receive a message. + * // Receiving script. + * var channelName = "com.highfidelity.example.messages-example"; + * + * function onMessageReceived(channel, message, sender, localOnly) { + * print("Message received:"); + * print("- channel: " + channel); + * print("- message: " + message); + * print("- sender: " + sender); + * print("- localOnly: " + localOnly); + * } + * + * Messages.subscribe(channelName); + * Messages.messageReceived.connect(onMessageReceived); + * + * Script.scriptEnding.connect(function () { + * Messages.messageReceived.disconnect(onMessageReceived); + * Messages.unsubscribe(channelName); + * }); + * + * + * // Sending script. + * var channelName = "com.highfidelity.example.messages-example"; + * var message = "Hello"; + * Messages.sendMessage(channelName, message); + */ Q_INVOKABLE void sendMessage(QString channel, QString message, bool localOnly = false); + + /**jsdoc + * Send a text message locally on a channel. + * This is the same as calling {@link Messages.sendMessage|sendMessage} with localOnly set to + * true. + * @function Messages.sendLocalMessage + * @param {string} channel - The channel to send the message on. + * @param {string} message - The message to send. + */ Q_INVOKABLE void sendLocalMessage(QString channel, QString message); + + /**jsdoc + * Send a data message on a channel. + * @function Messages.sendData + * @param {string} channel - The channel to send the data on. + * @param {object} data - The data to send. The data is handled as a byte stream, for example as may be provided via a + * JavaScript Int8Array object. + * @param {boolean} [localOnly=false] - If false then the message is sent to all Interface, client entity, + * server entity, and assignment client scripts in the domain.
+ * If true then: if sent from an Interface or client entity script it is received by all Interface and + * client entity scripts; if sent from a server entity script it is received by all entity server scripts; and if sent + * from an assignment client script it is received only by that same assignment client script. + * @example Send and receive data. + * // Receiving script. + * var channelName = "com.highfidelity.example.messages-example"; + * + * function onDataReceived(channel, data, sender, localOnly) { + * var int8data = new Int8Array(data); + * var dataAsString = ""; + * for (var i = 0; i < int8data.length; i++) { + * if (i > 0) { + * dataAsString += ", "; + * } + * dataAsString += int8data[i]; + * } + * print("Data received:"); + * print("- channel: " + channel); + * print("- data: " + dataAsString); + * print("- sender: " + sender); + * print("- localOnly: " + localOnly); + * } + * + * Messages.subscribe(channelName); + * Messages.dataReceived.connect(onDataReceived); + * + * Script.scriptEnding.connect(function () { + * Messages.dataReceived.disconnect(onDataReceived); + * Messages.unsubscribe(channelName); + * }); + * + * + * // Sending script. + * var channelName = "com.highfidelity.example.messages-example"; + * var int8data = new Int8Array([1, 1, 2, 3, 5, 8, 13]); + * Messages.sendData(channelName, int8data.buffer); + */ Q_INVOKABLE void sendData(QString channel, QByteArray data, bool localOnly = false); + + /**jsdoc + * Subscribe the scripting environment — Interface, the entity script server, or assignment client instance — + * to receive messages on a specific channel. Note that, for example, if there are two Interface scripts that subscribe to + * different channels, both scripts will receive messages on both channels. + * @function Messages.subscribe + * @param {string} channel - The channel to subscribe to. + */ Q_INVOKABLE void subscribe(QString channel); + + /**jsdoc + * Unsubscribe the scripting environment from receiving messages on a specific channel. + * @function Messages.unsubscribe + * @param {string} channel - The channel to unsubscribe from. + */ Q_INVOKABLE void unsubscribe(QString channel); static void decodeMessagesPacket(QSharedPointer receivedMessage, QString& channel, @@ -43,7 +163,34 @@ public: static std::unique_ptr encodeMessagesDataPacket(QString channel, QByteArray data, QUuid senderID); signals: + /**jsdoc + * Triggered when the a text message is received. + * @function Messages.messageReceived + * @param {string} channel - The channel that the message was sent on. You can use this to filter out messages not relevant + * to your script. + * @param {string} message - The message received. + * @param {Uuid} senderID - The UUID of the sender: the user's session UUID if sent by an Interface or client entity + * script, the UUID of the entity script server if sent by a server entity script, or the UUID of the assignment client + * instance if sent by an assignment client script. + * @param {boolean} localOnly - true if the message was sent with localOnly = true. + * @returns {Signal} + */ void messageReceived(QString channel, QString message, QUuid senderUUID, bool localOnly); + + /**jsdoc + * Triggered when a data message is received. + * @function Messages.dataReceived + * @param {string} channel - The channel that the message was sent on. You can use this to filter out messages not relevant + * to your script. + * @param {object} data - The data received. The data is handled as a byte stream, for example as may be used by a + * JavaScript Int8Array object. + * @param {Uuid} senderID - The UUID of the sender: the user's session UUID if sent by an Interface or client entity + * script, the UUID of the entity script server if sent by a server entity script, or the UUID of the assignment client + * script, the UUID of the entity script server if sent by a server entity script, or the UUID of the assignment client + * instance if sent by an assignment client script. + * @param {boolean} localOnly - true if the message was sent with localOnly = true. + * @returns {Signal} + */ void dataReceived(QString channel, QByteArray data, QUuid senderUUID, bool localOnly); private slots: diff --git a/libraries/networking/src/NetworkingConstants.cpp b/libraries/networking/src/NetworkingConstants.cpp new file mode 100644 index 0000000000..622c307efa --- /dev/null +++ b/libraries/networking/src/NetworkingConstants.cpp @@ -0,0 +1,24 @@ +// +// NetworkingConstants.cpp +// libraries/networking/src +// +// Created by Seth Alves on 2018-2-28. +// 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 "NetworkingConstants.h" + +namespace NetworkingConstants { + // You can change the return of this function if you want to use a custom metaverse URL at compile time + // or you can pass a custom URL via the env variable + QUrl METAVERSE_SERVER_URL() { + const QString HIFI_METAVERSE_URL_ENV = "HIFI_METAVERSE_URL"; + const QUrl serverURL = QProcessEnvironment::systemEnvironment().contains(HIFI_METAVERSE_URL_ENV) + ? QUrl(QProcessEnvironment::systemEnvironment().value(HIFI_METAVERSE_URL_ENV)) + : METAVERSE_SERVER_URL_STABLE; + return serverURL; + }; +} diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index a4726f9b1a..8eb1e71ed6 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -25,18 +25,17 @@ namespace NetworkingConstants { // if you manually generate a personal access token for the domains scope // at https://staging.highfidelity.com/user/tokens/new?for_domain_server=true - const QUrl METAVERSE_SERVER_URL_STABLE("https://metaverse.highfidelity.com"); - const QUrl METAVERSE_SERVER_URL_STAGING("https://staging.highfidelity.com"); - - // You can change the return of this function if you want to use a custom metaverse URL at compile time - // or you can pass a custom URL via the env variable - static const QUrl METAVERSE_SERVER_URL() { - static const QString HIFI_METAVERSE_URL_ENV = "HIFI_METAVERSE_URL"; - static const QUrl serverURL = QProcessEnvironment::systemEnvironment().contains(HIFI_METAVERSE_URL_ENV) - ? QUrl(QProcessEnvironment::systemEnvironment().value(HIFI_METAVERSE_URL_ENV)) - : METAVERSE_SERVER_URL_STABLE; - return serverURL; - }; + const QUrl METAVERSE_SERVER_URL_STABLE { "https://metaverse.highfidelity.com" }; + const QUrl METAVERSE_SERVER_URL_STAGING { "https://staging.highfidelity.com" }; + QUrl METAVERSE_SERVER_URL(); } +const QString URL_SCHEME_HIFI = "hifi"; +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 URL_SCHEME_ATP = "atp"; + #endif // hifi_NetworkingConstants_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 71d448ede9..cb0d2e4cd5 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -55,7 +55,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) // handle domain change signals from AddressManager connect(addressManager.data(), &AddressManager::possibleDomainChangeRequired, - &_domainHandler, &DomainHandler::setSocketAndID); + &_domainHandler, &DomainHandler::setURLAndID); connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID, &_domainHandler, &DomainHandler::setIceServerHostnameAndID); @@ -91,7 +91,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) connect(accountManager.data(), &AccountManager::newKeypair, this, &NodeList::sendDomainServerCheckIn); // clear out NodeList when login is finished - connect(accountManager.data(), SIGNAL(loginComplete()) , this, SLOT(reset())); + connect(accountManager.data(), SIGNAL(loginComplete(const QUrl&)) , this, SLOT(reset())); // clear our NodeList when logout is requested connect(accountManager.data(), SIGNAL(logoutComplete()) , this, SLOT(reset())); @@ -106,7 +106,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) // setup our timer to send keepalive pings (it's started and stopped on domain connect/disconnect) _keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS); // 1s, Qt::CoarseTimer acceptable connect(&_keepAlivePingTimer, &QTimer::timeout, this, &NodeList::sendKeepAlivePings); - connect(&_domainHandler, SIGNAL(connectedToDomain(QString)), &_keepAlivePingTimer, SLOT(start())); + connect(&_domainHandler, SIGNAL(connectedToDomain(QUrl)), &_keepAlivePingTimer, SLOT(start())); connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop); // set our sockAddrBelongsToDomainOrNode method as the connection creation filter for the udt::Socket diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index 317cabdc61..d06b74b724 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -26,12 +26,14 @@ #include "NetworkAccessManager.h" #include "NetworkLogging.h" -ResourceManager::ResourceManager() { +ResourceManager::ResourceManager(bool atpSupportEnabled) : _atpSupportEnabled(atpSupportEnabled) { _thread.setObjectName("Resource Manager Thread"); - auto assetClient = DependencyManager::set(); - assetClient->moveToThread(&_thread); - QObject::connect(&_thread, &QThread::started, assetClient.data(), &AssetClient::initCaching); + if (_atpSupportEnabled) { + auto assetClient = DependencyManager::set(); + assetClient->moveToThread(&_thread); + QObject::connect(&_thread, &QThread::started, assetClient.data(), &AssetClient::initCaching); + } _thread.start(); } @@ -111,6 +113,10 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { request = new HTTPResourceRequest(normalizedURL); } else if (scheme == URL_SCHEME_ATP) { + if (!_atpSupportEnabled) { + qCDebug(networking) << "ATP support not enabled, unable to create request for URL: " << url.url(); + return nullptr; + } request = new AssetResourceRequest(normalizedURL); } else { qCDebug(networking) << "Unknown scheme (" << scheme << ") for URL: " << url.url(); @@ -146,7 +152,7 @@ bool ResourceManager::resourceExists(const QUrl& url) { reply->deleteLater(); return reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200; - } else if (scheme == URL_SCHEME_ATP) { + } else if (scheme == URL_SCHEME_ATP && _atpSupportEnabled) { auto request = new AssetResourceRequest(url); ByteRange range; range.fromInclusive = 1; diff --git a/libraries/networking/src/ResourceManager.h b/libraries/networking/src/ResourceManager.h index 5728a7bd32..9fc636f5fe 100644 --- a/libraries/networking/src/ResourceManager.h +++ b/libraries/networking/src/ResourceManager.h @@ -22,19 +22,12 @@ #include "ResourceRequest.h" -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 URL_SCHEME_ATP = "atp"; - class ResourceManager: public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY public: - ResourceManager(); + ResourceManager(bool atpSupportEnabled = true); void setUrlPrefixOverride(const QString& prefix, const QString& replacement); QString normalizeURL(const QString& urlString); @@ -57,6 +50,7 @@ private: using PrefixMap = std::map; + bool _atpSupportEnabled; PrefixMap _prefixMap; QMutex _prefixMapLock; diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index b34c05106f..c1fe6ccd85 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -576,7 +576,7 @@ void Connection::processControl(ControlPacketPointer controlPacket) { // where the other end expired our connection. Let's reset. #ifdef UDT_CONNECTION_DEBUG - qCDebug(networking) << "Got handshake request, stopping SendQueue"; + qCDebug(networking) << "Got HandshakeRequest from" << _destination << ", stopping SendQueue"; #endif _hasReceivedHandshakeACK = false; stopSendQueue(); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 98a9087d37..09fd31a41e 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -121,7 +121,7 @@ public: ReplicatedAvatarIdentity, ReplicatedKillAvatar, ReplicatedBulkAvatarData, - OctreeFileReplacementFromUrl, + DomainContentReplacementFromUrl, ChallengeOwnership, EntityScriptCallMethod, ChallengeOwnershipRequest, @@ -171,7 +171,7 @@ public: << PacketTypeEnum::Value::DomainServerPathResponse << PacketTypeEnum::Value::DomainServerAddedNode << PacketTypeEnum::Value::DomainServerConnectionToken << PacketTypeEnum::Value::DomainSettingsRequest << PacketTypeEnum::Value::OctreeDataFileRequest << PacketTypeEnum::Value::OctreeDataFileReply - << PacketTypeEnum::Value::OctreeDataPersist << PacketTypeEnum::Value::OctreeFileReplacementFromUrl + << PacketTypeEnum::Value::OctreeDataPersist << PacketTypeEnum::Value::DomainContentReplacementFromUrl << PacketTypeEnum::Value::DomainSettings << PacketTypeEnum::Value::ICEServerPeerInformation << PacketTypeEnum::Value::ICEServerQuery << PacketTypeEnum::Value::ICEServerHeartbeat << PacketTypeEnum::Value::ICEServerHeartbeatACK << PacketTypeEnum::Value::ICEPing diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 8b93a05130..4189cb613c 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -402,6 +402,10 @@ void Socket::readPendingDatagrams() { packet->getDataSize(), packet->getPayloadSize())) { // the connection could not be created or indicated that we should not continue processing this packet +#ifdef UDT_CONNECTION_DEBUG + qCDebug(networking) << "Can't process packet: version" << (unsigned int)NLPacket::versionInHeader(*packet) + << ", type" << NLPacket::typeInHeader(*packet); +#endif continue; } } diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 7e0c82506b..dafdfd5bf4 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1685,6 +1685,15 @@ bool Octree::readFromURL(const QString& urlString) { } auto data = request->getData(); + + QByteArray uncompressedJsonData; + bool wasCompressed = gunzip(data, uncompressedJsonData); + + if (wasCompressed) { + QDataStream inputStream(uncompressedJsonData); + return readFromStream(uncompressedJsonData.size(), inputStream, marketplaceID); + } + QDataStream inputStream(data); return readFromStream(data.size(), inputStream, marketplaceID); } diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index e95084df52..ff6801109e 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -140,9 +140,6 @@ public: virtual void setContext(const gpu::ContextPointer& context) final { _gpuContext = context; } virtual void submitFrame(const gpu::FramePointer& newFrame) = 0; - // Does the rendering surface have current focus? - virtual bool hasFocus() const = 0; - // The size of the rendering target (may be larger than the device size due to distortion) virtual glm::uvec2 getRecommendedRenderSize() const = 0; diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp index 704111762a..9c1bb79355 100644 --- a/libraries/qml/src/qml/OffscreenSurface.cpp +++ b/libraries/qml/src/qml/OffscreenSurface.cpp @@ -166,18 +166,30 @@ bool OffscreenSurface::eventFilter(QObject* originalDestination, QEvent* event) case QEvent::TouchUpdate: case QEvent::TouchEnd: { QTouchEvent *originalEvent = static_cast(event); - QTouchEvent fakeEvent(*originalEvent); - auto newTouchPoints = fakeEvent.touchPoints(); - for (size_t i = 0; i < newTouchPoints.size(); ++i) { - const auto &originalPoint = originalEvent->touchPoints()[i]; - auto &newPoint = newTouchPoints[i]; - newPoint.setPos(originalPoint.pos()); + QEvent::Type fakeMouseEventType = QEvent::None; + Qt::MouseButton fakeMouseButton = Qt::LeftButton; + Qt::MouseButtons fakeMouseButtons = Qt::NoButton; + switch (event->type()) { + case QEvent::TouchBegin: + fakeMouseEventType = QEvent::MouseButtonPress; + fakeMouseButtons = Qt::LeftButton; + break; + case QEvent::TouchUpdate: + fakeMouseEventType = QEvent::MouseMove; + fakeMouseButtons = Qt::LeftButton; + break; + case QEvent::TouchEnd: + fakeMouseEventType = QEvent::MouseButtonRelease; + fakeMouseButtons = Qt::NoButton; + break; } - fakeEvent.setTouchPoints(newTouchPoints); - if (QCoreApplication::sendEvent(_sharedObject->getWindow(), &fakeEvent)) { - qInfo() << __FUNCTION__ << "sent fake touch event:" << fakeEvent.type() - << "_quickWindow handled it... accepted:" << fakeEvent.isAccepted(); - return false; //event->isAccepted(); + // Same case as OffscreenUi.cpp::eventFilter: touch events are always being accepted so we now use mouse events and consider one touch, touchPoints()[0]. + QMouseEvent fakeMouseEvent(fakeMouseEventType, originalEvent->touchPoints()[0].pos(), fakeMouseButton, fakeMouseButtons, Qt::NoModifier); + fakeMouseEvent.ignore(); + if (QCoreApplication::sendEvent(_sharedObject->getWindow(), &fakeMouseEvent)) { + /*qInfo() << __FUNCTION__ << "sent fake touch event:" << fakeMouseEvent.type() + << "_quickWindow handled it... accepted:" << fakeMouseEvent.isAccepted();*/ + return fakeMouseEvent.isAccepted(); } break; } @@ -188,11 +200,13 @@ bool OffscreenSurface::eventFilter(QObject* originalDestination, QEvent* event) event->ignore(); if (QCoreApplication::sendEvent(window->activeFocusItem(), event)) { bool eventAccepted = event->isAccepted(); - QInputMethodQueryEvent* imqEvent = static_cast(event); - // this block disables the selection cursor in android which appears in - // the top-left corner of the screen - if (imqEvent->queries() & Qt::ImEnabled) { - imqEvent->setValue(Qt::ImEnabled, QVariant(false)); + if (event->type() == QEvent::InputMethodQuery) { + QInputMethodQueryEvent *imqEvent = static_cast(event); + // this block disables the selection cursor in android which appears in + // the top-left corner of the screen + if (imqEvent->queries() & Qt::ImEnabled) { + imqEvent->setValue(Qt::ImEnabled, QVariant(false)); + } } return eventAccepted; } diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp index f2af5bd036..d66f0f1dab 100644 --- a/libraries/qml/src/qml/impl/SharedObject.cpp +++ b/libraries/qml/src/qml/impl/SharedObject.cpp @@ -69,6 +69,7 @@ SharedObject::SharedObject() { _quickWindow->setColor(QColor(255, 255, 255, 0)); _quickWindow->setClearBeforeRendering(true); + QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &SharedObject::onAboutToQuit); } @@ -124,6 +125,7 @@ void SharedObject::setRootItem(QQuickItem* rootItem) { _renderThread->setObjectName(objectName()); _renderThread->start(); + // Create event handler for the render thread _renderObject = new RenderEventHandler(this, _renderThread); QCoreApplication::postEvent(this, new OffscreenEvent(OffscreenEvent::Initialize)); @@ -152,9 +154,16 @@ void SharedObject::destroy() { QObject::disconnect(_renderControl); QObject::disconnect(qApp); - QMutexLocker lock(&_mutex); - _quit = true; - QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Quit)); + { + QMutexLocker lock(&_mutex); + _quit = true; + QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Quit), Qt::HighEventPriority); + } + // Block until the rendering thread has stopped + // FIXME this is undesirable because this is blocking the main thread, + // but I haven't found a reliable way to do this only at application + // shutdown + _renderThread->wait(); } diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 7fece45b2f..319b6ad415 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -8,6 +8,9 @@ include_hifi_library_headers(audio) include_hifi_library_headers(networking) include_hifi_library_headers(octree) +# tell CMake to exclude qrc_fonts.cpp for policy CMP0071 +set_property(SOURCE qrc_fonts.cpp PROPERTY SKIP_AUTOMOC ON) + if (NOT ANDROID) target_nsight() endif () diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 02ff234c01..90424b04b2 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -9,6 +9,7 @@ #include "AnimDebugDraw.h" +#include #include #include @@ -21,7 +22,6 @@ #include "animdebugdraw_vert.h" #include "animdebugdraw_frag.h" - class AnimDebugDrawData { public: diff --git a/libraries/render-utils/src/AntialiasingEffect.h b/libraries/render-utils/src/AntialiasingEffect.h index da9a0bd384..03fdb9d9a4 100644 --- a/libraries/render-utils/src/AntialiasingEffect.h +++ b/libraries/render-utils/src/AntialiasingEffect.h @@ -88,7 +88,7 @@ class AntialiasingConfig : public render::Job::Config { Q_PROPERTY(float blend MEMBER blend NOTIFY dirty) Q_PROPERTY(float sharpen MEMBER sharpen NOTIFY dirty) Q_PROPERTY(float covarianceGamma MEMBER covarianceGamma NOTIFY dirty) - + Q_PROPERTY(bool constrainColor MEMBER constrainColor NOTIFY dirty) Q_PROPERTY(bool feedbackColor MEMBER feedbackColor NOTIFY dirty) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index bf265fae8c..3fd18a0eaf 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -100,6 +100,8 @@ static const int VERTICES_PER_TRIANGLE = 3; static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; +static const gpu::Element TEXCOORD0_ELEMENT { gpu::VEC2, gpu::FLOAT, gpu::UV }; +static const gpu::Element TANGENT_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA }; static const gpu::Element TEXCOORD4_ELEMENT { gpu::VEC4, gpu::FLOAT, gpu::XYZW }; @@ -107,8 +109,10 @@ static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT; static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT; static gpu::Stream::FormatPointer INSTANCED_SOLID_FADE_STREAM_FORMAT; -static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normals -static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3); +static const uint SHAPE_VERTEX_STRIDE = sizeof(GeometryCache::ShapeVertex); // position, normal, texcoords, tangent +static const uint SHAPE_NORMALS_OFFSET = offsetof(GeometryCache::ShapeVertex, normal); +static const uint SHAPE_TEXCOORD0_OFFSET = offsetof(GeometryCache::ShapeVertex, uv); +static const uint SHAPE_TANGENT_OFFSET = offsetof(GeometryCache::ShapeVertex, tangent); void GeometryCache::computeSimpleHullPointListForShape(const int entityShape, const glm::vec3 &entityExtents, QVector &outPointList) { @@ -167,16 +171,20 @@ std::vector polygon() { return result; } -void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const geometry::VertexVector& vertices) { +void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const std::vector& vertices) { gpu::Buffer::Size offset = vertexBuffer->getSize(); vertexBuffer->append(vertices); - gpu::Buffer::Size viewSize = vertices.size() * sizeof(glm::vec3); + gpu::Buffer::Size viewSize = vertices.size() * sizeof(ShapeVertex); _positionView = gpu::BufferView(vertexBuffer, offset, viewSize, SHAPE_VERTEX_STRIDE, POSITION_ELEMENT); _normalView = gpu::BufferView(vertexBuffer, offset + SHAPE_NORMALS_OFFSET, viewSize, SHAPE_VERTEX_STRIDE, NORMAL_ELEMENT); + _texCoordView = gpu::BufferView(vertexBuffer, offset + SHAPE_TEXCOORD0_OFFSET, + viewSize, SHAPE_VERTEX_STRIDE, TEXCOORD0_ELEMENT); + _tangentView = gpu::BufferView(vertexBuffer, offset + SHAPE_TANGENT_OFFSET, + viewSize, SHAPE_VERTEX_STRIDE, TANGENT_ELEMENT); } void GeometryCache::ShapeData::setupIndices(gpu::BufferPointer& indexBuffer, const geometry::IndexVector& indices, const geometry::IndexVector& wireIndices) { @@ -202,6 +210,8 @@ void GeometryCache::ShapeData::setupIndices(gpu::BufferPointer& indexBuffer, con void GeometryCache::ShapeData::setupBatch(gpu::Batch& batch) const { batch.setInputBuffer(gpu::Stream::POSITION, _positionView); batch.setInputBuffer(gpu::Stream::NORMAL, _normalView); + batch.setInputBuffer(gpu::Stream::TEXCOORD, _texCoordView); + batch.setInputBuffer(gpu::Stream::TANGENT, _tangentView); batch.setIndexBuffer(_indicesView); } @@ -268,14 +278,14 @@ static IndexPair indexToken(geometry::Index a, geometry::Index b) { template void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { using namespace geometry; - VertexVector vertices; + std::vector vertices; IndexVector solidIndices, wireIndices; IndexPairs wireSeenIndices; size_t faceCount = shape.faces.size(); size_t faceIndexCount = triangulatedFaceIndexCount(); - vertices.reserve(N * faceCount * 2); + vertices.reserve(N * faceCount); solidIndices.reserve(faceIndexCount * faceCount); Index baseVertex = 0; @@ -284,11 +294,35 @@ void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { using namespace geometry; - VertexVector vertices; - vertices.reserve(shape.vertices.size() * 2); + std::vector vertices; + vertices.reserve(shape.vertices.size()); for (const auto& vertex : shape.vertices) { - vertices.push_back(vertex); - vertices.push_back(vertex); + // We'll fill in the correct tangents later, once we correct the UVs + vertices.emplace_back(vertex, vertex, calculateSphereTexCoord(vertex), vec3(0.0f)); + } + + // We need to fix up the sphere's UVs because it's actually a tesselated icosahedron. See http://mft-dev.dk/uv-mapping-sphere/ + size_t faceCount = shape.faces.size(); + for (size_t f = 0; f < faceCount; f++) { + // Fix zipper + { + float& u1 = vertices[shape.faces[f][0]].uv.x; + float& u2 = vertices[shape.faces[f][1]].uv.x; + float& u3 = vertices[shape.faces[f][2]].uv.x; + + if (glm::isnan(u1)) { + u1 = (u2 + u3) / 2.0f; + } + if (glm::isnan(u2)) { + u2 = (u1 + u3) / 2.0f; + } + if (glm::isnan(u3)) { + u3 = (u1 + u2) / 2.0f; + } + + const float U_THRESHOLD = 0.25f; + float max = glm::max(u1, glm::max(u2, u3)); + float min = glm::min(u1, glm::min(u2, u3)); + + if (max - min > U_THRESHOLD) { + if (u1 < U_THRESHOLD) { + u1 += 1.0f; + } + if (u2 < U_THRESHOLD) { + u2 += 1.0f; + } + if (u3 < U_THRESHOLD) { + u3 += 1.0f; + } + } + } + + // Fix swirling at poles + for (Index i = 0; i < N; i++) { + Index originalIndex = shape.faces[f][i]; + if (shape.vertices[originalIndex].y == 1.0f || shape.vertices[originalIndex].y == -1.0f) { + float uSum = 0.0f; + for (Index i2 = 1; i2 <= N - 1; i2++) { + float u = vertices[shape.faces[f][(i + i2) % N]].uv.x; + uSum += u; + } + uSum /= (float)(N - 1); + vertices[originalIndex].uv.x = uSum; + break; + } + } + + // Fill in tangents + for (Index i = 0; i < N; i++) { + vec3 tangent = calculateSphereTangent(vertices[shape.faces[f][i]].uv.x); + vertices[shape.faces[f][i]].tangent = tangent; + } } IndexVector solidIndices, wireIndices; IndexPairs wireSeenIndices; - size_t faceCount = shape.faces.size(); size_t faceIndexCount = triangulatedFaceIndexCount(); solidIndices.reserve(faceIndexCount * faceCount); @@ -365,25 +473,22 @@ void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid template void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer, bool isConical = false) { using namespace geometry; - VertexVector vertices; + std::vector vertices; IndexVector solidIndices, wireIndices; // Top (if not conical) and bottom faces std::vector shape = polygon(); if (isConical) { for (uint32_t i = 0; i < N; i++) { - vertices.push_back(vec3(0.0f, 0.5f, 0.0f)); - vertices.push_back(vec3(0.0f, 1.0f, 0.0f)); + vertices.emplace_back(vec3(0.0f, 0.5f, 0.0f), vec3(0.0f, 1.0f, 0.0f), vec2((float)i / (float)N, 1.0f), vec3(0.0f)); } } else { for (const vec3& v : shape) { - vertices.push_back(vec3(v.x, 0.5f, v.z)); - vertices.push_back(vec3(0.0f, 1.0f, 0.0f)); + vertices.emplace_back(vec3(v.x, 0.5f, v.z), vec3(0.0f, 1.0f, 0.0f), vec2(v.x, v.z) + vec2(0.5f), vec3(1.0f, 0.0f, 0.0f)); } } for (const vec3& v : shape) { - vertices.push_back(vec3(v.x, -0.5f, v.z)); - vertices.push_back(vec3(0.0f, -1.0f, 0.0f)); + vertices.emplace_back(vec3(v.x, -0.5f, v.z), vec3(0.0f, -1.0f, 0.0f), vec2(-v.x, v.z) + vec2(0.5f), vec3(-1.0f, 0.0f, 0.0f)); } Index baseVertex = 0; for (uint32_t i = 2; i < N; i++) { @@ -412,15 +517,16 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver vec3 topRight = (isConical ? vec3(0.0f, 0.5f, 0.0f) : vec3(right.x, 0.5f, right.z)); vec3 bottomLeft = vec3(left.x, -0.5f, left.z); vec3 bottomRight = vec3(right.x, -0.5f, right.z); + vec3 tangent = glm::normalize(bottomLeft - bottomRight); - vertices.push_back(topLeft); - vertices.push_back(normal); - vertices.push_back(bottomLeft); - vertices.push_back(normal); - vertices.push_back(topRight); - vertices.push_back(normal); - vertices.push_back(bottomRight); - vertices.push_back(normal); + // Our tex coords go in the opposite direction as our vertices + float u = 1.0f - (float)i / (float)N; + float u2 = 1.0f - (float)(i + 1) / (float)N; + + vertices.emplace_back(topLeft, normal, vec2(u, 0.0f), tangent); + vertices.emplace_back(bottomLeft, normal, vec2(u, 1.0f), tangent); + vertices.emplace_back(topRight, normal, vec2(u2, 0.0f), tangent); + vertices.emplace_back(bottomRight, normal, vec2(u2, 1.0f), tangent); solidIndices.push_back(baseVertex + 0); solidIndices.push_back(baseVertex + 2); @@ -439,41 +545,6 @@ void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& ver shapeData.setupIndices(indexBuffer, solidIndices, wireIndices); } -void drawCircle(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { - // Draw a circle with radius 1/4th the size of the bounding box - using namespace geometry; - - VertexVector vertices; - IndexVector solidIndices, wireIndices; - const int NUM_CIRCLE_VERTICES = 64; - - std::vector shape = polygon(); - for (const vec3& v : shape) { - vertices.push_back(vec3(v.x, 0.0f, v.z)); - vertices.push_back(vec3(0.0f, 0.0f, 0.0f)); - } - - Index baseVertex = 0; - for (uint32_t i = 2; i < NUM_CIRCLE_VERTICES; i++) { - solidIndices.push_back(baseVertex + 0); - solidIndices.push_back(baseVertex + i); - solidIndices.push_back(baseVertex + i - 1); - solidIndices.push_back(baseVertex + NUM_CIRCLE_VERTICES); - solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES - 1); - solidIndices.push_back(baseVertex + i + NUM_CIRCLE_VERTICES); - } - - for (uint32_t i = 1; i <= NUM_CIRCLE_VERTICES; i++) { - wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES)); - wireIndices.push_back(baseVertex + i - 1); - wireIndices.push_back(baseVertex + (i % NUM_CIRCLE_VERTICES) + NUM_CIRCLE_VERTICES); - wireIndices.push_back(baseVertex + (i - 1) + NUM_CIRCLE_VERTICES); - } - - shapeData.setupVertices(vertexBuffer, vertices); - shapeData.setupIndices(indexBuffer, solidIndices, wireIndices); -} - // FIXME solids need per-face vertices, but smooth shaded // components do not. Find a way to support using draw elements // or draw arrays as appropriate @@ -506,9 +577,9 @@ void GeometryCache::buildShapes() { // Line { ShapeData& shapeData = _shapes[Line]; - shapeData.setupVertices(_shapeVertices, VertexVector { - vec3(-0.5f, 0.0f, 0.0f), vec3(-0.5f, 0.0f, 0.0f), - vec3(0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f) + shapeData.setupVertices(_shapeVertices, std::vector { + ShapeVertex(vec3(-0.5f, 0.0f, 0.0f), vec3(-0.5f, 0.0f, 0.0f), vec2(0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f)), + ShapeVertex(vec3(0.5f, 0.0f, 0.0f), vec3(0.5f, 0.0f, 0.0f), vec2(0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f)) }); IndexVector wireIndices; // Only two indices @@ -572,6 +643,8 @@ gpu::Stream::FormatPointer& getSolidStreamFormat() { SOLID_STREAM_FORMAT = std::make_shared(); // 1 for everyone SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); + SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); + SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); } return SOLID_STREAM_FORMAT; } @@ -581,6 +654,8 @@ gpu::Stream::FormatPointer& getInstancedSolidStreamFormat() { INSTANCED_SOLID_STREAM_FORMAT = std::make_shared(); // 1 for everyone INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); + INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); + INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return INSTANCED_SOLID_STREAM_FORMAT; @@ -591,6 +666,8 @@ gpu::Stream::FormatPointer& getInstancedSolidFadeStreamFormat() { INSTANCED_SOLID_FADE_STREAM_FORMAT = std::make_shared(); // 1 for everyone INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); + INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); + INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD2, gpu::Stream::TEXCOORD2, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD3, gpu::Stream::TEXCOORD3, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); @@ -893,6 +970,7 @@ void GeometryCache::updateVertices(int id, const QVector& points, con details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); + // TODO: circle3D overlays use this to define their vertices, so they need tex coords details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 3c6b85bed1..e0ba99b09e 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -354,13 +354,24 @@ public: /// Set a batch to the simple pipeline, returning the previous pipeline void useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend = false); + struct ShapeVertex { + ShapeVertex(const vec3& pos, const vec3& normal, const vec2& uv, const vec3& tangent) : pos(pos), normal(normal), uv(uv), tangent(tangent) {} + + vec3 pos; + vec3 normal; + vec2 uv; + vec3 tangent; + }; + struct ShapeData { gpu::BufferView _positionView; gpu::BufferView _normalView; + gpu::BufferView _texCoordView; + gpu::BufferView _tangentView; gpu::BufferView _indicesView; gpu::BufferView _wireIndicesView; - void setupVertices(gpu::BufferPointer& vertexBuffer, const geometry::VertexVector& vertices); + void setupVertices(gpu::BufferPointer& vertexBuffer, const std::vector& vertices); void setupIndices(gpu::BufferPointer& indexBuffer, const geometry::IndexVector& indices, const geometry::IndexVector& wireIndices); void setupBatch(gpu::Batch& batch) const; void draw(gpu::Batch& batch) const; diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index b3662385b0..26bb85a853 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -148,25 +148,25 @@ vec3 fetchLightmapMap(vec2 uv) { } <@endfunc@> -<@func tangentToViewSpace(fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> +<@func evalMaterialNormal(fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> { vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz); vec3 normalizedTangent = normalize(<$interpolatedTangent$>.xyz); - vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); + vec3 normalizedBitangent = cross(normalizedNormal, normalizedTangent); vec3 localNormal = <$fetchedNormal$>; - <$normal$> = vec3(normalizedTangent * localNormal.x + normalizedNormal * localNormal.y + normalizedBitangent * localNormal.z); + <$normal$> = vec3(normalizedBitangent * localNormal.x + normalizedNormal * localNormal.y + normalizedTangent * localNormal.z); } <@endfunc@> -<@func tangentToViewSpaceLOD(fragPos, fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> +<@func evalMaterialNormalLOD(fragPos, fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> { vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz); vec3 normalizedTangent = normalize(<$interpolatedTangent$>.xyz); - vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); + vec3 normalizedBitangent = cross(normalizedNormal, normalizedTangent); // attenuate the normal map divergence from the mesh normal based on distance - // THe attenuation range [20,100] meters from the eye is arbitrary for now + // The attenuation range [20,100] meters from the eye is arbitrary for now vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(20.0, 100.0, (-<$fragPos$>).z)); - <$normal$> = vec3(normalizedTangent * localNormal.x + normalizedNormal * localNormal.y + normalizedBitangent * localNormal.z); + <$normal$> = vec3(normalizedBitangent * localNormal.x + normalizedNormal * localNormal.y + normalizedTangent * localNormal.z); } <@endfunc@> diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index ecfb4847e4..5c64d4b584 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -47,6 +47,8 @@ template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderAr } } +const graphics::MaterialPointer MeshPartPayload::DEFAULT_MATERIAL = std::make_shared(); + MeshPartPayload::MeshPartPayload(const std::shared_ptr& mesh, int partIndex, graphics::MaterialPointer material) { updateMeshPart(mesh, partIndex); addMaterial(graphics::MaterialLayer(material, 0)); @@ -99,7 +101,7 @@ void MeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCastShad builder.withSubMetaCulled(); } - if (_drawMaterials.top().material) { + if (topMaterialExists()) { auto matKey = _drawMaterials.top().material->getKey(); if (matKey.isTranslucent()) { builder.withTransparent(); @@ -119,7 +121,7 @@ Item::Bound MeshPartPayload::getBound() const { ShapeKey MeshPartPayload::getShapeKey() const { graphics::MaterialKey drawMaterialKey; - if (_drawMaterials.top().material) { + if (topMaterialExists()) { drawMaterialKey = _drawMaterials.top().material->getKey(); } @@ -171,7 +173,7 @@ void MeshPartPayload::render(RenderArgs* args) { bindMesh(batch); // apply material properties - RenderPipelines::bindMaterial(_drawMaterials.top().material, batch, args->_enableTexturing); + RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing); args->_details._materialSwitches++; // Draw! @@ -356,7 +358,7 @@ void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCas builder.withDeformed(); } - if (_drawMaterials.top().material) { + if (topMaterialExists()) { auto matKey = _drawMaterials.top().material->getKey(); if (matKey.isTranslucent()) { builder.withTransparent(); @@ -387,7 +389,7 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe } graphics::MaterialKey drawMaterialKey; - if (_drawMaterials.top().material) { + if (topMaterialExists()) { drawMaterialKey = _drawMaterials.top().material->getKey(); } @@ -469,7 +471,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { bindMesh(batch); // apply material properties - RenderPipelines::bindMaterial(_drawMaterials.top().material, batch, args->_enableTexturing); + RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing); args->_details._materialSwitches++; // Draw! diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 087550345d..08ad7a8311 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -65,15 +65,18 @@ public: graphics::Mesh::Part _drawPart; size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } - size_t getMaterialTextureSize() { return _drawMaterials.top().material ? _drawMaterials.top().material->getTextureSize() : 0; } - int getMaterialTextureCount() { return _drawMaterials.top().material ? _drawMaterials.top().material->getTextureCount() : 0; } - bool hasTextureInfo() const { return _drawMaterials.top().material ? _drawMaterials.top().material->hasTextureInfo() : false; } + size_t getMaterialTextureSize() { return topMaterialExists() ? _drawMaterials.top().material->getTextureSize() : 0; } + int getMaterialTextureCount() { return topMaterialExists() ? _drawMaterials.top().material->getTextureCount() : 0; } + bool hasTextureInfo() const { return topMaterialExists() ? _drawMaterials.top().material->hasTextureInfo() : false; } void addMaterial(graphics::MaterialLayer material); void removeMaterial(graphics::MaterialPointer material); protected: + static const graphics::MaterialPointer DEFAULT_MATERIAL; render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() }; + + bool topMaterialExists() const { return !_drawMaterials.empty() && _drawMaterials.top().material; } }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2617ef12ad..70f873734a 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1604,7 +1604,8 @@ void Model::createVisibleRenderItemSet() { int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); - _modelMeshMaterialNames.push_back(getGeometry()->getShapeMaterial(shapeID)->getName()); + auto material = getGeometry()->getShapeMaterial(shapeID); + _modelMeshMaterialNames.push_back(material ? material->getName() : ""); _modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i }); shapeID++; } diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index d721664794..d54cefa5c7 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -98,6 +98,12 @@ void CompositeHUD::run(const RenderContextPointer& renderContext) { // Grab the HUD texture #if !defined(DISABLE_QML) gpu::doInBatch("CompositeHUD", renderContext->args->_context, [&](gpu::Batch& batch) { + glm::mat4 projMat; + Transform viewMat; + renderContext->args->getViewFrustum().evalProjectionMatrix(projMat); + renderContext->args->getViewFrustum().evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat, true); if (renderContext->args->_hudOperator) { renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); } diff --git a/libraries/render-utils/src/forward_model_normal_map.slf b/libraries/render-utils/src/forward_model_normal_map.slf index b32ed862d6..0ba464d3f0 100644 --- a/libraries/render-utils/src/forward_model_normal_map.slf +++ b/libraries/render-utils/src/forward_model_normal_map.slf @@ -58,7 +58,7 @@ void main(void) { vec3 fragPosition = _position.xyz; vec3 fragNormal; - <$tangentToViewSpace(normalTex, _normal, _tangent, fragNormal)$> + <$evalMaterialNormal(normalTex, _normal, _tangent, fragNormal)$> TransformCamera cam = getTransformCamera(); diff --git a/libraries/render-utils/src/model_lightmap_normal_map.slf b/libraries/render-utils/src/model_lightmap_normal_map.slf index 8734ea74b8..eecde59e54 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map.slf +++ b/libraries/render-utils/src/model_lightmap_normal_map.slf @@ -33,11 +33,11 @@ void main(void) { <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness, normalTexel, metallicTex)$> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$> - vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTexel, _normal, _tangent, viewNormal)$> + vec3 fragNormal; + <$evalMaterialNormalLOD(_position, normalTexel, _normal, _tangent, fragNormal)$> packDeferredFragmentLightmap( - normalize(viewNormal.xyz), + normalize(fragNormal.xyz), evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), getMaterialAlbedo(mat) * albedo.rgb * _color, getMaterialRoughness(mat) * roughness, diff --git a/libraries/render-utils/src/model_lightmap_normal_map_fade.slf b/libraries/render-utils/src/model_lightmap_normal_map_fade.slf index e6cb35ec4f..af497f90c6 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map_fade.slf +++ b/libraries/render-utils/src/model_lightmap_normal_map_fade.slf @@ -43,11 +43,11 @@ void main(void) { <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness, normalTexel, metallicTex)$> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$> - vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTexel, _normal, _tangent, viewNormal)$> + vec3 fragNormal; + <$evalMaterialNormalLOD(_position, normalTexel, _normal, _tangent, fragNormal)$> packDeferredFragmentLightmap( - normalize(viewNormal.xyz), + normalize(fragNormal.xyz), evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), getMaterialAlbedo(mat) * albedo.rgb * _color, getMaterialRoughness(mat) * roughness, diff --git a/libraries/render-utils/src/model_normal_map.slf b/libraries/render-utils/src/model_normal_map.slf index 82f667bf73..49613eca6a 100644 --- a/libraries/render-utils/src/model_normal_map.slf +++ b/libraries/render-utils/src/model_normal_map.slf @@ -46,8 +46,8 @@ void main(void) { vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; - vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> + vec3 fragNormal; + <$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$> float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; @@ -56,7 +56,7 @@ void main(void) { <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; packDeferredFragment( - normalize(viewNormal.xyz), + normalize(fragNormal.xyz), opacity, albedo, roughness, diff --git a/libraries/render-utils/src/model_normal_map_fade.slf b/libraries/render-utils/src/model_normal_map_fade.slf index 67bea98cf0..bf6222652c 100644 --- a/libraries/render-utils/src/model_normal_map_fade.slf +++ b/libraries/render-utils/src/model_normal_map_fade.slf @@ -56,8 +56,8 @@ void main(void) { vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; - vec3 viewNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> + vec3 fragNormal; + <$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$> float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; @@ -65,7 +65,7 @@ void main(void) { float scattering = getMaterialScattering(mat); packDeferredFragment( - normalize(viewNormal.xyz), + normalize(fragNormal.xyz), opacity, albedo, roughness, diff --git a/libraries/render-utils/src/model_translucent_normal_map.slf b/libraries/render-utils/src/model_translucent_normal_map.slf index 759007d93e..52015660c6 100644 --- a/libraries/render-utils/src/model_translucent_normal_map.slf +++ b/libraries/render-utils/src/model_translucent_normal_map.slf @@ -61,7 +61,7 @@ void main(void) { vec3 fragPosition = _position.xyz; vec3 fragNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, fragNormal)$> + <$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$> TransformCamera cam = getTransformCamera(); vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); diff --git a/libraries/render-utils/src/model_translucent_normal_map_fade.slf b/libraries/render-utils/src/model_translucent_normal_map_fade.slf index 204b5ac56b..c6c0e16812 100644 --- a/libraries/render-utils/src/model_translucent_normal_map_fade.slf +++ b/libraries/render-utils/src/model_translucent_normal_map_fade.slf @@ -71,7 +71,7 @@ void main(void) { vec3 fragPosition = _position.xyz; // Lighting is done in world space vec3 fragNormal; - <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, fragNormal)$> + <$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$> TransformCamera cam = getTransformCamera(); vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0)); diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index ed052adf6e..9c5efb9fa7 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -118,9 +118,15 @@ uint32_t Item::fetchMetaSubItemBounds(ItemBounds& subItemBounds, Scene& scene) c auto numSubs = fetchMetaSubItems(subItems); for (auto id : subItems) { - auto& item = scene.getItem(id); - if (item.exist()) { - subItemBounds.emplace_back(id, item.getBound()); + // TODO: Adding an extra check here even thought we shouldn't have too. + // We have cases when the id returned by fetchMetaSubItems is not allocated + if (scene.isAllocatedID(id)) { + auto& item = scene.getItem(id); + if (item.exist()) { + subItemBounds.emplace_back(id, item.getBound()); + } else { + numSubs--; + } } else { numSubs--; } diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index 4942c63e27..930da6a494 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -59,5 +59,7 @@ static const float MIN_AVATAR_SCALE = 0.005f; static const float MAX_AVATAR_HEIGHT = 1000.0f * DEFAULT_AVATAR_HEIGHT; // meters static const float MIN_AVATAR_HEIGHT = 0.005f * DEFAULT_AVATAR_HEIGHT; // meters +static const float AVATAR_WALK_SPEED_SCALAR = 1.0f; +static const float AVATAR_SPRINT_SPEED_SCALAR = 3.0f; #endif // hifi_AvatarConstants_h diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index ac402a549f..b5c76257ef 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -81,7 +81,7 @@ const QString& PathUtils::resourcesPath() { #else staticResourcePath = ":/"; #endif - + #if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) if (USE_SOURCE_TREE_RESOURCES()) { // For dev builds, optionally load content from the Git source tree @@ -120,6 +120,31 @@ QUrl PathUtils::resourcesUrl(const QString& relativeUrl) { return QUrl(resourcesUrl() + relativeUrl); } +QUrl PathUtils::expandToLocalDataAbsolutePath(const QUrl& fileUrl) { + QString path = fileUrl.path(); + + if (path.startsWith("/~/")) { + // this results in a qrc:// url... + // return resourcesUrl(path.mid(3)); + +#ifdef Q_OS_MAC + static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/"; +#elif defined (ANDROID) + static const QString staticResourcePath = + QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources/"; +#else + static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/"; +#endif + path.replace(0, 3, staticResourcePath); + QUrl expandedURL = QUrl::fromLocalFile(path); + return expandedURL; + } + + QUrl::fromLocalFile(resourcesPath()).toString(); + + return fileUrl; +} + const QString& PathUtils::qmlBaseUrl() { static const QString staticResourcePath = resourcesUrl() + "qml/"; return staticResourcePath; diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index ec71ccee6a..d879ac968d 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -37,6 +37,7 @@ public: static QUrl resourcesUrl(const QString& relative); static const QString& resourcesPath(); static const QString& qmlBaseUrl(); + static QUrl expandToLocalDataAbsolutePath(const QUrl& fileUrl); static QUrl qmlUrl(const QString& relative); #ifdef DEV_BUILD static const QString& projectRootPath(); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 4fee78a0db..78e368748b 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -363,12 +363,13 @@ public: /**jsdoc * Get the position of a vertex in the mesh. - * @function MeshProxy#getPos3 + * @function MeshProxy#getPos * @param {number} index - Integer index of the mesh vertex. * @returns {Vec3} Local position of the vertex relative to the mesh. * @deprecated Use the {@link Graphics} API instead. */ - Q_INVOKABLE virtual glm::vec3 getPos3(int index) const = 0; + Q_INVOKABLE virtual glm::vec3 getPos(int index) const = 0; + Q_INVOKABLE virtual glm::vec3 getPos3(int index) const { return getPos(index); } // deprecated }; Q_DECLARE_METATYPE(MeshProxy*); diff --git a/libraries/shared/src/shared/Shapes.cpp b/libraries/shared/src/shared/Shapes.cpp index dabdf21202..afcf956165 100644 --- a/libraries/shared/src/shared/Shapes.cpp +++ b/libraries/shared/src/shared/Shapes.cpp @@ -8,8 +8,11 @@ #include "Shapes.h" +#include "qmath.h" + namespace geometry { +using glm::vec2; using glm::vec3; // The golden ratio @@ -19,8 +22,8 @@ Solid<3> tesselate(const Solid<3>& solid_, int count) { Solid<3> solid = solid_; float length = glm::length(solid.vertices[0]); for (int i = 0; i < count; ++i) { - Solid<3> result { solid.vertices, {} }; - result.vertices.reserve(solid.vertices.size() + solid.faces.size() * 3); + Solid<3> result { solid.vertices, {}, {} }; + result.vertices.reserve(solid.vertices.size() + solid.faces.size() * 6); for (size_t f = 0; f < solid.faces.size(); ++f) { Index baseVertex = (Index)result.vertices.size(); const Face<3>& oldFace = solid.faces[f]; @@ -30,13 +33,26 @@ Solid<3> tesselate(const Solid<3>& solid_, int count) { vec3 ab = glm::normalize(a + b) * length; vec3 bc = glm::normalize(b + c) * length; vec3 ca = glm::normalize(c + a) * length; + + result.vertices.push_back(a); + result.vertices.push_back(ab); + result.vertices.push_back(ca); + result.faces.push_back(Face<3>{ { baseVertex, baseVertex + 1, baseVertex + 2 } }); + + result.vertices.push_back(ab); + result.vertices.push_back(b); + result.vertices.push_back(bc); + result.faces.push_back(Face<3>{ { baseVertex + 3, baseVertex + 4, baseVertex + 5 } }); + + result.vertices.push_back(bc); + result.vertices.push_back(c); + result.vertices.push_back(ca); + result.faces.push_back(Face<3>{ { baseVertex + 6, baseVertex + 7, baseVertex + 8 } }); + result.vertices.push_back(ab); result.vertices.push_back(bc); result.vertices.push_back(ca); - result.faces.push_back(Face<3>{ { oldFace[0], baseVertex, baseVertex + 2 } }); - result.faces.push_back(Face<3>{ { baseVertex, oldFace[1], baseVertex + 1 } }); - result.faces.push_back(Face<3>{ { baseVertex + 1, oldFace[2], baseVertex + 2 } }); - result.faces.push_back(Face<3>{ { baseVertex, baseVertex + 1, baseVertex + 2 } }); + result.faces.push_back(Face<3>{ { baseVertex + 9, baseVertex + 10, baseVertex + 11 } }); } solid = result; } @@ -50,6 +66,10 @@ const Solid<3>& tetrahedron() { static const auto D = vec3(-1, -1, 1); static const Solid<3> TETRAHEDRON = Solid<3>{ { A, B, C, D }, + { vec2(0.75f, 0.5f), vec2(0.5f, 0.0f), vec2(0.25f, 0.5f), + vec2(0.5f, 1.0f), vec2(1.0f, 1.0f), vec2(0.75f, 0.5f), + vec2(0.25f, 0.5f), vec2(0.5f, 1.0f), vec2(0.75f, 0.5f), + vec2(0.25f, 0.5f), vec2(0.0f, 1.0f), vec2(0.5f, 1.0f) }, FaceVector<3>{ Face<3> { { 0, 1, 2 } }, Face<3> { { 3, 1, 0 } }, @@ -65,8 +85,15 @@ const Solid<4>& cube() { static const auto B = vec3(-1, 1, 1); static const auto C = vec3(-1, 1, -1); static const auto D = vec3(1, 1, -1); + static const float THIRD = 1.0f / 3.0f; static const Solid<4> CUBE = Solid<4>{ { A, B, C, D, -A, -B, -C, -D }, + { vec2(0.5f, 0.0f), vec2(0.25f, 0.0f), vec2(0.25f, THIRD), vec2(0.5f, THIRD), + vec2(0.5f, THIRD), vec2(0.25f, THIRD), vec2(0.25f, 2.0f * THIRD), vec2(0.5f, 2.0f * THIRD), + vec2(0.25f, THIRD), vec2(0.0f, THIRD), vec2(0.0f, 2.0f * THIRD), vec2(0.25f, 2.0f * THIRD), + vec2(1.0f, THIRD), vec2(0.75f, THIRD), vec2(0.75f, 2.0f * THIRD), vec2(1.0f, 2.0f * THIRD), + vec2(0.75f, THIRD), vec2(0.5f, THIRD), vec2(0.5f, 2.0f * THIRD), vec2(0.75f, 2.0f * THIRD), + vec2(0.25f, 1.0f), vec2(0.5f, 1.0f), vec2(0.5f, 2.0f * THIRD), vec2(0.25f, 2.0f * THIRD) }, FaceVector<4>{ Face<4> { { 3, 2, 1, 0 } }, Face<4> { { 0, 1, 7, 6 } }, @@ -86,8 +113,18 @@ const Solid<3>& octahedron() { static const auto D = vec3(0, 0, -1); static const auto E = vec3(1, 0, 0); static const auto F = vec3(-1, 0, 0); + static const float THIRD = 1.0f / 3.0f; + static const float SEVENTH = 1.0f / 7.0f; static const Solid<3> OCTAHEDRON = Solid<3>{ { A, B, C, D, E, F}, + { vec2(2.0f * SEVENTH, THIRD), vec2(SEVENTH, 2.0f * THIRD), vec2(3.0f * SEVENTH, 2.0f * THIRD), + vec2(2.0f * SEVENTH, THIRD), vec2(3.0f * SEVENTH, 2.0f * THIRD), vec2(4.0f * SEVENTH, THIRD), + vec2(5.0f * SEVENTH, 0.0f), vec2(4.0f * SEVENTH, THIRD), vec2(6.0f * SEVENTH, THIRD), + vec2(2.0f * SEVENTH, THIRD), vec2(0.0f, THIRD), vec2(1.0f * SEVENTH, 2.0f * THIRD), + vec2(2.0f * SEVENTH, 1.0f), vec2(3.0f * SEVENTH, 2.0f * THIRD), vec2(1.0f * SEVENTH, 2.0f * THIRD), + vec2(5.0f * SEVENTH, 2.0f * THIRD), vec2(4.0f * SEVENTH, THIRD), vec2(3.0f * SEVENTH, 2.0f * THIRD), + vec2(5.0f * SEVENTH, 2.0f * THIRD), vec2(6.0f * SEVENTH, THIRD), vec2(4.0f * SEVENTH, THIRD), + vec2(5.0f * SEVENTH, 2.0f * THIRD), vec2(1.0f, 2.0f * THIRD), vec2(6.0f * SEVENTH, THIRD) }, FaceVector<3> { Face<3> { { 0, 2, 4, } }, Face<3> { { 0, 4, 3, } }, @@ -116,11 +153,52 @@ const Solid<5>& dodecahedron() { static const vec3 I = vec3(0, -IP, P); static const vec3 J = vec3(P, 0, IP); + + /* _ + / \ | + / \ y2 + / \ | + / \ _ + \ / | + \ / y1 + \ / | + ___________ _ + |x3|- - x1 - -||x3| + |- - - - x2- - - -| + */ + + // x1, x2, and x3 are the solutions to the following system of equations: + // 1 = 3 * x1 + 3 * x2 + x3 + // x1 + 2 * x3 = (golden ratio) * x1 + // x2 = x1 + 2 * x3 + static const float x1 = 4.0f / (17.0f + 7.0f * sqrtf(5.0f)); + static const float x2 = (1.0f / 11.0f) * (5.0f * sqrtf(5.0f) - 9.0f); + static const float x2_2 = x2 / 2.0f; + static const float x3 = (1.0f / 11.0f) * (6.0f * sqrtf(5.0f) - 13.0f); + // y1 and y2 are the solutions to the following system of equations (x is the sidelength, but is different than x1 because the scale in the y direction is different): + // 1 = 3 * y1 + 2 * y2 + // y1 = sin(108 deg) * x + // y1 + y2 = x * sqrtf(5 + 2 * sqrtf(5)) / 2 + static const float y1 = sqrtf(2.0f * (5.0f + sqrtf(5.0f))) / (sqrtf(2.0f * (5.0f + sqrtf(5.0f))) + 4.0f * sqrtf(5.0f + 2.0f * sqrtf(5.0f))); + static const float y2 = -(sqrtf(2.0f * (5.0f + sqrtf(5.0f))) - 2.0f * sqrtf(5.0f + 2.0f * sqrtf(5.0f))) / (sqrtf(2.0f * (5.0f + sqrtf(5.0f))) + 4.0f * sqrtf(5.0f + 2.0f * sqrtf(5.0f))); + static const Solid<5> DODECAHEDRON = Solid<5>{ { A, B, C, D, E, F, G, H, I, J, -A, -B, -C, -D, -E, -F, -G, -H, -I, -J, }, + { vec2(x1 + x2_2, 0.0f), vec2(x2_2, 0.0f), vec2(x2_2 - x3, y1), vec2(x3 + x1, y1 + y2), vec2(x3 + x1 + x2_2, y1), + vec2(1.0f - (x2 - x3 + x2_2), 0.0f), vec2(1.0f - (x2 + x1 + x3), y2), vec2(1.0f - (x2 + x1), y1 + y2), vec2(1.0f - x2, y1 + y2), vec2(1.0f - (x2 - x3), y2), + vec2(1.0f - x2_2, y1), vec2(1.0f - x2, y1 + y2), vec2(1.0f - (x3 + x1), y1 + y2 + y1), vec2(1.0f - x3, y1 + y2 + y1), vec2(1.0f, y1 + y2), + vec2(x3, y1 + y2), vec2(0.0f, y1 + y2 + y1), vec2(x2_2, y1 + y2 + y1 + y2), vec2(x2, y1 + y2 + y1), vec2(x1 + x3, y1 + y2), + vec2(x3 + x1, y1 + y2), vec2(x2, y1 + y2 + y1), vec2(x2 + x1, y1 + y2 + y1), vec2(x3 + x1 + x2, y1 + y2), vec2(x3 + x1 + x2_2, y1), + vec2(x3 + x1 + x2_2, y1), vec2(x3 + x1 + x2, y1 + y2), vec2(x3 + x1 + x2 + x2_2, y1), vec2(x1 + x2 + x2_2, 0.0f), vec2(x3 + x1 + x2_2 + x3, 0.0f), + vec2(1.0f - (x3 + x1 + x2_2), 1.0f - y1), vec2(1.0f - (x3 + x1 + x2), 1.0f - (y1 + y2)), vec2(1.0f - (x3 + x1 + x2 + x2_2), 1.0f - y1), vec2(1.0f - (x1 + x2 + x2_2), 1.0f), vec2(1.0f - (x3 + x1 + x2_2 + x3), 1.0f), + vec2(x2 + x1 + x3, 1.0f - y2), vec2(x2 + x1, 1.0f - (y1 + y2)), vec2(x2, 1.0f - (y1 + y2)), vec2(x2 - x3, 1.0f - y2), vec2(x2 - x3 + x2_2, 1.0f), + vec2(x2 + x1 + x2, y1 + y2 + y1), vec2(x3 + x1 + x2 + x1, y1 + y2), vec2(x3 + x1 + x2, y1 + y2), vec2(x2 + x1, y1 + y2 + y1), vec2(x2 + x1 + x2_2, y1 + y2 + y1 + y2), + vec2(1.0f - (x3 + x1 + x2), y1 + y2 + y1), vec2(1.0f - (x2 + x1), y1 + y2), vec2(1.0f - (x2 + x1 + x2_2), y1), vec2(1.0f - (x2 + x1 + x2), y1 + y2), vec2(1.0f - (x3 + x1 + x2 + x1), y1 + y2 + y1), + vec2(1.0f - (x3 + x1 + x2_2), y1 + y2 + y1 + y2), vec2(1.0f - (x3 + x1), y1 + y2 + y1), vec2(1.0f - x2, y1 + y2), vec2(1.0f - (x2 + x1), y2 + y1), vec2(1.0f - (x3 + x1 + x2), y1 + y2 + y1), + vec2(1.0f - (x1 + x2_2), 1.0f), vec2(1.0f - x2_2, 1.0f), vec2(1.0f - (x2_2 - x3), 1.0f - y1), vec2(1.0f - (x1 + x3), 1.0f - (y1 + y2)), vec2(1.0f - (x3 + x1 + x2_2), 1.0f - y1) }, FaceVector<5> { Face<5> { { 0, 1, 2, 3, 4 } }, Face<5> { { 0, 5, 18, 6, 1 } }, @@ -148,12 +226,33 @@ const Solid<3>& icosahedron() { static const auto D = vec3(P, 0, N); static const auto E = vec3(P, 0, -N); static const auto F = vec3(0, N, -P); - + static const float THIRD = 1.0f / 3.0f; + static const float ELEVENTH = 1.0f / 11.0f; static const Solid<3> ICOSAHEDRON = Solid<3> { { A, B, C, D, E, F, -A, -B, -C, -D, -E, -F, }, + { vec2(3.0f * ELEVENTH, 0.0f), vec2(2.0f * ELEVENTH, THIRD), vec2(4.0f * ELEVENTH, THIRD), + vec2(2.0f * ELEVENTH, THIRD), vec2(3.0f * ELEVENTH, 2.0f * THIRD), vec2(4.0f * ELEVENTH, THIRD), + vec2(3.0f * ELEVENTH, 2.0f * THIRD), vec2(5.0f * ELEVENTH, 2.0f * THIRD), vec2(4.0f * ELEVENTH, THIRD), + vec2(5.0f * ELEVENTH, 2.0f * THIRD), vec2(6.0f * ELEVENTH, THIRD), vec2(4.0f * ELEVENTH, THIRD), + vec2(6.0f * ELEVENTH, THIRD), vec2(5.0f * ELEVENTH, 0.0f), vec2(4.0f * ELEVENTH, THIRD), + vec2(1.0f * ELEVENTH, 0.0f), vec2(0.0f, THIRD), vec2(2.0f * ELEVENTH, THIRD), + vec2(1.0f * ELEVENTH, 2.0f * THIRD), vec2(2.0f * ELEVENTH, THIRD), vec2(0.0f, THIRD), + vec2(2.0f * ELEVENTH, THIRD), vec2(1.0f * ELEVENTH, 2.0f * THIRD), vec2(3.0f * ELEVENTH, 2.0f * THIRD), + vec2(2.0f * ELEVENTH, 1.0f), vec2(3.0f * ELEVENTH, 2.0f * THIRD), vec2(1.0f * ELEVENTH, 2.0f * THIRD), + vec2(3.0f * ELEVENTH, 2.0f * THIRD), vec2(4.0f * ELEVENTH, 1.0f), vec2(5.0f * ELEVENTH, 2.0f * THIRD), + vec2(7.0f * ELEVENTH, 2.0f * THIRD), vec2(5.0f * ELEVENTH, 2.0f * THIRD), vec2(6.0f * ELEVENTH, 1.0f), + vec2(5.0f * ELEVENTH, 2.0f * THIRD), vec2(7.0f * ELEVENTH, 2.0f * THIRD), vec2(6.0f * ELEVENTH, THIRD), + vec2(8.0f * ELEVENTH, THIRD), vec2(6.0f * ELEVENTH, THIRD), vec2(7.0f * ELEVENTH, 2.0f * THIRD), + vec2(6.0f * ELEVENTH, THIRD), vec2(8.0f * ELEVENTH, THIRD), vec2(7.0f * ELEVENTH, 0.0f), + vec2(10.0f * ELEVENTH, THIRD), vec2(9.0f * ELEVENTH, 0.0f), vec2(8.0f * ELEVENTH, THIRD), + vec2(7.0f * ELEVENTH, 2.0f * THIRD), vec2(8.0f * ELEVENTH, 1.0f), vec2(9.0f * ELEVENTH, 2.0f * THIRD), + vec2(8.0f * ELEVENTH, THIRD), vec2(7.0f * ELEVENTH, 2.0f * THIRD), vec2(9.0f * ELEVENTH, 2.0f * THIRD), + vec2(10.0f * ELEVENTH, THIRD), vec2(8.0f * ELEVENTH, THIRD), vec2(9.0f * ELEVENTH, 2.0f * THIRD), + vec2(1.0f, 2.0f * THIRD), vec2(10.0f * ELEVENTH, THIRD), vec2(9.0f * ELEVENTH, 2.0f * THIRD), + vec2(10.0f * ELEVENTH, 1.0f), vec2(1.0f, 2.0f * THIRD), vec2(9.0f * ELEVENTH, 2.0f * THIRD) }, FaceVector<3> { Face<3> { { 1, 2, 0 } }, Face<3> { { 2, 3, 0 } }, diff --git a/libraries/shared/src/shared/Shapes.h b/libraries/shared/src/shared/Shapes.h index 3486a0a663..6bd2eab199 100644 --- a/libraries/shared/src/shared/Shapes.h +++ b/libraries/shared/src/shared/Shapes.h @@ -22,6 +22,7 @@ namespace geometry { using Index = uint32_t; using Vec = glm::vec3; using VertexVector = std::vector; + using TexCoordVector = std::vector; using IndexVector = std::vector; template @@ -33,6 +34,7 @@ namespace geometry { template struct Solid { VertexVector vertices; + TexCoordVector texCoords; FaceVector faces; Solid& fitDimension(float newMaxDimension) { diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 06cf929368..4a0f52e272 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -20,8 +20,12 @@ if (NOT SERVER_ONLY AND NOT ANDROID) add_subdirectory(${DIR}) set(DIR "oculusLegacy") add_subdirectory(${DIR}) - set(DIR "hifiSixense") - add_subdirectory(${DIR}) + + if (USE_SIXENSE) + set(DIR "hifiSixense") + add_subdirectory(${DIR}) + endif() + set(DIR "hifiSpacemouse") add_subdirectory(${DIR}) set(DIR "hifiNeuron") diff --git a/plugins/hifiCodec/CMakeLists.txt b/plugins/hifiCodec/CMakeLists.txt index 28c1dc3807..9ecaf7b511 100644 --- a/plugins/hifiCodec/CMakeLists.txt +++ b/plugins/hifiCodec/CMakeLists.txt @@ -12,5 +12,7 @@ link_hifi_libraries(audio plugins) add_dependency_external_projects(hifiAudioCodec) target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAUDIOCODEC_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${HIFIAUDIOCODEC_LIBRARIES}) -install_beside_console() +if (BUILD_SERVER) + install_beside_console() +endif () diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 638cadf574..893b7f48b1 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -19,7 +19,7 @@ if (WIN32 AND (NOT USE_GLES)) set(TARGET_NAME oculus) setup_hifi_plugin(Multimedia) link_hifi_libraries( - shared task gl gpu gpu-gl controllers ui qml + shared task gl gpu ${PLATFORM_GL_BACKEND} controllers ui qml plugins ui-plugins display-plugins input-plugins audio-client networking render-utils ${PLATFORM_GL_BACKEND} diff --git a/plugins/pcmCodec/CMakeLists.txt b/plugins/pcmCodec/CMakeLists.txt index 900a642a88..cce33ecd1a 100644 --- a/plugins/pcmCodec/CMakeLists.txt +++ b/plugins/pcmCodec/CMakeLists.txt @@ -9,5 +9,7 @@ set(TARGET_NAME pcmCodec) setup_hifi_client_server_plugin() link_hifi_libraries(shared plugins) -install_beside_console() +if (BUILD_SERVER) + install_beside_console() +endif () diff --git a/script-archive/inspect.js b/script-archive/inspect.js index 555b4105b7..18b26ab709 100644 --- a/script-archive/inspect.js +++ b/script-archive/inspect.js @@ -26,14 +26,14 @@ var RADIUS_RATE = 1.0 / 100.0; var PAN_RATE = 250.0; var Y_AXIS = { - x: 0, - y: 1, - z: 0 + x: 0, + y: 1, + z: 0 }; var X_AXIS = { - x: 1, - y: 0, - z: 0 + x: 1, + y: 0, + z: 0 }; var LOOK_AT_TIME = 500; @@ -56,21 +56,20 @@ var mode = noMode; var mouseLastX = 0; var mouseLastY = 0; - var center = { - x: 0, - y: 0, - z: 0 + x: 0, + y: 0, + z: 0 }; var position = { - x: 0, - y: 0, - z: 0 + x: 0, + y: 0, + z: 0 }; var vector = { - x: 0, - y: 0, - z: 0 + x: 0, + y: 0, + z: 0 }; var radius = 0.0; var azimuth = 0.0; @@ -83,258 +82,248 @@ var rotatingTowardsTarget = false; var targetCamOrientation; var oldPosition, oldOrientation; - function orientationOf(vector) { - var direction, - yaw, - pitch; + var direction, + yaw, + pitch; - direction = Vec3.normalize(vector); - yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS); - pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS); - return Quat.multiply(yaw, pitch); + direction = Vec3.normalize(vector); + yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS); + pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS); + return Quat.multiply(yaw, pitch); } - function handleRadialMode(dx, dy) { - azimuth += dx / AZIMUTH_RATE; - radius += radius * dy * RADIUS_RATE; - if (radius < 1) { - radius = 1; - } + azimuth += dx / AZIMUTH_RATE; + radius += radius * dy * RADIUS_RATE; + if (radius < 1) { + radius = 1; + } - vector = { - x: (Math.cos(altitude) * Math.cos(azimuth)) * radius, - y: Math.sin(altitude) * radius, - z: (Math.cos(altitude) * Math.sin(azimuth)) * radius - }; - position = Vec3.sum(center, vector); - Camera.setPosition(position); - Camera.setOrientation(orientationOf(vector)); + vector = { + x: (Math.cos(altitude) * Math.cos(azimuth)) * radius, + y: Math.sin(altitude) * radius, + z: (Math.cos(altitude) * Math.sin(azimuth)) * radius + }; + position = Vec3.sum(center, vector); + Camera.setPosition(position); + Camera.setOrientation(orientationOf(vector)); } function handleOrbitMode(dx, dy) { - azimuth += dx / AZIMUTH_RATE; - altitude += dy / ALTITUDE_RATE; - if (altitude > PI / 2.0) { - altitude = PI / 2.0; - } - if (altitude < -PI / 2.0) { - altitude = -PI / 2.0; - } + azimuth += dx / AZIMUTH_RATE; + altitude += dy / ALTITUDE_RATE; + if (altitude > PI / 2.0) { + altitude = PI / 2.0; + } + if (altitude < -PI / 2.0) { + altitude = -PI / 2.0; + } - vector = { - x: (Math.cos(altitude) * Math.cos(azimuth)) * radius, - y: Math.sin(altitude) * radius, - z: (Math.cos(altitude) * Math.sin(azimuth)) * radius - }; - position = Vec3.sum(center, vector); - Camera.setPosition(position); - Camera.setOrientation(orientationOf(vector)); + vector = { + x: (Math.cos(altitude) * Math.cos(azimuth)) * radius, + y: Math.sin(altitude) * radius, + z: (Math.cos(altitude) * Math.sin(azimuth)) * radius + }; + position = Vec3.sum(center, vector); + Camera.setPosition(position); + Camera.setOrientation(orientationOf(vector)); } - function handlePanMode(dx, dy) { - var up = Quat.getUp(Camera.getOrientation()); - var right = Quat.getRight(Camera.getOrientation()); - var distance = Vec3.length(vector); + var up = Quat.getUp(Camera.getOrientation()); + var right = Quat.getRight(Camera.getOrientation()); + var distance = Vec3.length(vector); - var dv = Vec3.sum(Vec3.multiply(up, distance * dy / PAN_RATE), Vec3.multiply(right, -distance * dx / PAN_RATE)); + var dv = Vec3.sum(Vec3.multiply(up, distance * dy / PAN_RATE), Vec3.multiply(right, -distance * dx / PAN_RATE)); - center = Vec3.sum(center, dv); - position = Vec3.sum(position, dv); + center = Vec3.sum(center, dv); + position = Vec3.sum(position, dv); - Camera.setPosition(position); - Camera.setOrientation(orientationOf(vector)); + Camera.setPosition(position); + Camera.setOrientation(orientationOf(vector)); } function saveCameraState() { - oldMode = Camera.mode; - oldPosition = Camera.getPosition(); - oldOrientation = Camera.getOrientation(); + oldMode = Camera.mode; + oldPosition = Camera.getPosition(); + oldOrientation = Camera.getOrientation(); - Camera.mode = "independent"; - Camera.setPosition(oldPosition); + Camera.mode = "independent"; + Camera.setPosition(oldPosition); } function restoreCameraState() { - Camera.mode = oldMode; - Camera.setPosition(oldPosition); - Camera.setOrientation(oldOrientation); + Camera.mode = oldMode; + Camera.setPosition(oldPosition); + Camera.setOrientation(oldOrientation); } function handleModes() { - var newMode = (mode == noMode) ? noMode : detachedMode; - if (alt) { - if (control) { - if (shift) { - newMode = panningMode; - } else { - newMode = orbitMode; - } - } else { - newMode = radialMode; + var newMode = (mode == noMode) ? noMode : detachedMode; + if (alt) { + if (control) { + if (shift) { + newMode = panningMode; + } else { + newMode = orbitMode; + } + } else { + newMode = radialMode; + } } - } - // if entering detachMode - if (newMode == detachedMode && mode != detachedMode) { - avatarPosition = MyAvatar.position; - avatarOrientation = MyAvatar.orientation; - } - // if leaving detachMode - if (mode == detachedMode && newMode == detachedMode && - (avatarPosition.x != MyAvatar.position.x || - avatarPosition.y != MyAvatar.position.y || - avatarPosition.z != MyAvatar.position.z || - avatarOrientation.x != MyAvatar.orientation.x || - avatarOrientation.y != MyAvatar.orientation.y || - avatarOrientation.z != MyAvatar.orientation.z || - avatarOrientation.w != MyAvatar.orientation.w)) { - newMode = noMode; - } + // if entering detachMode + if (newMode == detachedMode && mode != detachedMode) { + avatarPosition = MyAvatar.position; + avatarOrientation = MyAvatar.orientation; + } + // if leaving detachMode + if (mode == detachedMode && newMode == detachedMode && + (avatarPosition.x != MyAvatar.position.x || + avatarPosition.y != MyAvatar.position.y || + avatarPosition.z != MyAvatar.position.z || + avatarOrientation.x != MyAvatar.orientation.x || + avatarOrientation.y != MyAvatar.orientation.y || + avatarOrientation.z != MyAvatar.orientation.z || + avatarOrientation.w != MyAvatar.orientation.w)) { + newMode = noMode; + } - if (mode == noMode && newMode != noMode && Camera.mode == "independent") { - newMode = noMode; - } + if (mode == noMode && newMode != noMode && Camera.mode == "independent") { + newMode = noMode; + } - // if leaving noMode - if (mode == noMode && newMode != noMode) { - saveCameraState(); - } - // if entering noMode - if (newMode == noMode && mode != noMode) { - restoreCameraState(); - } + // if leaving noMode + if (mode == noMode && newMode != noMode) { + saveCameraState(); + } + // if entering noMode + if (newMode == noMode && mode != noMode) { + restoreCameraState(); + } - mode = newMode; + mode = newMode; } function keyPressEvent(event) { - var changed = false; + var changed = false; - if (event.text == "ALT") { - alt = true; - changed = true; - } - if (event.text == "CONTROL") { - control = true; - changed = true; - } - if (event.text == "SHIFT") { - shift = true; - changed = true; - } + if (event.text == "ALT") { + alt = true; + changed = true; + } + if (event.text == "CONTROL") { + control = true; + changed = true; + } + if (event.text == "SHIFT") { + shift = true; + changed = true; + } - if (changed) { - handleModes(); - } + if (changed) { + handleModes(); + } } function keyReleaseEvent(event) { - var changed = false; + var changed = false; - if (event.text == "ALT") { - alt = false; - changed = true; - mode = noMode; - restoreCameraState(); - } - if (event.text == "CONTROL") { - control = false; - changed = true; - } - if (event.text == "SHIFT") { - shift = false; - changed = true; - } - - if (changed) { - handleModes(); - } -} - - - -function mousePressEvent(event) { - if (alt && !isActive) { - mouseLastX = event.x; - mouseLastY = event.y; - - // Compute trajectories related values - var pickRay = Camera.computePickRay(mouseLastX, mouseLastY); - var modelIntersection = Entities.findRayIntersection(pickRay, true); - - position = Camera.getPosition(); - - var avatarTarget = MyAvatar.getTargetAvatarPosition(); - - - var distance = -1; - var string; - - if (modelIntersection.intersects && modelIntersection.accurate) { - distance = modelIntersection.distance; - center = modelIntersection.intersection; - string = "Inspecting model"; - //We've selected our target, now orbit towards it automatically - rotatingTowardsTarget = true; - //calculate our target cam rotation - Script.setTimeout(function() { - rotatingTowardsTarget = false; - }, LOOK_AT_TIME); - - vector = Vec3.subtract(position, center); - targetCamOrientation = orientationOf(vector); - radius = Vec3.length(vector); - azimuth = Math.atan2(vector.z, vector.x); - altitude = Math.asin(vector.y / Vec3.length(vector)); - - isActive = true; + if (event.text == "ALT") { + alt = false; + changed = true; + mode = noMode; + restoreCameraState(); + } + if (event.text == "CONTROL") { + control = false; + changed = true; + } + if (event.text == "SHIFT") { + shift = false; + changed = true; } - } + if (changed) { + handleModes(); + } +} + +function mousePressEvent(event) { + if (alt && !isActive) { + mouseLastX = event.x; + mouseLastY = event.y; + + // Compute trajectories related values + var pickRay = Camera.computePickRay(mouseLastX, mouseLastY); + var modelIntersection = Entities.findRayIntersection(pickRay, true); + var avatarIntersection = AvatarList.findRayIntersection(pickRay); + + position = Camera.getPosition(); + + if (avatarIntersection.intersects || (modelIntersection.intersects && modelIntersection.accurate)) { + if (avatarIntersection.intersects) { + center = avatarIntersection.intersection; + } else { + center = modelIntersection.intersection; + } + // We've selected our target, now orbit towards it automatically + rotatingTowardsTarget = true; + // calculate our target cam rotation + Script.setTimeout(function () { + rotatingTowardsTarget = false; + }, LOOK_AT_TIME); + + vector = Vec3.subtract(position, center); + targetCamOrientation = orientationOf(vector); + radius = Vec3.length(vector); + azimuth = Math.atan2(vector.z, vector.x); + altitude = Math.asin(vector.y / Vec3.length(vector)); + + isActive = true; + } + } } function mouseReleaseEvent(event) { - if (isActive) { - isActive = false; - } + if (isActive) { + isActive = false; + } } function mouseMoveEvent(event) { - if (isActive && mode != noMode && !rotatingTowardsTarget) { - if (mode == radialMode) { - handleRadialMode(event.x - mouseLastX, event.y - mouseLastY); + if (isActive && mode != noMode && !rotatingTowardsTarget) { + if (mode == radialMode) { + handleRadialMode(event.x - mouseLastX, event.y - mouseLastY); + } + if (mode == orbitMode) { + handleOrbitMode(event.x - mouseLastX, event.y - mouseLastY); + } + if (mode == panningMode) { + handlePanMode(event.x - mouseLastX, event.y - mouseLastY); + } } - if (mode == orbitMode) { - handleOrbitMode(event.x - mouseLastX, event.y - mouseLastY); - } - if (mode == panningMode) { - handlePanMode(event.x - mouseLastX, event.y - mouseLastY); - } - - } - mouseLastX = event.x; - mouseLastY = event.y; + mouseLastX = event.x; + mouseLastY = event.y; } function update() { - handleModes(); - if (rotatingTowardsTarget) { - rotateTowardsTarget(); - } + handleModes(); + if (rotatingTowardsTarget) { + rotateTowardsTarget(); + } } function rotateTowardsTarget() { - var newOrientation = Quat.mix(Camera.getOrientation(), targetCamOrientation, .1); - Camera.setOrientation(newOrientation); + var newOrientation = Quat.mix(Camera.getOrientation(), targetCamOrientation, 0.1); + Camera.setOrientation(newOrientation); } function scriptEnding() { - if (mode != noMode) { - restoreCameraState(); - } + if (mode != noMode) { + restoreCameraState(); + } } Controller.keyPressEvent.connect(keyPressEvent); @@ -345,4 +334,4 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mouseMoveEvent.connect(mouseMoveEvent); Script.update.connect(update); -Script.scriptEnding.connect(scriptEnding); \ No newline at end of file +Script.scriptEnding.connect(scriptEnding); diff --git a/scripts/+android/defaultScripts.js b/scripts/+android/defaultScripts.js index a8f6bf42a1..11aee6a9d2 100644 --- a/scripts/+android/defaultScripts.js +++ b/scripts/+android/defaultScripts.js @@ -16,7 +16,8 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/+android/touchscreenvirtualpad.js", "system/+android/bottombar.js", "system/+android/audio.js" , - "system/+android/modes.js"/*, + "system/+android/modes.js", + "system/+android/stats.js"/*, "system/away.js", "system/controllers/controllerDisplayManager.js", "system/controllers/handControllerGrabAndroid.js", diff --git a/scripts/developer/tests/messagesTests.js b/scripts/developer/tests/messagesTests.js index 18beafa4cc..094ad6f1c0 100644 --- a/scripts/developer/tests/messagesTests.js +++ b/scripts/developer/tests/messagesTests.js @@ -5,7 +5,7 @@ Messages.subscribe(channelName); //messageReceived(QString channel, QString message, QUuid senderUUID, bool localOnly); Messages.messageReceived.connect(function(channel, message, sender, local) { - print("message recieved on ", channel, " message:", message, " from:", sender, " local:", local); + print("message received on ", channel, " message:", message, " from:", sender, " local:", local); }); Messages.dataReceived.connect(function(channel, data, sender, local) { @@ -17,7 +17,7 @@ Messages.dataReceived.connect(function(channel, data, sender, local) { } dataAsString += int8data[i]; } - print("data recieved on ", channel, " from:", sender, " local:", local, "length of data:", int8data.length, " data:", dataAsString); + print("data received on ", channel, " from:", sender, " local:", local, "length of data:", int8data.length, " data:", dataAsString); }); var counter = 0; diff --git a/scripts/developer/tests/webOverlayTool.js b/scripts/developer/tests/webOverlayTool.js new file mode 100644 index 0000000000..1a3aa35205 --- /dev/null +++ b/scripts/developer/tests/webOverlayTool.js @@ -0,0 +1,102 @@ +// webSpawnTool.js +// +// Stress tests the rendering of web surfaces over time +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +SPAWNER = function (properties) { + properties = properties || {}; + var RADIUS = properties.radius || 5.0; // Spawn within this radius (square) + var SPAWN_COUNT = properties.count || 10000; // number of entities to spawn + var SPAWN_LIMIT = properties.spawnLimit || 1; + var SPAWN_INTERVAL = properties.spawnInterval || properties.interval || 2; + var SPAWN_LIFETIME = properties.lifetime || 10; // Entity timeout (when/if we crash, we need the entities to delete themselves) + + function randomPositionXZ(center, radius) { + return { + x: center.x + (Math.random() * radius * 2.0) - radius, + y: center.y, + z: center.z + (Math.random() * radius * 2.0) - radius + }; + } + + function makeObject(properties) { + + var overlay = Overlays.addOverlay("web3d", { + name: "Web", + url: "https://www.reddit.com/r/random", + localPosition: randomPositionXZ( { x: 0, y: 0, z: -1 }, 1), + localRotation: Quat.angleAxis(180, Vec3.Y_AXIS), + dimensions: {x: .8, y: .45, z: 0.1}, + color: { red: 255, green: 255, blue: 255 }, + alpha: 1.0, + showKeyboardFocusHighlight: false, + visible: true + }); + + var now = Date.now(); + + return { + destroy: function () { + Overlays.deleteOverlay(overlay) + }, + getAge: function () { + return (Date.now() - now) / 1000.0; + } + }; + } + + + var items = []; + var toCreate = 0; + var spawned = 0; + var spawnTimer = 0.0; + var keepAliveTimer = 0.0; + + function clear () { + } + + function create() { + toCreate = SPAWN_COUNT; + Script.update.connect(spawn); + } + + function spawn(dt) { + if (toCreate <= 0) { + Script.update.disconnect(spawn); + print("Finished spawning"); + } + else if ((spawnTimer -= dt) < 0.0){ + spawnTimer = SPAWN_INTERVAL; + + var n = Math.min(toCreate, SPAWN_LIMIT); + print("Spawning " + n + " items (" + (spawned += n) + ")"); + + toCreate -= n; + for (; n > 0; --n) { + items.push(makeObject()); + } + } + } + + function despawn() { + print("despawning"); + items.forEach(function (item) { + item.destroy(); + }); + item = []; + } + + function init () { + Script.update.disconnect(init); + Script.scriptEnding.connect(despawn); + clear(); + create(); + } + + Script.update.connect(init); +}; + +SPAWNER(); diff --git a/scripts/developer/utilities/audio/audioScope.js b/scripts/developer/utilities/audio/audioScope.js index 00c9e4b725..63cbf0a6e2 100644 --- a/scripts/developer/utilities/audio/audioScope.js +++ b/scripts/developer/utilities/audio/audioScope.js @@ -1,4 +1,4 @@ -var qml = Script.resourcesPath() + '/qml/AudioScope.qml'; +var qml = Script.resourcesPath() + '/qml/AudioScopeUI.qml'; var window = new OverlayWindow({ title: 'Audio Scope', source: qml, @@ -14,4 +14,4 @@ window.closed.connect(function () { AudioScope.setServerEcho(false); AudioScope.selectAudioScopeFiveFrames(); Script.stop(); -}); \ No newline at end of file +}); diff --git a/scripts/system/+android/audio.js b/scripts/system/+android/audio.js index b4f156d4bf..955f74d63a 100644 --- a/scripts/system/+android/audio.js +++ b/scripts/system/+android/audio.js @@ -46,7 +46,6 @@ function onMuteClicked() { printd("On Mute Clicked"); //Menu.setIsOptionChecked("Mute Microphone", !Menu.isOptionChecked("Mute Microphone")); Audio.muted = !Audio.muted; - onMuteToggled(); } function onMuteToggled() { diff --git a/scripts/system/+android/avatarSelection.js b/scripts/system/+android/avatarSelection.js index be58f61ac2..2946e541b5 100644 --- a/scripts/system/+android/avatarSelection.js +++ b/scripts/system/+android/avatarSelection.js @@ -34,6 +34,8 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See App.openUrl("https://metaverse.highfidelity.com/marketplace?category=avatars"); break; case 'hide': + Controller.setVPadHidden(false); + module.exports.hide(); module.exports.onHidden(); break; default: @@ -114,26 +116,27 @@ module.exports = { qml: "hifi/avatarSelection.qml", visible: false }); - /*, - visible: false*/ if (window) { window.fromQml.connect(fromQml); } init(); }, show: function() { + Controller.setVPadHidden(true); if (window) { window.setVisible(true); isVisible = true; } }, hide: function() { + Controller.setVPadHidden(false); if (window) { window.setVisible(false); } isVisible = false; }, destroy: function() { + Controller.setVPadHidden(false); if (window) { window.fromQml.disconnect(fromQml); window.close(); @@ -155,5 +158,7 @@ module.exports = { refreshSelectedAvatar: function(currentAvatarURL) { refreshSelected(currentAvatarURL); }, - onHidden: function() { } + onHidden: function() { + Controller.setVPadHidden(false); + } }; diff --git a/scripts/system/+android/modes.js b/scripts/system/+android/modes.js index b29548094f..c41ae1f327 100644 --- a/scripts/system/+android/modes.js +++ b/scripts/system/+android/modes.js @@ -17,7 +17,7 @@ var currentSelectedBtn; var SETTING_CURRENT_MODE_KEY = 'Android/Mode'; var MODE_VR = "VR", MODE_RADAR = "RADAR", MODE_MY_VIEW = "MY VIEW"; -var DEFAULT_MODE = MODE_RADAR; +var DEFAULT_MODE = MODE_MY_VIEW; var logEnabled = true; var radar = Script.require('./radar.js'); @@ -33,7 +33,6 @@ function init() { radar.setUniqueColor(uniqueColor); radar.init(); setupModesBar(); - radar.isTouchValid = isRadarModeValidTouch; } function shutdown() { @@ -183,34 +182,6 @@ function onButtonClicked(clickedButton, whatToDo, hideAllAfter) { } } -function isRadarModeValidTouch(coords) { - var qmlFragments = [modesbar.qmlFragment]; - var windows = []; - for (var i=0; i < qmlFragments.length; i++) { - var aQmlFrag = qmlFragments[i]; - if (aQmlFrag != null && aQmlFrag.isVisible() && - coords.x >= aQmlFrag.position.x * 3 && coords.x <= aQmlFrag.position.x * 3 + aQmlFrag.size.x * 3 && - coords.y >= aQmlFrag.position.y * 3 && coords.y <= aQmlFrag.position.y * 3 + aQmlFrag.size.y * 3 - ) { - printd("godViewModeTouchValid- false because of qmlFragments!? idx " + i); - return false; - } - } - - for (var i=0; i < windows.length; i++) { - var aWin = windows[i]; - if (aWin != null && aWin.position() != null && - coords.x >= aWin.position().x * 3 && coords.x <= aWin.position().x * 3 + aWin.width() * 3 && - coords.y >= aWin.position().y * 3 && coords.y <= aWin.position().y * 3 + aWin.height() * 3 - ) { - printd("godViewModeTouchValid- false because of windows!?"); - return false; - } - } - printd("godViewModeTouchValid- true by default "); - return true; -} - Script.scriptEnding.connect(function () { shutdown(); }); diff --git a/scripts/system/+android/radar.js b/scripts/system/+android/radar.js index 84fb66403f..455299dd5f 100644 --- a/scripts/system/+android/radar.js +++ b/scripts/system/+android/radar.js @@ -13,7 +13,7 @@ var radarModeInterface = {}; -var logEnabled = true; +var logEnabled = false; function printd(str) { if (logEnabled) { print("[radar.js] " + str); @@ -118,19 +118,10 @@ function actionOnObjectFromEvent(event) { } function mousePress(event) { - if (!isTouchValid(coords)) { - currentTouchIsValid = false; - return; - } else { - currentTouchIsValid = true; - } mousePressOrTouchEnd(event); } function mousePressOrTouchEnd(event) { - if (!currentTouchIsValid) { - return; - } if (radar) { if (actionOnObjectFromEvent(event)) { return; @@ -155,9 +146,6 @@ function fakeDoubleTap(event) { teleporter.dragTeleportRelease(event); } -var currentTouchIsValid = false; // Currently used to know if touch hasn't - // started on a UI overlay - var DOUBLE_TAP_TIME = 300; var fakeDoubleTapStart = Date.now(); var touchEndCount = 0; @@ -238,12 +226,6 @@ function touchEnd(event) { return; } - // if touch is invalid, cancel - if (!currentTouchIsValid) { - printd("touchEnd fail because !currentTouchIsValid"); - return; - } - if (analyzeDoubleTap(event)) return; // double tap detected, finish @@ -345,20 +327,6 @@ function computePointAtPlaneY(x, y, py) { p2.z, py); } -/******************************************************************************* - * - ******************************************************************************/ - -function isTouchValid(coords) { - // TODO: Extend to the detection of touches on new menu bars - var radarModeTouchValid = radarModeInterface.isTouchValid(coords); - - // getItemAtPoint does not exist anymore, look for another way to know if we - // are touching buttons - // is it still needed? - return /* !tablet.getItemAtPoint(coords) && */radarModeTouchValid; -} - /******************************************************************************* * ******************************************************************************/ @@ -373,16 +341,8 @@ function touchBegin(event) { x : event.x, y : event.y }; - if (!isTouchValid(coords)) { - printd("analyze touch - RADAR_TOUCH - INVALID"); - currentTouchIsValid = false; - touchStartingCoordinates = null; - } else { - printd("analyze touch - RADAR_TOUCH - ok"); - currentTouchIsValid = true; - touchStartingCoordinates = coords; - touchBeginTime = Date.now(); - } + touchStartingCoordinates = coords; + touchBeginTime = Date.now(); } var startedDraggingCamera = false; // first time @@ -848,9 +808,6 @@ function oneFingerTouchUpdate(event) { } function touchUpdate(event) { - if (!currentTouchIsValid) { - return; // avoid moving and zooming when tap is over UI entities - } if (event.isPinching || event.isPinchOpening) { pinchUpdate(event); } else { diff --git a/scripts/system/+android/stats.js b/scripts/system/+android/stats.js new file mode 100644 index 0000000000..a93bcb5794 --- /dev/null +++ b/scripts/system/+android/stats.js @@ -0,0 +1,39 @@ +"use strict"; +// +// stats.js +// scripts/system/ +// +// Created by Sam Gondelman on 3/14/18 +// 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 statsbar; +var statsButton; + +function init() { + statsbar = new QmlFragment({ + qml: "hifi/StatsBar.qml" + }); + + statsButton = statsbar.addButton({ + icon: "icons/stats.svg", + activeIcon: "icons/stats.svg", + textSize: 45, + bgOpacity: 0.0, + activeBgOpacity: 0.0, + bgColor: "#FFFFFF", + text: "STATS" + }); + statsButton.clicked.connect(function() { + Menu.triggerOption("Stats"); + }); +} + +init(); + +}()); // END LOCAL_SCOPE diff --git a/scripts/system/assets/images/Overlay-Viz-blank.png b/scripts/system/assets/images/Overlay-Viz-blank.png index 76f535b6e6..bbbf44f7a9 100644 Binary files a/scripts/system/assets/images/Overlay-Viz-blank.png and b/scripts/system/assets/images/Overlay-Viz-blank.png differ diff --git a/scripts/system/away.js b/scripts/system/away.js index 2a45786d0d..dc9b33e952 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -24,8 +24,9 @@ var OVERLAY_HEIGHT = 1080; var OVERLAY_DATA = { width: OVERLAY_WIDTH, height: OVERLAY_HEIGHT, - imageURL: "http://hifi-content.s3.amazonaws.com/alan/production/images/images/Overlay-Viz-blank.png", - color: {red: 255, green: 255, blue: 255}, + imageURL: Script.resolvePath("assets/images/Overlay-Viz-blank.png"), + emissive: true, + drawInFront: true, alpha: 1 }; var AVATAR_MOVE_FOR_ACTIVE_DISTANCE = 0.8; // meters -- no longer away if avatar moves this far while away @@ -37,7 +38,7 @@ var OVERLAY_DATA_HMD = { localRotation: {x: 0, y: 0, z: 0, w: 1}, width: OVERLAY_WIDTH, height: OVERLAY_HEIGHT, - url: "http://hifi-content.s3.amazonaws.com/alan/production/images/images/Overlay-Viz-blank.png", + url: Script.resolvePath("assets/images/Overlay-Viz-blank.png"), color: {red: 255, green: 255, blue: 255}, alpha: 1, scale: 2 * MyAvatar.sensorToWorldScale, diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 26ffb08796..d2e7d3ffc8 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -698,6 +698,9 @@ Window.location = "hifi://BankOfHighFidelity"; } break; + case 'wallet_availableUpdatesReceived': + // NOP + break; default: print('Unrecognized message from QML:', JSON.stringify(message)); } diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 09cea58cea..07450e54ba 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -475,7 +475,7 @@ Script.include("/~/system/libraries/Xform.js"); entityID = targetEntity.id; targetProps = targetEntity.props; - if (entityIsGrabbable(targetProps)) { + if (entityIsGrabbable(targetProps) || entityIsGrabbable(this.targetObject.entityProps)) { if (!entityIsDistanceGrabbable(targetProps)) { this.targetObject.makeDynamic(); } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 5e9aefcb07..cfaf517487 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -777,9 +777,12 @@ function findClickedEntity(event) { } var pickRay = Camera.computePickRay(event.x, event.y); - var overlayResult = Overlays.findRayIntersection(pickRay, true, getMainTabletIDs()); - if (overlayResult.intersects) { - return null; + var tabletIDs = getMainTabletIDs(); + if (tabletIDs.length > 0) { + var overlayResult = Overlays.findRayIntersection(pickRay, true, tabletIDs); + if (overlayResult.intersects) { + return null; + } } var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking @@ -968,8 +971,13 @@ function mouseReleaseEvent(event) { function wasTabletClicked(event) { var rayPick = Camera.computePickRay(event.x, event.y); - var result = Overlays.findRayIntersection(rayPick, true, getMainTabletIDs()); - return result.intersects; + var tabletIDs = getMainTabletIDs(); + if (tabletIDs.length === 0) { + return false; + } else { + var result = Overlays.findRayIntersection(rayPick, true, getMainTabletIDs()); + return result.intersects; + } } function mouseClickEvent(event) { diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index fb49de1050..864c7d92b4 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -30,6 +30,7 @@ var userIsLoggedIn = false; var walletNeedsSetup = false; var marketplaceBaseURL = "https://highfidelity.com"; + var messagesWaiting = false; function injectCommonCode(isDirectoryPage) { @@ -205,16 +206,22 @@ purchasesElement.id = "purchasesButton"; purchasesElement.setAttribute('href', "#"); - purchasesElement.innerHTML = "My Purchases"; + 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); $('#purchasesButton').on('click', function () { EventBridge.emitWebEvent(JSON.stringify({ type: "PURCHASES", - referrerURL: window.location.href + referrerURL: window.location.href, + hasUpdates: messagesWaiting })); }); } @@ -243,7 +250,7 @@ }); } - function buyButtonClicked(id, name, author, price, href, referrer) { + function buyButtonClicked(id, name, author, price, href, referrer, edition) { EventBridge.emitWebEvent(JSON.stringify({ type: "CHECKOUT", itemId: id, @@ -251,7 +258,8 @@ itemPrice: price ? parseInt(price, 10) : 0, itemHref: href, referrer: referrer, - itemAuthor: author + itemAuthor: author, + itemEdition: edition })); } @@ -319,7 +327,8 @@ $(this).closest('.grid-item').find('.creator').find('.value').text(), $(this).closest('.grid-item').find('.item-cost').text(), $(this).attr('data-href'), - "mainPage"); + "mainPage", + -1); }); } @@ -410,7 +419,11 @@ } var cost = $('.item-cost').text(); - if (availability !== 'available') { + var isUpdating = window.location.href.indexOf('edition=') > -1; + var urlParams = new URLSearchParams(window.location.search); + if (isUpdating) { + purchaseButton.html('UPDATE FOR FREE'); + } else if (availability !== 'available') { purchaseButton.html('UNAVAILABLE (' + availability + ')'); } else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { purchaseButton.html('PURCHASE 0; + messagesWaiting(userHasUpdates); + break; default: print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message)); } diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index ef7f35cf10..9d6ed0e595 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -13,8 +13,8 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries link_hifi_libraries( - shared task workload networking animation - ktx image octree gl gpu gpu-gl + shared task workload networking animation + ktx image octree gl gpu ${PLATFORM_GL_BACKEND} render render-utils graphics fbx model-networking graphics-scripting entities entities-renderer audio avatars script-engine diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp index 9eadc1dec2..4711dc4102 100644 --- a/tools/ac-client/src/ACClientApp.cpp +++ b/tools/ac-client/src/ACClientApp.cpp @@ -119,7 +119,7 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : nodeList->startThread(); const DomainHandler& domainHandler = nodeList->getDomainHandler(); - connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); + connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainChanged(QUrl))); connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ACClientApp::domainConnectionRefused); connect(nodeList.data(), &NodeList::nodeAdded, this, &ACClientApp::nodeAdded); @@ -169,7 +169,7 @@ void ACClientApp::domainConnectionRefused(const QString& reasonMessage, int reas qDebug() << "domainConnectionRefused"; } -void ACClientApp::domainChanged(const QString& domainHostname) { +void ACClientApp::domainChanged(QUrl domainURL) { if (_verbose) { qDebug() << "domainChanged"; } diff --git a/tools/ac-client/src/ACClientApp.h b/tools/ac-client/src/ACClientApp.h index d43e78eaeb..7a31b5ef5e 100644 --- a/tools/ac-client/src/ACClientApp.h +++ b/tools/ac-client/src/ACClientApp.h @@ -29,7 +29,7 @@ public: private slots: void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo); - void domainChanged(const QString& domainHostname); + void domainChanged(QUrl domainURL); void nodeAdded(SharedNodePointer node); void nodeActivated(SharedNodePointer node); void nodeKilled(SharedNodePointer node); diff --git a/tools/atp-client/src/ATPClientApp.cpp b/tools/atp-client/src/ATPClientApp.cpp index dbc2ad53f6..526065b2f7 100644 --- a/tools/atp-client/src/ATPClientApp.cpp +++ b/tools/atp-client/src/ATPClientApp.cpp @@ -158,7 +158,7 @@ ATPClientApp::ATPClientApp(int argc, char* argv[]) : nodeList->startThread(); const DomainHandler& domainHandler = nodeList->getDomainHandler(); - connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); + connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainChanged(QUrl))); connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ATPClientApp::domainConnectionRefused); connect(nodeList.data(), &NodeList::nodeAdded, this, &ATPClientApp::nodeAdded); @@ -227,7 +227,7 @@ void ATPClientApp::domainConnectionRefused(const QString& reasonMessage, int rea } } -void ATPClientApp::domainChanged(const QString& domainHostname) { +void ATPClientApp::domainChanged(QUrl domainURL) { if (_verbose) { qDebug() << "domainChanged"; } diff --git a/tools/atp-client/src/ATPClientApp.h b/tools/atp-client/src/ATPClientApp.h index 7ab4ec4a7a..63ee218e4b 100644 --- a/tools/atp-client/src/ATPClientApp.h +++ b/tools/atp-client/src/ATPClientApp.h @@ -31,7 +31,7 @@ public: private slots: void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo); - void domainChanged(const QString& domainHostname); + void domainChanged(QUrl domainURL); void nodeAdded(SharedNodePointer node); void nodeActivated(SharedNodePointer node); void nodeKilled(SharedNodePointer node); diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 549414cbab..71bb997303 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -18,4 +18,6 @@ elseif (APPLE) set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") endif() -install_beside_console() +if (BUILD_SERVER) + install_beside_console() +endif () diff --git a/tools/oven/src/BakerCLI.cpp b/tools/oven/src/BakerCLI.cpp index f5af5455fb..35550cdca8 100644 --- a/tools/oven/src/BakerCLI.cpp +++ b/tools/oven/src/BakerCLI.cpp @@ -18,6 +18,7 @@ #include "ModelBakingLoggingCategory.h" #include "BakerCLI.h" #include "FBXBaker.h" +#include "JSBaker.h" #include "TextureBaker.h" BakerCLI::BakerCLI(OvenCLIApplication* parent) : QObject(parent) { @@ -34,6 +35,7 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& qDebug() << "Baking file type: " << type; static const QString MODEL_EXTENSION { "fbx" }; + static const QString SCRIPT_EXTENSION { "js" }; QString extension = type; @@ -44,6 +46,7 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& // check what kind of baker we should be creating bool isFBX = extension == MODEL_EXTENSION; + bool isScript = extension == SCRIPT_EXTENSION; bool isSupportedImage = QImageReader::supportedImageFormats().contains(extension.toLatin1()); @@ -57,12 +60,16 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& outputPath) }; _baker->moveToThread(Oven::instance().getNextWorkerThread()); + } else if (isScript) { + _baker = std::unique_ptr { new JSBaker(inputUrl, outputPath) }; + _baker->moveToThread(Oven::instance().getNextWorkerThread()); } else if (isSupportedImage) { _baker = std::unique_ptr { new TextureBaker(inputUrl, image::TextureUsage::CUBE_TEXTURE, outputPath) }; _baker->moveToThread(Oven::instance().getNextWorkerThread()); } else { qCDebug(model_baking) << "Failed to determine baker type for file" << inputUrl; QCoreApplication::exit(OVEN_STATUS_CODE_FAIL); + return; } // invoke the bake method on the baker thread diff --git a/tools/oven/src/BakerCLI.h b/tools/oven/src/BakerCLI.h index 4f5b6607b0..bf33b625dd 100644 --- a/tools/oven/src/BakerCLI.h +++ b/tools/oven/src/BakerCLI.h @@ -14,6 +14,7 @@ #include #include +#include #include @@ -31,6 +32,8 @@ class BakerCLI : public QObject { public: BakerCLI(OvenCLIApplication* parent); + +public slots: void bakeFile(QUrl inputUrl, const QString& outputPath, const QString& type = QString::null); private slots: diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index 28ede982a0..3c6799db88 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "DomainBaker.h" + #include #include #include @@ -17,10 +19,9 @@ #include #include "Gzip.h" - #include "Oven.h" - -#include "DomainBaker.h" +#include "FBXBaker.h" +#include "OBJBaker.h" DomainBaker::DomainBaker(const QUrl& localModelFileURL, const QString& domainName, const QString& baseOutputPath, const QUrl& destinationPath, @@ -167,15 +168,18 @@ void DomainBaker::enumerateEntities() { // check if the file pointed to by this URL is a bakeable model, by comparing extensions auto modelFileName = modelURL.fileName(); - static const QString BAKEABLE_MODEL_EXTENSION { ".fbx" }; + static const QString BAKEABLE_MODEL_FBX_EXTENSION { ".fbx" }; + static const QString BAKEABLE_MODEL_OBJ_EXTENSION { ".obj" }; static const QString BAKED_MODEL_EXTENSION = ".baked.fbx"; - bool isBakedFBX = modelFileName.endsWith(BAKED_MODEL_EXTENSION, Qt::CaseInsensitive); - bool isUnbakedFBX = modelFileName.endsWith(BAKEABLE_MODEL_EXTENSION, Qt::CaseInsensitive) && !isBakedFBX; + bool isBakedModel = modelFileName.endsWith(BAKED_MODEL_EXTENSION, Qt::CaseInsensitive); + bool isBakeableFBX = modelFileName.endsWith(BAKEABLE_MODEL_FBX_EXTENSION, Qt::CaseInsensitive); + bool isBakeableOBJ = modelFileName.endsWith(BAKEABLE_MODEL_OBJ_EXTENSION, Qt::CaseInsensitive); + bool isBakeable = isBakeableFBX || isBakeableOBJ; - if (isUnbakedFBX || (_shouldRebakeOriginals && isBakedFBX)) { + if (isBakeable || (_shouldRebakeOriginals && isBakedModel)) { - if (isBakedFBX) { + if (isBakedModel) { // grab a URL to the original, that we assume is stored a directory up, in the "original" folder // with just the fbx extension qDebug() << "Re-baking original for" << modelURL; @@ -190,7 +194,7 @@ void DomainBaker::enumerateEntities() { modelURL = modelURL.adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment); } - // setup an FBXBaker for this URL, as long as we don't already have one + // setup a ModelBaker for this URL, as long as we don't already have one if (!_modelBakers.contains(modelURL)) { auto filename = modelURL.fileName(); auto baseName = filename.left(filename.lastIndexOf('.')); @@ -199,12 +203,23 @@ void DomainBaker::enumerateEntities() { while (QDir(_contentOutputPath + subDirName).exists()) { subDirName = "/" + baseName + "-" + QString::number(i++); } - QSharedPointer baker { - new FBXBaker(modelURL, []() -> QThread* { - return Oven::instance().getNextWorkerThread(); - }, _contentOutputPath + subDirName + "/baked", _contentOutputPath + subDirName + "/original"), - &FBXBaker::deleteLater - }; + + QSharedPointer baker; + if (isBakeableFBX) { + baker = { + new FBXBaker(modelURL, []() -> QThread* { + return Oven::instance().getNextWorkerThread(); + }, _contentOutputPath + subDirName + "/baked", _contentOutputPath + subDirName + "/original"), + &FBXBaker::deleteLater + }; + } else { + baker = { + new OBJBaker(modelURL, []() -> QThread* { + return Oven::instance().getNextWorkerThread(); + }, _contentOutputPath + subDirName + "/baked", _contentOutputPath + subDirName + "/original"), + &OBJBaker::deleteLater + }; + } // make sure our handler is called when the baker is done connect(baker.data(), &Baker::finished, this, &DomainBaker::handleFinishedModelBaker); @@ -299,16 +314,16 @@ void DomainBaker::bakeSkybox(QUrl skyboxURL, QJsonValueRef entity) { } void DomainBaker::handleFinishedModelBaker() { - auto baker = qobject_cast(sender()); + auto baker = qobject_cast(sender()); if (baker) { if (!baker->hasErrors()) { // this FBXBaker is done and everything went according to plan - qDebug() << "Re-writing entity references to" << baker->getFBXUrl(); + qDebug() << "Re-writing entity references to" << baker->getModelURL(); // enumerate the QJsonRef values for the URL of this FBX from our multi hash of // entity objects needing a URL re-write - for (QJsonValueRef entityValue : _entitiesNeedingRewrite.values(baker->getFBXUrl())) { + for (QJsonValueRef entityValue : _entitiesNeedingRewrite.values(baker->getModelURL())) { // convert the entity QJsonValueRef to a QJsonObject so we can modify its URL auto entity = entityValue.toObject(); @@ -317,7 +332,7 @@ void DomainBaker::handleFinishedModelBaker() { QUrl oldModelURL { entity[ENTITY_MODEL_URL_KEY].toString() }; // setup a new URL using the prefix we were passed - auto relativeFBXFilePath = baker->getBakedFBXFilePath().remove(_contentOutputPath); + auto relativeFBXFilePath = baker->getBakedModelFilePath().remove(_contentOutputPath); if (relativeFBXFilePath.startsWith("/")) { relativeFBXFilePath = relativeFBXFilePath.right(relativeFBXFilePath.length() - 1); } @@ -370,10 +385,10 @@ void DomainBaker::handleFinishedModelBaker() { } // remove the baked URL from the multi hash of entities needing a re-write - _entitiesNeedingRewrite.remove(baker->getFBXUrl()); + _entitiesNeedingRewrite.remove(baker->getModelURL()); // drop our shared pointer to this baker so that it gets cleaned up - _modelBakers.remove(baker->getFBXUrl()); + _modelBakers.remove(baker->getModelURL()); // emit progress to tell listeners how many models we have baked emit bakeProgress(++_completedSubBakes, _totalNumberOfSubBakes); diff --git a/tools/oven/src/DomainBaker.h b/tools/oven/src/DomainBaker.h index 6426af0710..e0286a51ff 100644 --- a/tools/oven/src/DomainBaker.h +++ b/tools/oven/src/DomainBaker.h @@ -61,7 +61,7 @@ private: QJsonArray _entities; - QHash> _modelBakers; + QHash> _modelBakers; QHash> _skyboxBakers; QMultiHash _entitiesNeedingRewrite; diff --git a/tools/oven/src/Oven.cpp b/tools/oven/src/Oven.cpp index 650683e1f0..c3fec2d15e 100644 --- a/tools/oven/src/Oven.cpp +++ b/tools/oven/src/Oven.cpp @@ -9,12 +9,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Oven.h" + #include #include #include -#include "Oven.h" +#include +#include +#include Oven* Oven::_staticInstance { nullptr }; @@ -29,9 +33,15 @@ Oven::Oven() { // setup our worker threads setupWorkerThreads(QThread::idealThreadCount()); + + // Initialize dependencies for OBJ Baker + DependencyManager::set(); + DependencyManager::set(false); } Oven::~Oven() { + DependencyManager::get()->cleanup(); + // quit all worker threads and wait on them for (auto& thread : _workerThreads) { thread->quit(); diff --git a/tools/oven/src/Oven.h b/tools/oven/src/Oven.h index effebb472e..c1acc07efb 100644 --- a/tools/oven/src/Oven.h +++ b/tools/oven/src/Oven.h @@ -14,6 +14,9 @@ #include #include +#include + +class QThread; class Oven { @@ -31,7 +34,7 @@ private: std::vector> _workerThreads; - std::atomic _nextWorkerThreadIndex; + std::atomic _nextWorkerThreadIndex; int _numWorkerThreads; static Oven* _staticInstance; diff --git a/tools/oven/src/OvenCLIApplication.cpp b/tools/oven/src/OvenCLIApplication.cpp index 38d9963eeb..2fb8ea03f2 100644 --- a/tools/oven/src/OvenCLIApplication.cpp +++ b/tools/oven/src/OvenCLIApplication.cpp @@ -40,7 +40,8 @@ OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) : QUrl inputUrl(QDir::fromNativeSeparators(parser.value(CLI_INPUT_PARAMETER))); QUrl outputUrl(QDir::fromNativeSeparators(parser.value(CLI_OUTPUT_PARAMETER))); QString type = parser.isSet(CLI_TYPE_PARAMETER) ? parser.value(CLI_TYPE_PARAMETER) : QString::null; - cli->bakeFile(inputUrl, outputUrl.toString(), type); + QMetaObject::invokeMethod(cli, "bakeFile", Qt::QueuedConnection, Q_ARG(QUrl, inputUrl), + Q_ARG(QString, outputUrl.toString()), Q_ARG(QString, type)); } else { parser.showHelp(); QCoreApplication::quit(); diff --git a/tools/oven/src/ui/BakeWidget.cpp b/tools/oven/src/ui/BakeWidget.cpp index 22e1b1aaf9..43f4c50328 100644 --- a/tools/oven/src/ui/BakeWidget.cpp +++ b/tools/oven/src/ui/BakeWidget.cpp @@ -41,5 +41,5 @@ void BakeWidget::cancelButtonClicked() { auto stackedWidget = qobject_cast(parentWidget()); stackedWidget->removeWidget(this); - this->deleteLater(); + deleteLater(); } diff --git a/tools/oven/src/ui/ModelBakeWidget.cpp b/tools/oven/src/ui/ModelBakeWidget.cpp index 61eea55917..f80185df0f 100644 --- a/tools/oven/src/ui/ModelBakeWidget.cpp +++ b/tools/oven/src/ui/ModelBakeWidget.cpp @@ -21,18 +21,21 @@ #include #include +#include "../Oven.h" #include "../OvenGUIApplication.h" - +#include "OvenMainWindow.h" +#include "FBXBaker.h" +#include "OBJBaker.h" #include "ModelBakeWidget.h" + static const auto EXPORT_DIR_SETTING_KEY = "model_export_directory"; static const auto MODEL_START_DIR_SETTING_KEY = "model_search_directory"; ModelBakeWidget::ModelBakeWidget(QWidget* parent, Qt::WindowFlags flags) : BakeWidget(parent, flags), _exportDirectory(EXPORT_DIR_SETTING_KEY), - _modelStartDirectory(MODEL_START_DIR_SETTING_KEY) -{ + _modelStartDirectory(MODEL_START_DIR_SETTING_KEY) { setupUI(); } @@ -113,7 +116,7 @@ void ModelBakeWidget::chooseFileButtonClicked() { startDir = QDir::homePath(); } - auto selectedFiles = QFileDialog::getOpenFileNames(this, "Choose Model", startDir, "Models (*.fbx)"); + auto selectedFiles = QFileDialog::getOpenFileNames(this, "Choose Model", startDir, "Models (*.fbx *.obj)"); if (!selectedFiles.isEmpty()) { // set the contents of the model file text box to be the path to the selected file @@ -189,7 +192,7 @@ void ModelBakeWidget::bakeButtonClicked() { subFolderName = modelName + "-" + QString::number(++iteration) + "/"; } - outputDirectory.mkdir(subFolderName); + outputDirectory.mkpath(subFolderName); if (!outputDirectory.exists()) { QMessageBox::warning(this, "Unable to create directory", "Unable to create output directory. Please create it manually or choose a different directory."); @@ -200,16 +203,25 @@ void ModelBakeWidget::bakeButtonClicked() { QDir bakedOutputDirectory = outputDirectory.absoluteFilePath("baked"); QDir originalOutputDirectory = outputDirectory.absoluteFilePath("original"); - + bakedOutputDirectory.mkdir("."); originalOutputDirectory.mkdir("."); - // everything seems to be in place, kick off a bake for this model now - auto baker = std::unique_ptr { - new FBXBaker(modelToBakeURL, []() -> QThread* { - return Oven::instance().getNextWorkerThread(); - }, bakedOutputDirectory.absolutePath(), originalOutputDirectory.absolutePath()) + std::unique_ptr baker; + auto getWorkerThreadCallback = []() -> QThread* { + return Oven::instance().getNextWorkerThread(); }; + // everything seems to be in place, kick off a bake for this model now + if (modelToBakeURL.fileName().endsWith(".fbx")) { + baker.reset(new FBXBaker(modelToBakeURL, getWorkerThreadCallback, bakedOutputDirectory.absolutePath(), + originalOutputDirectory.absolutePath())); + } else if (modelToBakeURL.fileName().endsWith(".obj")) { + baker.reset(new OBJBaker(modelToBakeURL, getWorkerThreadCallback, bakedOutputDirectory.absolutePath(), + originalOutputDirectory.absolutePath())); + } else { + qWarning() << "Unknown model type: " << modelToBakeURL.fileName(); + continue; + } // move the baker to the FBX baker thread baker->moveToThread(Oven::instance().getNextWorkerThread()); @@ -218,7 +230,7 @@ void ModelBakeWidget::bakeButtonClicked() { QMetaObject::invokeMethod(baker.get(), "bake"); // make sure we hear about the results of this baker when it is done - connect(baker.get(), &FBXBaker::finished, this, &ModelBakeWidget::handleFinishedBaker); + connect(baker.get(), &Baker::finished, this, &ModelBakeWidget::handleFinishedBaker); // add a pending row to the results window to show that this bake is in process auto resultsWindow = OvenGUIApplication::instance()->getMainWindow()->showResultsWindow(); @@ -231,27 +243,31 @@ void ModelBakeWidget::bakeButtonClicked() { } void ModelBakeWidget::handleFinishedBaker() { - if (auto baker = qobject_cast(sender())) { - // add the results of this bake to the results window - auto it = std::find_if(_bakers.begin(), _bakers.end(), [baker](const BakerRowPair& value) { - return value.first.get() == baker; - }); + Baker* baker = dynamic_cast(sender()); + if (!baker) { + qWarning() << "Received signal from unexpected sender"; + return; + } - for (auto& file : baker->getOutputFiles()) { - qDebug() << "Baked file: " << file; + // add the results of this bake to the results window + auto it = std::find_if(_bakers.begin(), _bakers.end(), [baker](const BakerRowPair& value) { + return value.first.get() == baker; + }); + + for (auto& file : baker->getOutputFiles()) { + qDebug() << "Baked file: " << file; + } + + if (it != _bakers.end()) { + auto resultRow = it->second; + auto resultsWindow = OvenGUIApplication::instance()->getMainWindow()->showResultsWindow(); + + if (baker->hasErrors()) { + resultsWindow->changeStatusForRow(resultRow, baker->getErrors().join("\n")); + } else { + resultsWindow->changeStatusForRow(resultRow, "Success"); } - if (it != _bakers.end()) { - auto resultRow = it->second; - auto resultsWindow = OvenGUIApplication::instance()->getMainWindow()->showResultsWindow(); - - if (baker->hasErrors()) { - resultsWindow->changeStatusForRow(resultRow, baker->getErrors().join("\n")); - } else { - resultsWindow->changeStatusForRow(resultRow, "Success"); - } - - _bakers.erase(it); - } + _bakers.erase(it); } } diff --git a/tools/oven/src/ui/ModelBakeWidget.h b/tools/oven/src/ui/ModelBakeWidget.h index b42b8725f6..fad623bf24 100644 --- a/tools/oven/src/ui/ModelBakeWidget.h +++ b/tools/oven/src/ui/ModelBakeWidget.h @@ -16,8 +16,6 @@ #include -#include - #include "BakeWidget.h" class QLineEdit; diff --git a/tools/oven/src/ui/OvenMainWindow.cpp b/tools/oven/src/ui/OvenMainWindow.cpp index dd40fb1f8f..bebc2fa7dc 100644 --- a/tools/oven/src/ui/OvenMainWindow.cpp +++ b/tools/oven/src/ui/OvenMainWindow.cpp @@ -46,7 +46,7 @@ ResultsWindow* OvenMainWindow::showResultsWindow(bool shouldRaise) { _resultsWindow->show(); // place the results window initially below our window - _resultsWindow->move(_resultsWindow->x(), this->frameGeometry().bottom()); + _resultsWindow->move(_resultsWindow->x(), frameGeometry().bottom()); } // show the results window and make sure it is in front diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 0b12cb64ed..a52e948f01 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -41,18 +41,17 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { } try { QByteArray fbxContents = fbx.readAll(); - FBXGeometry* geom; + FBXGeometry::Pointer geom; if (filename.toLower().endsWith(".obj")) { bool combineParts = false; geom = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts); } else if (filename.toLower().endsWith(".fbx")) { - geom = readFBX(fbxContents, QVariantHash(), filename); + geom.reset(readFBX(fbxContents, QVariantHash(), filename)); } else { qWarning() << "file has unknown extension" << filename; return false; } result = *geom; - delete geom; reSortFBXGeometryMeshes(result); } catch (const QString& error) {