mirror of
https://github.com/lubosz/overte.git
synced 2025-04-26 15:35:29 +02:00
Merge remote-tracking branch 'hifi/master' into android_ui_not_move_camera
This commit is contained in:
commit
6cb5cd7f92
197 changed files with 3116 additions and 7550 deletions
CMakeLists.txt
assignment-client/src
cmake
macros
templates
domain-server
resources/web
src
interface
CMakeLists.txt
resources
icons
qml
+android
controls-uit
dialogs
hifi
+android
commerce
checkout
common
purchases
wallet
styles-uit
src
libraries
animation/src
avatars-renderer/src/avatars-renderer
baking/src
controllers/src/controllers
display-plugins/src/display-plugins
embedded-webserver/src
entities-renderer/src
RenderableMaterialEntityItem.cppRenderableMaterialEntityItem.hRenderableShapeEntityItem.cppRenderableShapeEntityItem.h
entities/src
EntityEditPacketSender.cppEntityItem.cppEntityItemProperties.cppEntityScriptingInterface.cppEntityScriptingInterface.hEntityTree.cppEntityTree.h
gl/src/gl
gpu-gl-common
CMakeLists.txt
src/gpu/gl
GLBackend.cppGLBackend.hGLBackendInput.cppGLBackendOutput.cppGLBackendPipeline.cppGLBackendQuery.cppGLBackendShader.cppGLBackendState.cppGLBackendTexture.cppGLBackendTransform.cppGLBuffer.cppGLBuffer.hGLFramebuffer.cppGLFramebuffer.hGLInputFormat.cppGLInputFormat.hGLPipeline.cppGLPipeline.hGLQuery.hGLShader.cppGLShader.hGLShared.cppGLShared.hGLState.cppGLState.hGLTexelFormat.cppGLTexelFormat.hGLTexture.cppGLTexture.h
gpu-gl
|
@ -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,59 +27,67 @@ 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)
|
||||
|
||||
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)
|
||||
|
||||
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})
|
||||
|
@ -84,17 +96,17 @@ MESSAGE(STATUS "Build installer: " ${BUILD_INSTALLER})
|
|||
MESSAGE(STATUS "GL ES: " ${USE_GLES})
|
||||
|
||||
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()
|
||||
|
||||
#
|
||||
|
@ -160,16 +172,16 @@ endif()
|
|||
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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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<ReceivedMessage> mess
|
|||
void EntityScriptServer::aboutToFinish() {
|
||||
shutdownScriptEngine();
|
||||
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||
DependencyManager::get<EntityScriptingInterface>()->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<ResourceManager>()->cleanup();
|
||||
|
||||
|
|
|
@ -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})
|
||||
|
||||
|
@ -58,6 +58,23 @@ macro(GENERATE_INSTALLERS)
|
|||
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")
|
||||
|
@ -88,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()
|
||||
|
|
|
@ -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)
|
||||
|
@ -153,11 +158,6 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
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}")
|
||||
|
|
|
@ -46,3 +46,5 @@ 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@")
|
||||
|
|
|
@ -437,6 +437,12 @@ SectionEnd
|
|||
!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
|
||||
|
@ -612,10 +618,11 @@ Function InstallTypesPage
|
|||
|
||||
${If} $CustomInstallTemporaryState == ${BST_UNCHECKED}
|
||||
${NSD_Check} $ExpressInstallRadioButton
|
||||
Call ChangeExpressLabel
|
||||
${Else}
|
||||
Call ChangeCustomLabel
|
||||
${EndIf}
|
||||
|
||||
Call ChangeExpressLabel
|
||||
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
|
@ -649,7 +656,7 @@ Function PostInstallOptionsPage
|
|||
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
|
||||
|
@ -658,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
|
||||
|
@ -667,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
|
||||
|
||||
|
@ -681,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
|
||||
|
@ -694,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
|
||||
|
@ -703,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
|
||||
|
@ -711,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}
|
||||
|
@ -774,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
|
||||
|
||||
|
@ -792,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@"
|
||||
|
@ -820,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@"
|
||||
|
@ -849,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
|
||||
|
@ -886,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
|
||||
|
@ -900,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
|
||||
|
@ -1026,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@"
|
||||
|
||||
|
@ -1041,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}
|
||||
|
@ -1185,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}
|
||||
|
|
|
@ -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(){
|
|||
+ "<div class='dropdown'><div class='dropdown-toggle' data-toggle='dropdown' aria-expanded='false'><span class='glyphicon glyphicon-option-vertical'></span></div>"
|
||||
+ "<ul class='dropdown-menu dropdown-menu-right'>"
|
||||
+ "<li><a class='" + BACKUP_RESTORE_LINK_CLASS + "' href='#'>Restore from here</a></li><li class='divider'></li>"
|
||||
+ "<li><a class='" + BACKUP_DOWNLOAD_LINK_CLASS + "' href='/api/backups/" + backup.id + "'>Download</a></li><li class='divider'></li>"
|
||||
+ "<li><a class='" + BACKUP_DOWNLOAD_LINK_CLASS + "' data-backup-id='" + backup.id + "' href='#'>Download</a></li><li class='divider'></li>"
|
||||
+ "<li><a class='" + BACKUP_DELETE_LINK_CLASS + "' href='#' target='_blank'>Delete</a></li></ul></div></td>";
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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<std::mutex> 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<std::mutex> lock { _consolidatedBackupsMutex };
|
||||
auto& consolidatedBackup = _consolidatedBackups[fileName];
|
||||
consolidatedBackup.state = ConsolidatedBackupInfo::COMPLETE_WITH_ERROR;
|
||||
consolidatedBackup.error = error;
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> lock { _consolidatedBackupsMutex };
|
||||
auto& consolidatedBackup = _consolidatedBackups[fileName];
|
||||
consolidatedBackup.state = ConsolidatedBackupInfo::COMPLETE_WITH_SUCCESS;
|
||||
consolidatedBackup.absoluteFilePath = copyFilePath;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DomainContentBackupManager::createManualBackup(MiniPromise::Promise promise, const QString& name) {
|
||||
|
|
|
@ -15,9 +15,15 @@
|
|||
#ifndef hifi_DomainContentBackupManager_h
|
||||
#define hifi_DomainContentBackupManager_h
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <GenericThread.h>
|
||||
|
||||
|
@ -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<BackupHandlerPointer> _backupHandlers;
|
||||
std::chrono::milliseconds _persistInterval { 0 };
|
||||
|
||||
std::mutex _consolidatedBackupsMutex;
|
||||
std::unordered_map<QString, ConsolidatedBackupInfo> _consolidatedBackups;
|
||||
|
||||
std::atomic<bool> _isRecovering { false };
|
||||
QString _recoveryFilename { };
|
||||
|
||||
|
|
|
@ -164,6 +164,8 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
_iceServerAddr(ICE_SERVER_DEFAULT_HOSTNAME),
|
||||
_iceServerPort(ICE_SERVER_DEFAULT_PORT)
|
||||
{
|
||||
PathUtils::removeTemporaryApplicationDirs();
|
||||
|
||||
parseCommandLine();
|
||||
|
||||
DependencyManager::set<tracing::Tracer>();
|
||||
|
@ -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,37 +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<QFile>(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, id](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<QFile>(new QFile(path)) };
|
||||
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:" << 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) {
|
||||
|
@ -2216,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;
|
||||
|
||||
|
@ -2228,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
|
||||
|
@ -2626,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);
|
||||
|
@ -2671,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);
|
||||
|
@ -2702,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(' ');
|
||||
|
|
|
@ -26,17 +26,17 @@ 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()
|
||||
|
||||
|
@ -203,12 +203,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
|
||||
|
@ -232,6 +226,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()
|
||||
|
|
16
interface/resources/icons/+android/stats.svg
Normal file
16
interface/resources/icons/+android/stats.svg
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<path d="M45,40.3l-7.1-6.3c-0.7-0.7-1.7-0.9-2.6-0.8c2.3-2.9,3.6-6.4,3.6-10.3c0-9.9-7.9-17-16.9-17.1c-9.3,0-16.7,7.4-17,16.3
|
||||
C4.7,32,12.6,39.6,21.9,39.7c4.2,0,8.1-1.5,11-4.1c-0.2,0.9,0.1,1.9,0.9,2.6l7.1,6.3c1.2,1.1,3,1.1,4.1-0.1c0.5-0.6,0.8-1.3,0.8-2
|
||||
C45.9,41.6,45.6,40.8,45,40.3z M22.5,35.4c-7.3,0.3-13.2-5.5-13.2-12.8C9.2,15.8,14.9,10.1,21.9,10c7-0.1,12.8,5.6,12.8,12.7
|
||||
C34.7,29.7,29.2,35.2,22.5,35.4z"/>
|
||||
<path d="M26.2,19.9c-0.5,1.2-0.9,2.4-1.3,3.5c-0.6,1.6-1.2,3.2-1.8,4.8c-0.3,0.8-0.8,1.2-1.6,1.2c-0.8,0-1.2-0.3-1.6-1.2
|
||||
c-0.9-2.1-1.8-4.2-2.6-6.2c0-0.1-0.1-0.2-0.2-0.5c-0.3,0.5-0.6,0.9-0.8,1.4c-0.4,0.9-1.1,1.3-2.1,1.2c-0.6,0-1.2,0-1.8,0
|
||||
c-0.9,0-1.5-0.6-1.5-1.4c-0.1-0.7,0.5-1.4,1.3-1.5c0.2,0,0.4-0.1,0.5-0.1c0.7,0.1,1.1-0.2,1.5-0.9c0.5-1,1.1-2,1.6-3
|
||||
c0.3-0.6,0.8-1,1.6-1c0.7,0,1.2,0.5,1.5,1.1c0.8,1.8,1.6,3.7,2.3,5.5c0.1,0.2,0.1,0.3,0.3,0.6c0.2-0.6,0.4-1.1,0.6-1.6
|
||||
c0.9-2.4,1.8-4.8,2.7-7.2c0.3-0.9,0.8-1.2,1.5-1.2c0.8,0,1.3,0.4,1.6,1.2c0.7,1.8,1.3,3.6,1.9,5.5c0.4,1,0.1,0.8,1.1,0.8
|
||||
c0.4,0,0.8,0,1.1,0.1c0.7,0.2,1.2,0.8,1.1,1.6c-0.1,0.7-0.6,1.3-1.4,1.3c-1,0-2,0-3,0c-0.7,0-1.1-0.4-1.4-1.1
|
||||
C27,22.1,26.6,21.1,26.2,19.9z"/>
|
||||
</svg>
|
After (image error) Size: 1.5 KiB |
19
interface/resources/icons/tablet-icons/market-a-msg.svg
Normal file
19
interface/resources/icons/tablet-icons/market-a-msg.svg
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#EF3B4E;}
|
||||
</style>
|
||||
<circle class="st0" cx="44.1" cy="6" r="5.6"/>
|
||||
<g>
|
||||
<path d="M17.6,33.4c0.4,0,0.6,0,0.9,0c6.5,0,13,0,19.5,0c1.3,0,2,0.5,2.2,1.5c0.2,1.1-0.5,2.2-1.6,2.3c-0.2,0-0.5,0-0.7,0
|
||||
c-7.4,0-14.8,0-22.3,0c-2,0-2.7-0.9-2.2-2.9c0.3-1.1,0.5-2.3,0.8-3.4c0.3-0.9,0.2-1.8,0-2.8C12.7,22,11.1,15.9,9.6,9.8
|
||||
C9.4,9.3,9.2,9.1,8.7,9.1c-1.3,0-2.6,0-3.9,0c-1.2,0-2-0.8-2-1.9c0-1.1,0.8-1.9,2-1.9c2,0,3.9,0,5.9,0c1.1,0,1.7,0.5,2,1.5
|
||||
c0.4,1.3,1.6,4.8,1.9,6.2c2.2,0.1,4.3,0.2,6.5,0.3c6.7,0.3,11.4,0.4,18.1,0.7c1.2,0.1,2.3,0.1,3.5,0.2c1.4,0.1,2.2,1.4,1.6,2.6
|
||||
c-0.9,2.5-3.5,8.5-4.4,10.4c-0.4,0.8-1,1.1-1.9,1.2c-5.9,0.3-12.5,0.5-18.4,0.7c-0.8,0-1.2,0.3-1.3,1.1
|
||||
C18.4,30.7,17.7,32.9,17.6,33.4z"/>
|
||||
<path d="M39.2,42c0,1.9-1.6,3.6-3.5,3.6c-2,0-3.6-1.6-3.6-3.6c0-1.9,1.6-3.6,3.6-3.6C37.6,38.5,39.2,40.1,39.2,42z"/>
|
||||
<path d="M17.8,41.9c0,1.9-1.7,3.5-3.6,3.5c-1.9,0-3.5-1.7-3.5-3.6c0-2,1.6-3.6,3.6-3.6C16.2,38.3,17.8,39.9,17.8,41.9z"/>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 1.3 KiB |
|
@ -1,64 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 50 50"
|
||||
style="enable-background:new 0 0 50 50;"
|
||||
xml:space="preserve"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="market-a.svg"><metadata
|
||||
id="metadata20"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs18" /><sodipodi:namedview
|
||||
pagecolor="#ff0000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="852"
|
||||
inkscape:window-height="480"
|
||||
id="namedview16"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4.72"
|
||||
inkscape:cx="25"
|
||||
inkscape:cy="25"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" /><style
|
||||
type="text/css"
|
||||
id="style4">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style><g
|
||||
id="Layer_2" /><g
|
||||
id="Layer_1"
|
||||
style="fill:#000000;fill-opacity:1"><g
|
||||
id="g8"
|
||||
style="fill:#000000;fill-opacity:1"><path
|
||||
class="st0"
|
||||
d="M45.4,13.7c-1.6-0.1-3.2,0-4.8,0c-7.5,0-18,0-25.7,0c-0.6-1.8-1-3.5-1.7-5.2C13,7.9,12.2,7.1,11.5,7 C9.4,6.7,7.3,6.7,5.2,6.7c-1.1,0-1.9,0.5-1.9,1.7c0,1.2,0.8,1.7,1.9,1.7c1.3,0,2.7,0,4,0.1c0.5,0.1,1.2,0.6,1.4,1.1 c0.9,2.6,1.7,5.2,2.5,7.8c1.3,4.3,1.8,5.5,3.1,10.2c0.6,2.3,1.2,2.8,2.2,3.3c1.1,0.4,2.1,0.4,2.1,0.4h1.8c4.6,0,12.2,0,16.8,0 c1.1,0,2.1-0.1,2.6-1.4c1.9-5.1,3.8-10.2,5.7-15.3C47.8,14.7,47.1,13.8,45.4,13.7z M38.9,28.7c-0.1,0.3-0.8,0.7-1.2,0.7 c-4.6,0-12.2,0-16.8,0c-0.4,0-1.1-0.3-1.2-0.7c-1.3-3.8-2.4-7.6-3.7-11.5h27.1C41.8,21.2,40.4,24.9,38.9,28.7z"
|
||||
id="path10"
|
||||
style="fill:#000000;fill-opacity:1" /><path
|
||||
class="st0"
|
||||
d="M17.2,37.3L17.2,37.3c-1,0-1.7,0.2-2.2,0.7c-0.6,0.6-0.9,1.3-0.9,2.3c0,1,0.3,1.8,0.8,2.4 c0.6,0.6,1.4,0.9,2.2,0.9c1.8,0,3.2-1.4,3.2-3.2c0-0.9-0.3-1.7-0.9-2.3C18.8,37.6,18,37.3,17.2,37.3z"
|
||||
id="path12"
|
||||
style="fill:#000000;fill-opacity:1" /><path
|
||||
class="st0"
|
||||
d="M35.1,37.3L35.1,37.3c-1,0-1.7,0.2-2.2,0.7c-0.6,0.6-0.9,1.4-0.9,2.4c0,1,0.3,1.8,0.8,2.4 c0.6,0.6,1.3,0.9,2.2,0.9c1.8,0,3.2-1.5,3.2-3.3c0-0.9-0.3-1.6-0.9-2.2C36.8,37.6,36,37.3,35.1,37.3z"
|
||||
id="path14"
|
||||
style="fill:#000000;fill-opacity:1" /></g></g></svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M17.6,33.4c0.4,0,0.6,0,0.9,0c6.5,0,13,0,19.5,0c1.3,0,2,0.5,2.2,1.5c0.2,1.1-0.5,2.2-1.6,2.3c-0.2,0-0.5,0-0.7,0
|
||||
c-7.4,0-14.8,0-22.3,0c-2,0-2.7-0.9-2.2-2.9c0.3-1.1,0.5-2.3,0.8-3.4c0.3-0.9,0.2-1.8,0-2.8C12.7,22,11.1,15.9,9.6,9.8
|
||||
C9.4,9.3,9.2,9.1,8.7,9.1c-1.3,0-2.6,0-3.9,0c-1.2,0-2-0.8-2-1.9c0-1.1,0.8-1.9,2-1.9c2,0,3.9,0,5.9,0c1.1,0,1.7,0.5,2,1.5
|
||||
c0.4,1.3,1.6,4.8,1.9,6.2c2.2,0.1,4.3,0.2,6.5,0.3c6.7,0.3,11.4,0.4,18.1,0.7c1.2,0.1,2.3,0.1,3.5,0.2c1.4,0.1,2.2,1.4,1.6,2.6
|
||||
c-0.9,2.5-3.5,8.5-4.4,10.4c-0.4,0.8-1,1.1-1.9,1.2c-5.9,0.3-12.5,0.5-18.4,0.7c-0.8,0-1.2,0.3-1.3,1.1
|
||||
C18.4,30.7,17.7,32.9,17.6,33.4z"/>
|
||||
<path d="M39.2,42c0,1.9-1.6,3.6-3.5,3.6c-2,0-3.6-1.6-3.6-3.6c0-1.9,1.6-3.6,3.6-3.6C37.6,38.5,39.2,40.1,39.2,42z"/>
|
||||
<path d="M17.8,41.9c0,1.9-1.7,3.5-3.6,3.5c-1.9,0-3.5-1.7-3.5-3.6c0-2,1.6-3.6,3.6-3.6C16.2,38.3,17.8,39.9,17.8,41.9z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before (image error) Size: 3 KiB After (image error) Size: 1.2 KiB |
21
interface/resources/icons/tablet-icons/market-i-msg.svg
Normal file
21
interface/resources/icons/tablet-icons/market-i-msg.svg
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#EF3B4E;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<circle class="st0" cx="44.1" cy="6" r="5.6"/>
|
||||
<g>
|
||||
<path class="st1" d="M17.6,33.4c0.4,0,0.6,0,0.9,0c6.5,0,13,0,19.5,0c1.3,0,2,0.5,2.2,1.5c0.2,1.1-0.5,2.2-1.6,2.3
|
||||
c-0.2,0-0.5,0-0.7,0c-7.4,0-14.8,0-22.3,0c-2,0-2.7-0.9-2.2-2.9c0.3-1.1,0.5-2.3,0.8-3.4c0.3-0.9,0.2-1.8,0-2.8
|
||||
C12.7,22,11.1,15.9,9.6,9.8C9.4,9.3,9.2,9.1,8.7,9.1c-1.3,0-2.6,0-3.9,0c-1.2,0-2-0.8-2-1.9c0-1.1,0.8-1.9,2-1.9c2,0,3.9,0,5.9,0
|
||||
c1.1,0,1.7,0.5,2,1.5c0.4,1.3,1.6,4.8,1.9,6.2c2.2,0.1,4.3,0.2,6.5,0.3c6.7,0.3,11.4,0.4,18.1,0.7c1.2,0.1,2.3,0.1,3.5,0.2
|
||||
c1.4,0.1,2.2,1.4,1.6,2.6c-0.9,2.5-3.5,8.5-4.4,10.4c-0.4,0.8-1,1.1-1.9,1.2c-5.9,0.3-12.5,0.5-18.4,0.7c-0.8,0-1.2,0.3-1.3,1.1
|
||||
C18.4,30.7,17.7,32.9,17.6,33.4z"/>
|
||||
<path class="st1" d="M39.2,42c0,1.9-1.6,3.6-3.5,3.6c-2,0-3.6-1.6-3.6-3.6c0-1.9,1.6-3.6,3.6-3.6C37.6,38.5,39.2,40.1,39.2,42z"/>
|
||||
<path class="st1" d="M17.8,41.9c0,1.9-1.7,3.5-3.6,3.5c-1.9,0-3.5-1.7-3.5-3.6c0-2,1.6-3.6,3.6-3.6C16.2,38.3,17.8,39.9,17.8,41.9z
|
||||
"/>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 1.3 KiB |
|
@ -1,23 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
<g id="Layer_1">
|
||||
<g>
|
||||
<path class="st0" d="M45.4,13.7c-1.6-0.1-3.2,0-4.8,0c-7.5,0-18,0-25.7,0c-0.6-1.8-1-3.5-1.7-5.2C13,7.9,12.2,7.1,11.5,7
|
||||
C9.4,6.7,7.3,6.7,5.2,6.7c-1.1,0-1.9,0.5-1.9,1.7c0,1.2,0.8,1.7,1.9,1.7c1.3,0,2.7,0,4,0.1c0.5,0.1,1.2,0.6,1.4,1.1
|
||||
c0.9,2.6,1.7,5.2,2.5,7.8c1.3,4.3,1.8,5.5,3.1,10.2c0.6,2.3,1.2,2.8,2.2,3.3c1.1,0.4,2.1,0.4,2.1,0.4h1.8c4.6,0,12.2,0,16.8,0
|
||||
c1.1,0,2.1-0.1,2.6-1.4c1.9-5.1,3.8-10.2,5.7-15.3C47.8,14.7,47.1,13.8,45.4,13.7z M38.9,28.7c-0.1,0.3-0.8,0.7-1.2,0.7
|
||||
c-4.6,0-12.2,0-16.8,0c-0.4,0-1.1-0.3-1.2-0.7c-1.3-3.8-2.4-7.6-3.7-11.5h27.1C41.8,21.2,40.4,24.9,38.9,28.7z"/>
|
||||
<path class="st0" d="M17.2,37.3L17.2,37.3c-1,0-1.7,0.2-2.2,0.7c-0.6,0.6-0.9,1.3-0.9,2.3c0,1,0.3,1.8,0.8,2.4
|
||||
c0.6,0.6,1.4,0.9,2.2,0.9c1.8,0,3.2-1.4,3.2-3.2c0-0.9-0.3-1.7-0.9-2.3C18.8,37.6,18,37.3,17.2,37.3z"/>
|
||||
<path class="st0" d="M35.1,37.3L35.1,37.3c-1,0-1.7,0.2-2.2,0.7c-0.6,0.6-0.9,1.4-0.9,2.4c0,1,0.3,1.8,0.8,2.4
|
||||
c0.6,0.6,1.3,0.9,2.2,0.9c1.8,0,3.2-1.5,3.2-3.3c0-0.9-0.3-1.6-0.9-2.2C36.8,37.6,36,37.3,35.1,37.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st0" d="M17.6,33.4c0.4,0,0.6,0,0.9,0c6.5,0,13,0,19.5,0c1.3,0,2,0.5,2.2,1.5c0.2,1.1-0.5,2.2-1.6,2.3
|
||||
c-0.2,0-0.5,0-0.7,0c-7.4,0-14.8,0-22.3,0c-2,0-2.7-0.9-2.2-2.9c0.3-1.1,0.5-2.3,0.8-3.4c0.3-0.9,0.2-1.8,0-2.8
|
||||
C12.7,22,11.1,15.9,9.6,9.8C9.4,9.3,9.2,9.1,8.7,9.1c-1.3,0-2.6,0-3.9,0c-1.2,0-2-0.8-2-1.9c0-1.1,0.8-1.9,2-1.9c2,0,3.9,0,5.9,0
|
||||
c1.1,0,1.7,0.5,2,1.5c0.4,1.3,1.6,4.8,1.9,6.2c2.2,0.1,4.3,0.2,6.5,0.3c6.7,0.3,11.4,0.4,18.1,0.7c1.2,0.1,2.3,0.1,3.5,0.2
|
||||
c1.4,0.1,2.2,1.4,1.6,2.6c-0.9,2.5-3.5,8.5-4.4,10.4c-0.4,0.8-1,1.1-1.9,1.2c-5.9,0.3-12.5,0.5-18.4,0.7c-0.8,0-1.2,0.3-1.3,1.1
|
||||
C18.4,30.7,17.7,32.9,17.6,33.4z"/>
|
||||
<path class="st0" d="M39.2,42c0,1.9-1.6,3.6-3.5,3.6c-2,0-3.6-1.6-3.6-3.6c0-1.9,1.6-3.6,3.6-3.6C37.6,38.5,39.2,40.1,39.2,42z"/>
|
||||
<path class="st0" d="M17.8,41.9c0,1.9-1.7,3.5-3.6,3.5c-1.9,0-3.5-1.7-3.5-3.6c0-2,1.6-3.6,3.6-3.6C16.2,38.3,17.8,39.9,17.8,41.9z
|
||||
"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before (image error) Size: 1.4 KiB After (image error) Size: 1.3 KiB |
9
interface/resources/qml/+android/StatText.qml
Normal file
9
interface/resources/qml/+android/StatText.qml
Normal file
|
@ -0,0 +1,9 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
|
||||
Text {
|
||||
color: "white";
|
||||
style: Text.Outline;
|
||||
styleColor: "black";
|
||||
font.pixelSize: 15;
|
||||
}
|
321
interface/resources/qml/controls-uit/FilterBar.qml
Normal file
321
interface/resources/qml/controls-uit/FilterBar.qml
Normal file
|
@ -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.");
|
||||
}
|
||||
}
|
|
@ -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 != ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
71
interface/resources/qml/hifi/+android/StatsBar.qml
Normal file
71
interface/resources/qml/hifi/+android/StatsBar.qml
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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 " +
|
||||
"<b>domain's server settings</b> before you can replace this domain's content with <b>" + root.itemName + "</b>";
|
||||
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 = "<b>Your Wallet does not have sufficient funds to purchase this item again.</b>";
|
||||
// Else if you don't already own the item...
|
||||
} else {
|
||||
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item.</b>";
|
||||
}
|
||||
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 = "<b>Your Wallet does not have sufficient funds to purchase this item again.</b>";
|
||||
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 = "<b>Your Wallet does not have sufficient funds to purchase this item.</b>";
|
||||
}
|
||||
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 " +
|
||||
"<b>domain's server settings</b> before you can replace this domain's content with <b>" + root.itemName + "</b>";
|
||||
buyTextContainer.color = "#FFC3CD";
|
||||
buyTextContainer.border.color = "#F3808F";
|
||||
buyGlyph.text = hifi.glyphs.alert;
|
||||
buyGlyph.size = 54;
|
||||
}
|
||||
|
||||
handleBuyAgainLogic();
|
||||
}
|
||||
} else {
|
||||
buyText.text = "";
|
||||
|
@ -1062,11 +1162,13 @@ Rectangle {
|
|||
root.activeView = "checkoutMain";
|
||||
} else {
|
||||
root.activeView = "checkoutSuccess";
|
||||
root.ownershipStatusReceived = false;
|
||||
Commerce.alreadyOwned(root.itemId);
|
||||
root.availableUpdatesReceived = false;
|
||||
Commerce.getAvailableUpdates(root.itemId);
|
||||
root.balanceReceived = false;
|
||||
Commerce.balance();
|
||||
}
|
||||
root.balanceReceived = false;
|
||||
root.ownershipStatusReceived = false;
|
||||
Commerce.alreadyOwned(root.itemId);
|
||||
Commerce.balance();
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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: "<font color='#0093C5'><a href='#'>Update this item now</a></font>";
|
||||
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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -377,8 +377,6 @@ 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 std::vector<std::pair<QString, Application::AcceptURLMethod>> Application::_acceptedExtensions {
|
||||
{ SVO_EXTENSION, &Application::importSVOFromURL },
|
||||
{ SVO_JSON_EXTENSION, &Application::importSVOFromURL },
|
||||
|
@ -513,6 +511,27 @@ std::atomic<uint64_t> DeadlockWatchdogThread::_maxElapsed;
|
|||
std::atomic<int> DeadlockWatchdogThread::_maxElapsedAverage;
|
||||
ThreadSafeMovingAverage<int, DeadlockWatchdogThread::HEARTBEAT_SAMPLES> 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:
|
||||
|
@ -542,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<AddressManager>()->handleLookupString(url.toString());
|
||||
return true;
|
||||
}
|
||||
|
@ -940,6 +959,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
|
||||
setProperty(hifi::properties::CRASHED, _previousSessionCrashed);
|
||||
|
||||
_entityClipboard->setIsServerlessMode(true);
|
||||
|
||||
{
|
||||
const QString TEST_SCRIPT = "--testScript";
|
||||
const QString TRACE_FILE = "--traceFile";
|
||||
|
@ -1035,7 +1056,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();
|
||||
|
@ -1097,9 +1122,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]() {
|
||||
|
@ -2046,7 +2071,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();
|
||||
|
||||
|
@ -3023,27 +3048,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<AddressManager>()->goToLocalSandbox();
|
||||
sentTo = SENT_TO_SANDBOX;
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
|
||||
DependencyManager::get<AddressManager>()->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<AddressManager>()->goToLocalSandbox();
|
||||
sentTo = SENT_TO_SANDBOX;
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
|
||||
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
|
||||
sentTo = SENT_TO_PREVIOUS_LOCATION;
|
||||
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
|
||||
DependencyManager::get<AddressManager>()->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<AddressManager>()->loadSettings(addressLookupString);
|
||||
sentTo = SENT_TO_PREVIOUS_LOCATION;
|
||||
}
|
||||
|
||||
UserActivityLogger::getInstance().logAction("startup_sent_to", {
|
||||
{ "sent_to", sentTo },
|
||||
|
@ -3083,6 +3108,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>();
|
||||
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);
|
||||
|
@ -4583,7 +4659,7 @@ void Application::initDisplay() {
|
|||
}
|
||||
|
||||
void Application::init() {
|
||||
|
||||
|
||||
// Make sure Login state is up to date
|
||||
DependencyManager::get<DialogsManager>()->toggleLoginDialog();
|
||||
if (!DISABLE_DEFERRED) {
|
||||
|
@ -4608,7 +4684,9 @@ void Application::init() {
|
|||
qCDebug(interfaceapp) << "Loaded settings";
|
||||
|
||||
// fire off an immediate domain-server check in now that settings are loaded
|
||||
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
|
||||
if (!isServerlessMode()) {
|
||||
DependencyManager::get<NodeList>()->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.
|
||||
|
@ -5742,10 +5820,15 @@ void Application::updateWindowTitle() const {
|
|||
|
||||
QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
|
||||
QString username = accountManager->getAccountInfo().getUsername();
|
||||
QString currentPlaceName = DependencyManager::get<AddressManager>()->getHost();
|
||||
|
||||
if (currentPlaceName.isEmpty()) {
|
||||
currentPlaceName = nodeList->getDomainHandler().getHostname();
|
||||
QString currentPlaceName;
|
||||
if (isServerlessMode()) {
|
||||
currentPlaceName = "serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
|
||||
} else {
|
||||
currentPlaceName = DependencyManager::get<AddressManager>()->getDomainURL().host();
|
||||
if (currentPlaceName.isEmpty()) {
|
||||
currentPlaceName = nodeList->getDomainHandler().getHostname();
|
||||
}
|
||||
}
|
||||
|
||||
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
|
||||
|
@ -5758,7 +5841,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", "");
|
||||
}
|
||||
|
||||
|
@ -5797,15 +5880,22 @@ void Application::clearDomainAvatars() {
|
|||
DependencyManager::get<AvatarManager>()->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<NodeList>();
|
||||
clearDomainOctreeDetails();
|
||||
}
|
||||
|
||||
void Application::nodeAdded(SharedNodePointer node) const {
|
||||
|
@ -5922,22 +6012,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<bool (EntityItemPointer&)> 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<bool(const OctreeElementPointer&, void*)> elementScan = [&](const OctreeElementPointer& element, void* unused) {
|
||||
if (element->getAACube().touches(avatarBox)) {
|
||||
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
|
||||
entityTreeElement->getEntities(entityScan, entities);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (element->getAACube().touches(avatarBox)) {
|
||||
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
|
||||
entityTreeElement->getEntities(entityScan, entities);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
entityTree->withReadLock([&] {
|
||||
// Pass the second function to the general-purpose EntityTree::findEntities()
|
||||
|
@ -6173,7 +6263,7 @@ 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;
|
||||
}
|
||||
QString lowerPath = url.path().toLower();
|
||||
|
@ -6186,15 +6276,14 @@ 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<AddressManager>().data(), "handleLookupString",
|
||||
Qt::AutoConnection, Q_ARG(const QString&, urlString));
|
||||
return true;
|
||||
}
|
||||
|
||||
QUrl url(urlString);
|
||||
QString lowerPath = url.path().toLower();
|
||||
for (auto& pair : _acceptedExtensions) {
|
||||
if (lowerPath.endsWith(pair.first, Qt::CaseInsensitive)) {
|
||||
|
@ -7033,7 +7122,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<AddressManager>()->handleLookupString(url.toString());
|
||||
} else {
|
||||
// address manager did not handle - ask QDesktopServices to handle
|
||||
|
@ -7347,10 +7436,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) {
|
||||
|
|
|
@ -161,6 +161,8 @@ public:
|
|||
QRect getRecommendedHUDRect() const;
|
||||
glm::vec2 getDeviceSize() const;
|
||||
bool hasFocus() const;
|
||||
void setFocus();
|
||||
void raise();
|
||||
|
||||
void showCursor(const Cursor::Icon& cursor);
|
||||
|
||||
|
@ -282,6 +284,8 @@ public:
|
|||
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
|
||||
void saveNextPhysicsStats(QString filename);
|
||||
|
||||
bool isServerlessMode() const;
|
||||
|
||||
void replaceDomainContent(const QString& url);
|
||||
|
||||
signals:
|
||||
|
@ -293,7 +297,6 @@ signals:
|
|||
void activeDisplayPluginChanged();
|
||||
|
||||
void uploadRequest(QString path);
|
||||
void receivedHifiSchemeURL(const QString& url);
|
||||
|
||||
public slots:
|
||||
QVector<EntityItemID> pasteEntities(float x, float y, float z);
|
||||
|
@ -389,6 +392,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:
|
||||
|
@ -423,7 +429,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);
|
||||
|
|
|
@ -49,7 +49,7 @@ void DiscoverabilityManager::updateLocation() {
|
|||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
|
||||
bool discoverable = (_mode.get() != Discoverability::None);
|
||||
bool discoverable = (_mode.get() != Discoverability::None) && !domainHandler.isServerless();
|
||||
|
||||
|
||||
if (accountManager->isLoggedIn()) {
|
||||
|
|
|
@ -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<AccountManager>();
|
||||
|
@ -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<Wallet>();
|
||||
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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<AccountManager>();
|
||||
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>();
|
||||
ledger->getAvailableUpdates(itemId);
|
||||
}
|
||||
|
||||
void QmlCommerce::updateItem(const QString& certificateId) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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<OffscreenUi>();
|
||||
|
|
|
@ -74,16 +74,14 @@ 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::raiseMainWindow() {
|
||||
// It's forbidden to call raise() from another thread.
|
||||
qApp->postLambdaEvent([] {
|
||||
qApp->getWindow()->raise();
|
||||
qApp->raise();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -126,7 +124,7 @@ void WindowScriptingInterface::promptAsync(const QString& message, const QString
|
|||
}
|
||||
|
||||
void WindowScriptingInterface::disconnectedFromDomain() {
|
||||
emit domainChanged("");
|
||||
emit domainChanged(QUrl());
|
||||
}
|
||||
|
||||
QString fixupPathForMac(const QString& directory) {
|
||||
|
|
|
@ -62,13 +62,14 @@ 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.raiseMainWindow
|
||||
*/
|
||||
void raiseMainWindow();
|
||||
|
@ -523,7 +524,7 @@ signals:
|
|||
* Triggered when you change the domain you're visiting. <strong>Warning:</strong> 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 <caption>Report when you change domains.</caption>
|
||||
* function onDomainChanged(domain) {
|
||||
|
@ -532,7 +533,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.
|
||||
|
|
|
@ -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<DialogsManager>().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed);
|
||||
connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL);
|
||||
}
|
||||
|
||||
void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions) {
|
||||
|
|
|
@ -36,7 +36,6 @@ signals:
|
|||
void backEnabledChanged();
|
||||
void forwardEnabledChanged();
|
||||
void useFeedChanged();
|
||||
void receivedHifiSchemeURL(const QString& url);
|
||||
void hostChanged();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<NodeList>()->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<EntityTreeRenderer>();
|
||||
|
@ -1806,4 +1810,4 @@ scriptable::ScriptableModelBase Avatar::getScriptableModel() {
|
|||
result.appendMaterials(_materials);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 == '`');
|
||||
}
|
||||
|
|
|
@ -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<QString> UserInputMapper::getDeviceNames() {
|
||||
Locker locker(_lock);
|
||||
QVector<QString> 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<Action> 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<QString> UserInputMapper::getActionNames() const {
|
||||
Locker locker(_lock);
|
||||
QVector<QString> 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<ArrayEndpoint>();
|
||||
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 <typename T>
|
||||
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;
|
||||
|
|
|
@ -16,11 +16,14 @@
|
|||
using namespace controller;
|
||||
|
||||
void ActionEndpoint::apply(float newValue, const Pointer& source) {
|
||||
InputRecorder* inputRecorder = InputRecorder::getInstance();
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
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<UserInputMapper>();
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -55,7 +55,7 @@ HTTPConnection::~HTTPConnection() {
|
|||
|
||||
QHash<QString, QString> HTTPConnection::parseUrlEncodedForm() {
|
||||
// make sure we have the correct MIME type
|
||||
QList<QByteArray> elements = _requestHeaders.value("Content-Type").split(';');
|
||||
QList<QByteArray> elements = requestHeader("Content-Type").split(';');
|
||||
|
||||
QString contentType = elements.at(0).trimmed();
|
||||
if (contentType != "application/x-www-form-urlencoded") {
|
||||
|
@ -75,7 +75,7 @@ QHash<QString, QString> HTTPConnection::parseUrlEncodedForm() {
|
|||
|
||||
QList<FormData> HTTPConnection::parseFormData() const {
|
||||
// make sure we have the correct MIME type
|
||||
QList<QByteArray> elements = _requestHeaders.value("Content-Type").split(';');
|
||||
QList<QByteArray> 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(", ");
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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<float>& 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<float>& 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<gpu::Stream::Format> MaterialEntityRenderer::_streamFormat = nullptr;
|
||||
std::shared_ptr<gpu::BufferStream> MaterialEntityRenderer::_stream = nullptr;
|
||||
std::shared_ptr<gpu::Buffer> MaterialEntityRenderer::_verticesBuffer = nullptr;
|
||||
|
||||
void MaterialEntityRenderer::generateMesh() {
|
||||
_streamFormat = std::make_shared<gpu::Stream::Format>();
|
||||
_stream = std::make_shared<gpu::BufferStream>();
|
||||
_verticesBuffer = std::make_shared<gpu::Buffer>();
|
||||
|
||||
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<float> 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<GeometryCache>()->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<GeometryCache>()->getSphereTriangleCount();
|
||||
}
|
||||
|
|
|
@ -40,20 +40,6 @@ private:
|
|||
Transform _renderTransform;
|
||||
|
||||
std::shared_ptr<NetworkMaterial> _drawMaterial;
|
||||
|
||||
static int _numVertices;
|
||||
static std::shared_ptr<gpu::Stream::Format> _streamFormat;
|
||||
static std::shared_ptr<gpu::BufferStream> _stream;
|
||||
static std::shared_ptr<gpu::Buffer> _verticesBuffer;
|
||||
|
||||
void generateMesh();
|
||||
void addTriangleFan(std::vector<float>& 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<float>& 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;
|
||||
};
|
||||
|
||||
} }
|
||||
|
|
|
@ -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 <FadeEffect.h>
|
||||
|
@ -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<bool>([&] {
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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([&] {
|
||||
|
|
|
@ -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 <code>"Box"</code>, <code>"Shape"</code>, and <code>"Sphere"</code> depending on changes to
|
||||
* the <code>shape</code> property set for entities of these types.) <em>Read-only.</em>
|
||||
* @property {boolean} clientOnly=false - If <code>true</code> then the entity is an avatar entity, otherwise it is a server
|
||||
* entity. <em>Read-only.</em>
|
||||
* @property {boolean} clientOnly=false - If <code>true</code> 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. <em>Value cannot be changed after the entity is created.</em><br />
|
||||
* The value can also be set at entity creation by using the <code>clientOnly</code> parameter in
|
||||
* {@link Entities.addEntity}.
|
||||
* @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if <code>clientOnly</code> is
|
||||
* <code>true</code>, otherwise {@link Uuid|Uuid.NULL}. <em>Read-only.</em>
|
||||
*
|
||||
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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 <code>true</code>, 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 <code>true</code>, or if <code>clientOnly</code> is set <code>true</code> 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 <caption>Create a box entity in front of your avatar.</caption>
|
||||
* var entityID = Entities.addEntity({
|
||||
|
|
|
@ -493,7 +493,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;
|
||||
}
|
||||
|
||||
|
@ -1509,7 +1509,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);
|
||||
}
|
||||
|
@ -2181,23 +2182,25 @@ QVector<EntityItemID> 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();
|
||||
}
|
||||
|
|
|
@ -283,6 +283,9 @@ public:
|
|||
|
||||
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
|
||||
|
||||
void setIsServerlessMode(bool value) { _serverlessDomain = value; }
|
||||
bool isServerlessMode() const { return _serverlessDomain; }
|
||||
|
||||
static void setAddMaterialToEntityOperator(std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> addMaterialToEntityOperator) { _addMaterialToEntityOperator = addMaterialToEntityOperator; }
|
||||
static void setRemoveMaterialFromEntityOperator(std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> removeMaterialFromEntityOperator) { _removeMaterialFromEntityOperator = removeMaterialFromEntityOperator; }
|
||||
static bool addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName);
|
||||
|
@ -325,7 +328,7 @@ protected:
|
|||
void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);
|
||||
|
||||
bool isScriptInWhitelist(const QString& scriptURL);
|
||||
|
||||
|
||||
QReadWriteLock _newlyCreatedHooksLock;
|
||||
QVector<NewlyCreatedEntityHook*> _newlyCreatedHooks;
|
||||
|
||||
|
@ -412,6 +415,8 @@ private:
|
|||
static std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> _removeMaterialFromAvatarOperator;
|
||||
static std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> _addMaterialToOverlayOperator;
|
||||
static std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> _removeMaterialFromOverlayOperator;
|
||||
|
||||
bool _serverlessDomain { false };
|
||||
};
|
||||
|
||||
#endif // hifi_EntityTree_h
|
||||
|
|
|
@ -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);
|
||||
|
|
6
libraries/gpu-gl-common/CMakeLists.txt
Normal file
6
libraries/gpu-gl-common/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(TARGET_NAME gpu-gl-common)
|
||||
setup_hifi_library(Concurrent)
|
||||
link_hifi_libraries(shared gl gpu)
|
||||
GroupSources("src")
|
||||
target_opengl()
|
||||
|
|
@ -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 <functional>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include "../gles/GLESBackend.h"
|
||||
|
||||
#if defined(NSIGHT_FOUND)
|
||||
#include "nvToolsExt.h"
|
||||
#endif
|
||||
|
||||
#include <shared/GlobalAppProperties.h>
|
||||
#include <GPUIdent.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
#include <QtCore/QProcessEnvironment>
|
||||
|
||||
#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<GLBackend> result;
|
||||
|
||||
qDebug() << "Using OpenGL ES backend";
|
||||
result = std::make_shared<gpu::gles::GLESBackend>();
|
||||
|
||||
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<GLBackend*>(qApp->property(hifi::properties::gl::BACKEND).value<void*>());
|
||||
}
|
||||
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),
|
||||
|
@ -151,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
|
||||
|
@ -228,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);
|
||||
}
|
||||
|
||||
|
@ -296,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
|
||||
{
|
||||
|
@ -305,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
|
||||
|
|
@ -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
|
|
@ -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;
|
|
@ -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() {
|
|
@ -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<GLenum, NUM_SHADER_DOMAINS> SHADER_DOMAINS { {
|
||||
static const std::array<GLenum, NUM_SHADER_DOMAINS> SHADER_DOMAINS{ {
|
||||
GL_VERTEX_SHADER,
|
||||
GL_FRAGMENT_SHADER,
|
||||
GL_GEOMETRY_SHADER,
|
||||
|
@ -30,22 +47,33 @@ static const std::array<GLenum, NUM_SHADER_DOMAINS> SHADER_DOMAINS { {
|
|||
|
||||
// Domain specific defines
|
||||
// Must match the order of type specified in gpu::Shader::Type
|
||||
static const std::array<std::string, NUM_SHADER_DOMAINS> DOMAIN_DEFINES { {
|
||||
static const std::array<std::string, NUM_SHADER_DOMAINS> 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?";
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
@ -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;
|
|
@ -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);
|
|
@ -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:
|
|
@ -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)
|
||||
|
|
|
@ -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 <mutex>
|
||||
#include <queue>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include "../gl41/GL41Backend.h"
|
||||
#include "../gl45/GL45Backend.h"
|
||||
|
||||
#if defined(NSIGHT_FOUND)
|
||||
#include "nvToolsExt.h"
|
||||
#endif
|
||||
|
||||
#include <shared/GlobalAppProperties.h>
|
||||
#include <GPUIdent.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
#include <QtCore/QProcessEnvironment>
|
||||
|
||||
#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<GLBackend> result;
|
||||
if (!disableOpenGL45 && version >= 0x0405) {
|
||||
qCDebug(gpugllogging) << "Using OpenGL 4.5 backend";
|
||||
result = std::make_shared<gpu::gl45::GL45Backend>();
|
||||
} else {
|
||||
qCDebug(gpugllogging) << "Using OpenGL 4.1 backend";
|
||||
result = std::make_shared<gpu::gl41::GL41Backend>();
|
||||
}
|
||||
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<GLBackend*>(qApp->property(hifi::properties::gl::BACKEND).value<void*>());
|
||||
}
|
||||
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<void()> 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<void()> lambda) const {
|
||||
Lock lock(_trashMutex);
|
||||
_lambdaQueue.push_back(lambda);
|
||||
}
|
||||
|
||||
void GLBackend::recycle() const {
|
||||
PROFILE_RANGE(render_gpu_gl, __FUNCTION__)
|
||||
{
|
||||
std::list<std::function<void()>> lamdbasTrash;
|
||||
{
|
||||
Lock lock(_trashMutex);
|
||||
std::swap(_lambdaQueue, lamdbasTrash);
|
||||
}
|
||||
for (auto lambda : lamdbasTrash) {
|
||||
lambda();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<GLuint> ids;
|
||||
std::list<std::pair<GLuint, Size>> 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<GLuint> ids;
|
||||
std::list<GLuint> 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<GLuint> ids;
|
||||
std::list<std::pair<GLuint, Size>> 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<std::pair<GLuint, Texture::ExternalRecycler>> externalTexturesTrash;
|
||||
{
|
||||
Lock lock(_trashMutex);
|
||||
std::swap(_externalTexturesTrash, externalTexturesTrash);
|
||||
}
|
||||
if (!externalTexturesTrash.empty()) {
|
||||
std::vector<GLsync> 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<GLuint> programsTrash;
|
||||
{
|
||||
Lock lock(_trashMutex);
|
||||
std::swap(_programsTrash, programsTrash);
|
||||
}
|
||||
for (auto id : programsTrash) {
|
||||
glDeleteProgram(id);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::list<GLuint> shadersTrash;
|
||||
{
|
||||
Lock lock(_trashMutex);
|
||||
std::swap(_shadersTrash, shadersTrash);
|
||||
}
|
||||
for (auto id : shadersTrash) {
|
||||
glDeleteShader(id);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<GLuint> ids;
|
||||
std::list<GLuint> 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();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue