From 07600f2a81b98c8c3317fa53b02dfb7acd731529 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 3 May 2018 20:21:05 -0400 Subject: [PATCH 01/38] add etc2comp dependency on desktop and android --- android/build.gradle | 45 ++++++++++++++----------- cmake/externals/etc2comp/CMakeLists.txt | 44 ++++++++++++++++++++++++ cmake/macros/TargetEtc2Comp.cmake | 22 ++++++++++++ cmake/modules/FindEtc2Comp.cmake | 37 ++++++++++++++++++++ libraries/image/CMakeLists.txt | 1 + libraries/image/src/image/Image.cpp | 6 ++++ 6 files changed, 136 insertions(+), 19 deletions(-) create mode 100644 cmake/externals/etc2comp/CMakeLists.txt create mode 100644 cmake/macros/TargetEtc2Comp.cmake create mode 100644 cmake/modules/FindEtc2Comp.cmake diff --git a/android/build.gradle b/android/build.gradle index 1dfef97f91..e041142282 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -14,7 +14,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.1.0' } } @@ -66,19 +66,19 @@ ext { def baseFolder = new File(HIFI_ANDROID_PRECOMPILED) def appDir = new File(projectDir, 'app') def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a') -def baseUrl = 'https://hifi-public.s3.amazonaws.com/austin/android/' +def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/' def qtFile='qt-5.9.3_linux_armv8-libcpp_openssl.tgz' def qtChecksum='04599670ccca84bd2b15f6915568eb2d' -def qtVersionId='PeoqzN31n.YvLfs9JE2SgHgZ4.IaKAlt' +def qtVersionId='8QbCma4ryEPgBYn_8kgYgB10IvNx9I1W' if (Os.isFamily(Os.FAMILY_MAC)) { qtFile = 'qt-5.9.3_osx_armv8-libcpp_openssl.tgz' qtChecksum='4b02de9d67d6bfb202355a808d2d9c59' - qtVersionId='HygCmtMLPYioyil0DfXckGVzhw2SXZA9' + qtVersionId='2gfgoYCggJGyXxKiazaPGsMs1Gn9j4og' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { qtFile = 'qt-5.9.3_win_armv8-libcpp_openssl.tgz' qtChecksum='c3e25db64002d0f43cf565e0ef708911' - qtVersionId='HeVObSVLCBoc7yY7He1oBMvPIH0VkClT' + qtVersionId='xKIteC6HO0xrmcWeMmhQcmKyPEsnUrcZ' } def packages = [ @@ -89,61 +89,66 @@ def packages = [ ], bullet: [ file: 'bullet-2.83_armv8-libcpp.tgz', - versionId: 'ljb7v.1IjVRqyopUKVDbVnLA4z88J8Eo', + versionId: 'L6MqTwt24yZAx0cHgLyN.GPky1kVGsq3', checksum: '2c558d604fce337f5eba3eb7ec1252fd', ], draco: [ file: 'draco_armv8-libcpp.tgz', - versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m', + versionId: '3.B.uBj31kWlgND3_R2xwQzT_TP6Dz_8', checksum: '617a80d213a5ec69fbfa21a1f2f738cd', ], glad: [ file: 'glad_armv8-libcpp.zip', - versionId: 'Q9szthzeye8fFyAA.cY26Lgn2B8kezEE', + versionId: 'r5Zran.JSCtvrrB6Q4KaqfIoALPw3lYY', checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449', ], glm: [ file: 'glm-0.9.8.tgz', - versionId: 'BlkJNwaYV2Gfy5XwMeU7K0uzPDRKFMt2', + versionId: 'CHPAk.Un_8MC4v8cIqkEJ468Y4_RQDmd', checksum: 'd2b42cee31d2bc17bab6ce69e6b3f30a', ], gvr: [ file: 'gvrsdk_v1.101.0.tgz', - versionId: 'UTberAIFraEfF9IVjoV66u1DTPTopgeY', + versionId: 'nqBV_j81Uc31rC7bKIrlya_Hah4v3y5r', checksum: '57fd02baa069176ba18597a29b6b4fc7', ], nvtt: [ file: 'nvtt_armv8-libcpp.zip', - versionId: 'vLqrqThvpq4gp75BHMAqO6HhfTXaa0An', + versionId: 'lmkBVR5t4UF1UUwMwEirnk9H_8Nt90IO', checksum: 'eb46d0b683e66987190ed124aabf8910', sharedLibFolder: 'lib', includeLibs: ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'], ], openssl: [ file: 'openssl-1.1.0g_armv8.tgz', - versionId: 'DmahmSGFS4ltpHyTdyQvv35WOeUOiib9', + versionId: 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW', checksum: 'cabb681fbccd79594f65fcc266e02f32', ], polyvox: [ file: 'polyvox_armv8-libcpp.tgz', - versionId: 'LDJtzMTvdm4SAc2KYg8Cg6uwWk4Vq3e3', - checksum: '349ad5b72aaf2749ca95d847e60c5314', + versionId: 'UmHp.EOFiepdXnv2YxwNXNO3J6nVsBkE', + checksum: '5c918288741ee754c16aeb12bb46b9e1', sharedLibFolder: 'lib', includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'], ], tbb: [ file: 'tbb-2018_U1_armv8_libcpp.tgz', - versionId: 'YZliDD8.Menh1IVXKEuLPeO3xAjJ1UdF', + versionId: 'mrRbWnv4O4evcM1quRH43RJqimlRtaKB', checksum: '20768f298f53b195e71b414b0ae240c4', sharedLibFolder: 'lib/release', includeLibs: ['libtbb.so', 'libtbbmalloc.so'], ], hifiAC: [ file: 'libplugins_libhifiCodec.zip', - versionId: 'mzKhsRCgVmloqq5bvE.0IwYK1NjGQc_G', + versionId: 'i31pW.qNbvFOXRxbyiJUxg3sphaFNmZU', checksum: '9412a8e12c88a4096c1fc843bb9fe52d', sharedLibFolder: '', includeLibs: ['libplugins_libhifiCodec.so'] + ], + etc2comp: [ + file: 'etc2comp-armv8-libcpp.tgz', + versionId: '1WfqJYWLOP0g22eikYjBzn62gza7MKE6', + checksum: '0b4c2e6de728546845f0e471dfdeb604' ] ] @@ -152,15 +157,15 @@ def scribeLocalFile='scribe' + EXEC_SUFFIX def scribeFile='scribe_linux_x86_64' def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6' -def scribeVersion='wgpf4dB2Ltzg4Lb2jJ4nPFsHoDkmK_OO' +def scribeVersion='u_iTrJDaE95i2abTPXOpPZckGBIim53G' if (Os.isFamily(Os.FAMILY_MAC)) { scribeFile = 'scribe_osx_x86_64' scribeChecksum='72db9d32d4e1e50add755570ac5eb749' - scribeVersion='o_NbPrktzEYtBkQf3Tn7zc1nZWzM52w6' + scribeVersion='DAW0DmnjCRib4MD8x93bgc2Z2MpPojZC' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { scribeFile = 'scribe_win32_x86_64.exe' scribeChecksum='678e43d290c90fda670c6fefe038a06d' - scribeVersion='GCCJxlmd2irvNOFWfZR0U1UCLHndHQrC' + scribeVersion='PuullrA_bPlO9kXZRt8rLe536X1UI.m7' } def options = [ @@ -361,6 +366,7 @@ task verifyOpenSSL(type: Verify) { def p = packages['openssl']; src new File(bas task verifyPolyvox(type: Verify) { def p = packages['polyvox']; src new File(baseFolder, p['file']); checksum p['checksum'] } task verifyTBB(type: Verify) { def p = packages['tbb']; src new File(baseFolder, p['file']); checksum p['checksum'] } task verifyHifiAC(type: Verify) { def p = packages['hifiAC']; src new File(baseFolder, p['file']); checksum p['checksum'] } +task verifyEtc2Comp(type: Verify) { def p = packages['etc2comp']; src new File(baseFolder, p['file']); checksum p['checksum'] } task verifyDependencyDownloads(dependsOn: downloadDependencies) { } verifyDependencyDownloads.dependsOn verifyQt @@ -371,6 +377,7 @@ verifyDependencyDownloads.dependsOn verifyOpenSSL verifyDependencyDownloads.dependsOn verifyPolyvox verifyDependencyDownloads.dependsOn verifyTBB verifyDependencyDownloads.dependsOn verifyHifiAC +verifyDependencyDownloads.dependsOn verifyEtc2Comp task extractDependencies(dependsOn: verifyDependencyDownloads) { doLast { diff --git a/cmake/externals/etc2comp/CMakeLists.txt b/cmake/externals/etc2comp/CMakeLists.txt new file mode 100644 index 0000000000..9cee0652bc --- /dev/null +++ b/cmake/externals/etc2comp/CMakeLists.txt @@ -0,0 +1,44 @@ +set(EXTERNAL_NAME etc2comp) + +if (ANDROID) + set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") +endif () + +include(ExternalProject) +ExternalProject_Add( + ${EXTERNAL_NAME} + URL https://hifi-public.s3.amazonaws.com/dependencies/etc2comp-master.zip + URL_MD5 d0969e14af6b10b3306f880c8e2b1184 + CMAKE_ARGS ${ANDROID_CMAKE_ARGS} + BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 +) + +# Hide this external target (for ide users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + +ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + +if (WIN32) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/build/EtcLib/Debug/EtcLib.lib CACHE FILEPATH "Path to Etc2Comp debug library") + + # use generator expression to ensure the correct library is found when building different configurations in VS + set(_LIB_FOLDER "$<$:build/EtcLib/RelWithDebInfo>") + set(_LIB_FOLDER "${_LIB_FOLDER}$<$:build/EtcLib/MinSizeRel>") + set(_LIB_FOLDER "${_LIB_FOLDER}$<$,$>:build/EtcLib/Release>") + + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/${_LIB_FOLDER}/EtcLib.lib CACHE FILEPATH "Path to Etc2Comp release library") +else () + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to EtcLib debug library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/build/EtcLib/libEtcLib.a CACHE FILEPATH "Path to EtcLib release library") +endif () + +set(ETC_INCLUDE_DIR ${SOURCE_DIR}/EtcLib/Etc CACHE FILEPATH "Path to Etc2Comp/Etc include directory") +set(ETCCODEC_INCLUDE_DIR ${SOURCE_DIR}/EtcLib/EtcCodec CACHE FILEPATH "Path to Etc2Comp/EtcCodec include directory") +# ETC2COMP_INCLUDE_DIRS will be set later by FindEtc2Comp \ No newline at end of file diff --git a/cmake/macros/TargetEtc2Comp.cmake b/cmake/macros/TargetEtc2Comp.cmake new file mode 100644 index 0000000000..8dfae175a9 --- /dev/null +++ b/cmake/macros/TargetEtc2Comp.cmake @@ -0,0 +1,22 @@ +# +# Copyright 2018 High Fidelity, Inc. +# Created by Sam Gondelman on 5/2/2018 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_ETC2COMP) + if (ANDROID) + set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/etc2comp) + set(ETC2COMP_INCLUDE_DIRS "${INSTALL_DIR}/include/Etc" "${INSTALL_DIR}/include/EtcCodec") + set(ETC2COMP_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libEtcLib.a) + set(ETC2COMP_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libEtcLib.a) + select_library_configurations(ETC2COMP) + else() + add_dependency_external_projects(etc2comp) + find_package(ETC2COMP REQUIRED) + endif() + + target_include_directories(${TARGET_NAME} PRIVATE ${ETC2COMP_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${ETC2COMP_LIBRARIES}) +endmacro() diff --git a/cmake/modules/FindEtc2Comp.cmake b/cmake/modules/FindEtc2Comp.cmake new file mode 100644 index 0000000000..1b990368fd --- /dev/null +++ b/cmake/modules/FindEtc2Comp.cmake @@ -0,0 +1,37 @@ +# +# FindEtc2Comp.cmake +# +# Try to find the Etc2Comp compression library. +# +# Once done this will define +# +# ETC2COMP_FOUND - system found Etc2Comp +# ETC2COMP_INCLUDE_DIRS - the Etc2Comp include directory +# ETC2COMP_LIBRARIES - link to this to use Etc2Comp +# +# Created on 5/2/2018 by Sam Gondelman +# Copyright 2018 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# + +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("etc2comp") + +find_path(ETC_INCLUDE_DIR NAMES Etc.h HINTS ${ETC2COMP_SEARCH_DIRS}) +find_path(ETCCODEC_INCLUDE_DIR NAMES EtcBlock4x4.h HINTS ${ETC2COMP_SEARCH_DIRS}) +set(ETC2COMP_INCLUDE_DIRS "${ETC_INCLUDE_DIR}" "${ETCCODEC_INCLUDE_DIR}") + +find_library(ETC2COMP_LIBRARY_DEBUG NAMES ETC2COMP ETC2COMP_LIB PATH_SUFFIXES EtcLib/Debug HINTS ${ETC2COMP_SEARCH_DIRS}) +find_library(ETC2COMP_LIBRARY_RELEASE NAMES ETC2COMP ETC2COMP_LIB PATH_SUFFIXES EtcLib/Release EtcLib HINTS ${ETC2COMP_SEARCH_DIRS}) + +include(SelectLibraryConfigurations) +select_library_configurations(ETC2COMP) + +set(ETC2COMP_LIBRARIES ${ETC2COMP_LIBRARY}) + +find_package_handle_standard_args(ETC2COMP "Could NOT find ETC2COMP, try to set the path to ETC2COMP root folder in the system variable ETC2COMP_ROOT_DIR or create a directory etc2comp in HIFI_LIB_DIR and paste the necessary files there" + ETC2COMP_INCLUDE_DIRS ETC2COMP_LIBRARIES) + +mark_as_advanced(ETC2COMP_INCLUDE_DIRS ETC2COMP_LIBRARIES ETC2COMP_SEARCH_DIRS) diff --git a/libraries/image/CMakeLists.txt b/libraries/image/CMakeLists.txt index 8073cc2b5e..6bc5c762f5 100644 --- a/libraries/image/CMakeLists.txt +++ b/libraries/image/CMakeLists.txt @@ -2,3 +2,4 @@ set(TARGET_NAME image) setup_hifi_library() link_hifi_libraries(shared gpu) target_nvtt() +target_etc2comp() diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index c1c07838c7..1639dde246 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -31,8 +31,14 @@ using namespace gpu; #define CPU_MIPMAPS 1 #include +#include + static const glm::uvec2 SPARSE_PAGE_SIZE(128); +#ifdef Q_OS_ANDROID +static const glm::uvec2 MAX_TEXTURE_SIZE(1024); +#else static const glm::uvec2 MAX_TEXTURE_SIZE(4096); +#endif bool DEV_DECIMATE_TEXTURES = false; std::atomic DECIMATED_TEXTURE_COUNT{ 0 }; std::atomic RECTIFIED_TEXTURE_COUNT{ 0 }; From 7418fe782cabf2dccc3ea57ba1f79ead926021a1 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 9 May 2018 12:22:30 -0700 Subject: [PATCH 02/38] local compression on android --- libraries/image/src/image/Image.cpp | 121 +++++++++++++++++++++------- 1 file changed, 94 insertions(+), 27 deletions(-) diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 1639dde246..55cafe9571 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -31,7 +31,10 @@ using namespace gpu; #define CPU_MIPMAPS 1 #include +#ifdef Q_OS_ANDROID #include +#include +#endif static const glm::uvec2 SPARSE_PAGE_SIZE(128); #ifdef Q_OS_ANDROID @@ -43,6 +46,7 @@ bool DEV_DECIMATE_TEXTURES = false; std::atomic DECIMATED_TEXTURE_COUNT{ 0 }; std::atomic RECTIFIED_TEXTURE_COUNT{ 0 }; +// TODO: pick compressed android hdr format static const auto HDR_FORMAT = gpu::Element::COLOR_R11G11B10; static std::atomic compressColorTextures { false }; @@ -560,7 +564,9 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic(localCopy.constBits()); + auto mipFormat = texture->getStoredMipFormat(); +#ifndef Q_OS_ANDROID nvtt::TextureType textureType = nvtt::TextureType_2D; nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB; nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror; @@ -590,8 +596,6 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomicgetStoredMipFormat(); if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGB) { compressionOptions.setFormat(nvtt::Format_BC1); } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_MASK) { @@ -675,6 +679,78 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic floatData; + floatData.resize(width * height); + for (int y = 0; y < height; y++) { + QRgb *line = (QRgb *) localCopy.scanLine(y); + for (int x = 0; x < width; x++) { + QRgb &pixel = line[x]; + floatData[x + y * width] = vec4(qRed(pixel), qGreen(pixel), qBlue(pixel), qAlpha(pixel)) / 255.0f; + } + } + + Etc::EncodeMipmaps( + (float *)floatData.data(), width, height, + etcFormat, errorMetric, effort, + numEncodeThreads, numEncodeThreads, + numMips, Etc::FILTER_WRAP_X | Etc::FILTER_WRAP_Y, + mipMaps, &encodingTime + ); + + // free up the memory afterward to avoid bloating the heap + data = nullptr; + localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one. + + for (int i = 0; i < numMips; i++) { + if (face >= 0) { + texture->assignStoredMipFace(i, face, mipMaps[i].uiEncodingBitsBytes, static_cast(mipMaps[i].paucEncodingBits.get())); + } else { + texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast(mipMaps[i].paucEncodingBits.get())); + } + } + + delete[] mipMaps; +#endif } #endif @@ -744,7 +820,6 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma gpu::Element formatMip; gpu::Element formatGPU; if (isColorTexturesCompressionEnabled()) { -#ifndef USE_GLES if (validAlpha) { // NOTE: This disables BC1a compression because it was producing odd artifacts on text textures // for the tutorial. Instead we use BC3 (which is larger) but doesn't produce the same artifacts). @@ -752,23 +827,15 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma } else { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGB; } -#else - if (validAlpha) { - formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA; - } else { - formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB; - } -#endif - formatMip = formatGPU; } else { -#ifdef USE_GLES +#ifdef Q_OS_ANDROID // GLES does not support GL_BGRA - formatMip = gpu::Element::COLOR_SRGBA_32; + formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA; #else - formatMip = gpu::Element::COLOR_SBGRA_32; + formatGPU = gpu::Element::COLOR_SBGRA_32; #endif - formatGPU = gpu::Element::COLOR_SRGBA_32; } + formatMip = formatGPU; if (isStrict) { theTexture = gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); @@ -882,16 +949,18 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr gpu::TexturePointer theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - gpu::Element formatMip = gpu::Element::VEC2NU8_XY; - gpu::Element formatGPU = gpu::Element::VEC2NU8_XY; + gpu::Element formatMip; + gpu::Element formatGPU; if (isNormalTexturesCompressionEnabled()) { -#ifndef USE_GLES formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY; -#else + } else { +#ifdef Q_OS_ANDROID formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY; +#else + formatGPU = gpu::Element::VEC2NU8_XY; #endif - formatMip = formatGPU; } + formatMip = formatGPU; theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); @@ -923,16 +992,15 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr gpu::Element formatMip; gpu::Element formatGPU; if (isGrayscaleTexturesCompressionEnabled()) { -#ifndef USE_GLES formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED; -#else - formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED; -#endif - formatMip = formatGPU; } else { - formatMip = gpu::Element::COLOR_R_8; +#ifdef Q_OS_ANDROID + formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED; +#else formatGPU = gpu::Element::COLOR_R_8; +#endif } + formatMip = formatGPU; theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); @@ -1295,7 +1363,6 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI gpu::Element formatMip; gpu::Element formatGPU; if (isCubeTexturesCompressionEnabled()) { - // TODO: gles: pick HDR ETC format formatMip = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB; formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB; } else { From 6b268191c82bcdcfa1f17b589ba8143b2cacc970 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 14 May 2018 15:29:36 -0700 Subject: [PATCH 03/38] local compression, patched etc2comp --- android/build.gradle | 6 +-- cmake/externals/etc2comp/CMakeLists.txt | 6 ++- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- libraries/image/src/image/Image.cpp | 71 ++++++++++++------------- 4 files changed, 42 insertions(+), 43 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index e041142282..71d48e28d4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -146,9 +146,9 @@ def packages = [ includeLibs: ['libplugins_libhifiCodec.so'] ], etc2comp: [ - file: 'etc2comp-armv8-libcpp.tgz', - versionId: '1WfqJYWLOP0g22eikYjBzn62gza7MKE6', - checksum: '0b4c2e6de728546845f0e471dfdeb604' + file: 'etc2comp-patched-armv8-libcpp.tgz', + versionId: 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU', + checksum: '14b02795d774457a33bbc60e00a786bc' ] ] diff --git a/cmake/externals/etc2comp/CMakeLists.txt b/cmake/externals/etc2comp/CMakeLists.txt index 9cee0652bc..1de4932380 100644 --- a/cmake/externals/etc2comp/CMakeLists.txt +++ b/cmake/externals/etc2comp/CMakeLists.txt @@ -5,10 +5,12 @@ if (ANDROID) endif () include(ExternalProject) +# We use a patched version of etc2comp that properly generates all the necessary mips +# See https://github.com/google/etc2comp/pull/29 ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/dependencies/etc2comp-master.zip - URL_MD5 d0969e14af6b10b3306f880c8e2b1184 + URL https://hifi-public.s3.amazonaws.com/dependencies/etc2comp-patched.zip + URL_MD5 6fe3629de8ff99bc99f8c14558d841a3 CMAKE_ARGS ${ANDROID_CMAKE_ARGS} BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build INSTALL_COMMAND "" diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 129c125411..839cb915e2 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -352,7 +352,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { if (!Texture::evalKTXFormat(mipFormat, texelFormat, header)) { return nullptr; } - + // Set Dimensions uint32_t numFaces = 1; switch (texture.getType()) { diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 55cafe9571..2fc22b4eac 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -31,14 +31,14 @@ using namespace gpu; #define CPU_MIPMAPS 1 #include -#ifdef Q_OS_ANDROID +#ifdef USE_GLES #include #include #endif static const glm::uvec2 SPARSE_PAGE_SIZE(128); #ifdef Q_OS_ANDROID -static const glm::uvec2 MAX_TEXTURE_SIZE(1024); +static const glm::uvec2 MAX_TEXTURE_SIZE(2048); #else static const glm::uvec2 MAX_TEXTURE_SIZE(4096); #endif @@ -46,7 +46,6 @@ bool DEV_DECIMATE_TEXTURES = false; std::atomic DECIMATED_TEXTURE_COUNT{ 0 }; std::atomic RECTIFIED_TEXTURE_COUNT{ 0 }; -// TODO: pick compressed android hdr format static const auto HDR_FORMAT = gpu::Element::COLOR_R11G11B10; static std::atomic compressColorTextures { false }; @@ -563,10 +562,10 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic(localCopy.constBits()); auto mipFormat = texture->getStoredMipFormat(); -#ifndef Q_OS_ANDROID +#ifndef USE_GLES + const void* data = static_cast(localCopy.constBits()); nvtt::TextureType textureType = nvtt::TextureType_2D; nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB; nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror; @@ -681,43 +680,41 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic floatData; floatData.resize(width * height); @@ -725,27 +722,28 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic= 0) { - texture->assignStoredMipFace(i, face, mipMaps[i].uiEncodingBitsBytes, static_cast(mipMaps[i].paucEncodingBits.get())); - } else { - texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast(mipMaps[i].paucEncodingBits.get())); + if (mipMaps[i].paucEncodingBits.get()) { + if (face >= 0) { + texture->assignStoredMipFace(i, face, mipMaps[i].uiEncodingBitsBytes, static_cast(mipMaps[i].paucEncodingBits.get())); + } else { + texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast(mipMaps[i].paucEncodingBits.get())); + } } } @@ -755,8 +753,6 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic& abortProcessing = false, int face = -1) { #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateMips"); @@ -828,14 +824,15 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGB; } } else { -#ifdef Q_OS_ANDROID +#ifdef USE_GLES // GLES does not support GL_BGRA formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA; + formatMip = formatGPU; #else - formatGPU = gpu::Element::COLOR_SBGRA_32; + formatGPU = gpu::Element::COLOR_SRGBA_32; + formatMip = gpu::Element::COLOR_SBGRA_32; #endif } - formatMip = formatGPU; if (isStrict) { theTexture = gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); @@ -954,7 +951,7 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr if (isNormalTexturesCompressionEnabled()) { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY; } else { -#ifdef Q_OS_ANDROID +#ifdef USE_GLES formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY; #else formatGPU = gpu::Element::VEC2NU8_XY; @@ -994,7 +991,7 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr if (isGrayscaleTexturesCompressionEnabled()) { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED; } else { -#ifdef Q_OS_ANDROID +#ifdef USE_GLES formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED; #else formatGPU = gpu::Element::COLOR_R_8; From 12b91a2ab720e8eb5c8c73822b01220003996e96 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 16 May 2018 18:06:12 -0700 Subject: [PATCH 04/38] compress skyboxes --- android/build.gradle | 6 ++--- cmake/macros/TargetEtc2Comp.cmake | 2 +- libraries/gpu/src/gpu/Texture.cpp | 14 +++++++++++- libraries/image/src/image/Image.cpp | 34 ++++++++++++++++++++++++----- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 71d48e28d4..c771c776b5 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -14,7 +14,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.1.0' + classpath 'com.android.tools.build:gradle:3.0.1' } } @@ -126,8 +126,8 @@ def packages = [ ], polyvox: [ file: 'polyvox_armv8-libcpp.tgz', - versionId: 'UmHp.EOFiepdXnv2YxwNXNO3J6nVsBkE', - checksum: '5c918288741ee754c16aeb12bb46b9e1', + versionId: 'A2kbKiNhpIenGq23bKRRzg7IMAI5BI92', + checksum: 'dba88b3a098747af4bb169e9eb9af57e', sharedLibFolder: 'lib', includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'], ], diff --git a/cmake/macros/TargetEtc2Comp.cmake b/cmake/macros/TargetEtc2Comp.cmake index 8dfae175a9..44152a58d2 100644 --- a/cmake/macros/TargetEtc2Comp.cmake +++ b/cmake/macros/TargetEtc2Comp.cmake @@ -14,7 +14,7 @@ macro(TARGET_ETC2COMP) select_library_configurations(ETC2COMP) else() add_dependency_external_projects(etc2comp) - find_package(ETC2COMP REQUIRED) + find_package(Etc2Comp REQUIRED) endif() target_include_directories(${TARGET_NAME} PRIVATE ${ETC2COMP_INCLUDE_DIRS}) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index ed9505766b..a92243f808 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -684,9 +684,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< PROFILE_RANGE(render_gpu, "sphericalHarmonicsFromTexture"); +#ifndef USE_GLES auto mipFormat = cubeTexture.getStoredMipFormat(); std::function unpackFunc; - switch (mipFormat.getSemantic()) { case gpu::R11G11B10: unpackFunc = glm::unpackF2x11_1x10; @@ -698,6 +698,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< assert(false); break; } +#endif const uint sqOrder = order*order; @@ -732,7 +733,11 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) { PROFILE_RANGE(render_gpu, "ProcessFace"); +#ifndef USE_GLES auto data = reinterpret_cast( cubeTexture.accessStoredMipFace(0, face)->readData() ); +#else + auto data = cubeTexture.accessStoredMipFace(0, face)->readData(); +#endif if (data == nullptr) { continue; } @@ -816,8 +821,15 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< glm::vec3 color{ 0.0f, 0.0f, 0.0f }; for (int i = 0; i < stride; ++i) { for (int j = 0; j < stride; ++j) { +#ifndef USE_GLES int k = (int)(x + i - halfStride + (y + j - halfStride) * width); color += unpackFunc(data[k]); +#else + const int NUM_COMPONENTS_PER_PIXEL = 4; + int k = NUM_COMPONENTS_PER_PIXEL * (int)(x + i - halfStride + (y + j - halfStride) * width); + // BGRA -> RGBA + color += glm::pow(glm::vec3(data[k + 2], data[k + 1], data[k]) / 255.0f, glm::vec3(2.2f)); +#endif } } diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 2fc22b4eac..63a4725f64 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -84,7 +84,13 @@ const QStringList getSupportedFormats() { return stringFormats; } + +// On GLES, we don't use HDR skyboxes +#ifndef USE_GLES QImage::Format QIMAGE_HDR_FORMAT = QImage::Format_RGB30; +#else +QImage::Format QIMAGE_HDR_FORMAT = QImage::Format_RGB32; +#endif TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) { switch (type) { @@ -557,7 +563,7 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateMips"); +#ifndef USE_GLES if (image.format() == QIMAGE_HDR_FORMAT) { generateHDRMips(texture, std::move(image), abortProcessing, face); } else { generateLDRMips(texture, std::move(image), abortProcessing, face); } +#else + generateLDRMips(texture, std::move(image), abortProcessing, face); +#endif #else texture->setAutoGenerateMips(true); #endif @@ -1354,18 +1364,25 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI QImage image = processSourceImage(std::move(localCopy), true); if (image.format() != QIMAGE_HDR_FORMAT) { +#ifndef USE_GLES image = convertToHDRFormat(std::move(image), HDR_FORMAT); +#else + image = image.convertToFormat(QImage::Format_RGB32); +#endif } gpu::Element formatMip; gpu::Element formatGPU; if (isCubeTexturesCompressionEnabled()) { - formatMip = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB; formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB; } else { - formatMip = HDR_FORMAT; +#ifdef USE_GLES + formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB; +#else formatGPU = HDR_FORMAT; +#endif } + formatMip = formatGPU; // Find the layout of the cubemap in the 2D image // Use the original image size since processSourceImage may have altered the size / aspect ratio @@ -1412,9 +1429,16 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI // Generate irradiance while we are at it if (generateIrradiance) { PROFILE_RANGE(resource_parse, "generateIrradiance"); - auto irradianceTexture = gpu::Texture::createCube(HDR_FORMAT, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)); + gpu::Element irradianceFormat; + // TODO: we could locally compress the irradiance texture on Android, but we don't need to +#ifndef USE_GLES + irradianceFormat = HDR_FORMAT; +#else + irradianceFormat = gpu::Element::COLOR_SRGBA_32; +#endif + auto irradianceTexture = gpu::Texture::createCube(irradianceFormat, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)); irradianceTexture->setSource(srcImageName); - irradianceTexture->setStoredMipFormat(HDR_FORMAT); + irradianceTexture->setStoredMipFormat(irradianceFormat); for (uint8 face = 0; face < faces.size(); ++face) { irradianceTexture->assignStoredMipFace(0, face, faces[face].byteCount(), faces[face].constBits()); } From 282e61c085563fd58f3430a66bc7eb634e587f69 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 21 May 2018 13:43:24 -0700 Subject: [PATCH 05/38] patch etc2comp for mac --- cmake/externals/etc2comp/CMakeLists.txt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cmake/externals/etc2comp/CMakeLists.txt b/cmake/externals/etc2comp/CMakeLists.txt index 1de4932380..d6d21d6703 100644 --- a/cmake/externals/etc2comp/CMakeLists.txt +++ b/cmake/externals/etc2comp/CMakeLists.txt @@ -4,14 +4,20 @@ if (ANDROID) set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") endif () +if (APPLE) + set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++) +endif () + include(ExternalProject) # We use a patched version of etc2comp that properly generates all the necessary mips # See https://github.com/google/etc2comp/pull/29 +# We also use part of https://github.com/google/etc2comp/pull/1, which fixes a bug +# that would override CMAKE_CXX_FLAGS ExternalProject_Add( ${EXTERNAL_NAME} URL https://hifi-public.s3.amazonaws.com/dependencies/etc2comp-patched.zip - URL_MD5 6fe3629de8ff99bc99f8c14558d841a3 - CMAKE_ARGS ${ANDROID_CMAKE_ARGS} + URL_MD5 4c96153eb179acbe619e3d99d3330595 + CMAKE_ARGS ${ANDROID_CMAKE_ARGS} ${EXTRA_CMAKE_FLAGS} BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build INSTALL_COMMAND "" LOG_DOWNLOAD 1 @@ -36,6 +42,9 @@ if (WIN32) set(_LIB_FOLDER "${_LIB_FOLDER}$<$,$>:build/EtcLib/Release>") set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/${_LIB_FOLDER}/EtcLib.lib CACHE FILEPATH "Path to Etc2Comp release library") +elseif (APPLE) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/build/EtcLib/Debug/libEtcLib.a CACHE FILEPATH "Path to EtcLib debug library") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/build/EtcLib/Release/libEtcLib.a CACHE FILEPATH "Path to EtcLib release library") else () set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to EtcLib debug library") set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/build/EtcLib/libEtcLib.a CACHE FILEPATH "Path to EtcLib release library") From c2ceb48a28d5e3ac3ca0154ef4a61f6c8cea7733 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 23 May 2018 14:16:05 -0700 Subject: [PATCH 06/38] fixing key conflicts --- interface/src/Application.cpp | 18 +++++++++++++++--- interface/src/Menu.cpp | 6 +++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6a102f418b..5836e6f308 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3668,9 +3668,21 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; - case Qt::Key_1: - case Qt::Key_2: - case Qt::Key_3: + case Qt::Key_1: { + Menu* menu = Menu::getInstance(); + menu->triggerOption(MenuOption::FirstPerson); + break; + } + case Qt::Key_2: { + Menu* menu = Menu::getInstance(); + menu->triggerOption(MenuOption::FullscreenMirror); + break; + } + case Qt::Key_3: { + Menu* menu = Menu::getInstance(); + menu->triggerOption(MenuOption::ThirdPerson); + break; + } case Qt::Key_4: case Qt::Key_5: case Qt::Key_6: diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f55c389a1f..81c3963c33 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -201,21 +201,21 @@ Menu::Menu() { // View > First Person auto firstPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FirstPerson, Qt::Key_1, + viewMenu, MenuOption::FirstPerson, 0, true, qApp, SLOT(cameraMenuChanged()))); firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); // View > Third Person auto thirdPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::ThirdPerson, Qt::Key_3, + viewMenu, MenuOption::ThirdPerson, 0, false, qApp, SLOT(cameraMenuChanged()))); thirdPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); // View > Mirror auto viewMirrorAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FullscreenMirror, Qt::Key_2, + viewMenu, MenuOption::FullscreenMirror, 0, false, qApp, SLOT(cameraMenuChanged()))); viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); From e096ff91f6a7e66b08d594438868238b42a50dda Mon Sep 17 00:00:00 2001 From: Clement Date: Fri, 18 May 2018 15:01:19 -0700 Subject: [PATCH 07/38] Explicitely manage HTTPManager lifetime --- assignment-client/src/octree/OctreeServer.cpp | 3 +-- assignment-client/src/octree/OctreeServer.h | 2 +- domain-server/src/DomainServer.cpp | 3 +-- domain-server/src/DomainServer.h | 2 +- libraries/embedded-webserver/src/HTTPConnection.cpp | 1 - libraries/embedded-webserver/src/HTTPConnection.h | 3 --- libraries/embedded-webserver/src/HTTPManager.cpp | 3 +-- libraries/embedded-webserver/src/HTTPManager.h | 2 +- libraries/embedded-webserver/src/HTTPSConnection.h | 2 +- libraries/embedded-webserver/src/HTTPSManager.cpp | 4 ++-- libraries/embedded-webserver/src/HTTPSManager.h | 4 ++-- 11 files changed, 11 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index fad2c1f026..acf0abeed1 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -233,7 +233,6 @@ OctreeServer::OctreeServer(ReceivedMessage& message) : _argc(0), _argv(NULL), _parsedArgV(NULL), - _httpManager(NULL), _statusPort(0), _packetsPerClientPerInterval(10), _packetsTotalPerInterval(DEFAULT_PACKETS_PER_INTERVAL), @@ -285,7 +284,7 @@ void OctreeServer::initHTTPManager(int port) { QString documentRoot = QString("%1/web").arg(PathUtils::getAppDataPath()); // setup an httpManager with us as the request handler and the parent - _httpManager = new HTTPManager(QHostAddress::AnyIPv4, port, documentRoot, this, this); + _httpManager.reset(new HTTPManager(QHostAddress::AnyIPv4, port, documentRoot, this)); } bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index b25e537d70..87145dd46e 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -183,7 +183,7 @@ protected: bool _isShuttingDown = false; - HTTPManager* _httpManager; + std::unique_ptr _httpManager; int _statusPort; QString _statusHost; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 4e65df495c..991b8627d0 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -149,7 +149,6 @@ DomainServer::DomainServer(int argc, char* argv[]) : QCoreApplication(argc, argv), _gatekeeper(this), _httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this), - _httpsManager(NULL), _allAssignments(), _unfulfilledAssignments(), _isUsingDTLS(false), @@ -439,7 +438,7 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() { QSslCertificate sslCertificate(&certFile); QSslKey privateKey(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, keyPassphraseString.toUtf8()); - _httpsManager = new HTTPSManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTPS_PORT, sslCertificate, privateKey, QString(), this, this); + _httpsManager.reset(new HTTPSManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTPS_PORT, sslCertificate, privateKey, QString(), this)); qDebug() << "TCP server listening for HTTPS connections on" << DOMAIN_SERVER_HTTPS_PORT; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 01adbd99a9..3462f14e3c 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -220,7 +220,7 @@ private: DomainGatekeeper _gatekeeper; HTTPManager _httpManager; - HTTPSManager* _httpsManager; + std::unique_ptr _httpsManager; QHash _allAssignments; QQueue _unfulfilledAssignments; diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp index 12da599575..fa4fd39ca8 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPConnection.cpp @@ -33,7 +33,6 @@ HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager) QObject(parentManager), _parentManager(parentManager), _socket(socket), - _stream(socket), _address(socket->peerAddress()) { // take over ownership of the socket diff --git a/libraries/embedded-webserver/src/HTTPConnection.h b/libraries/embedded-webserver/src/HTTPConnection.h index e4d23e3c90..31ffbc9641 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.h +++ b/libraries/embedded-webserver/src/HTTPConnection.h @@ -112,9 +112,6 @@ protected: /// The underlying socket. QTcpSocket* _socket; - - /// The data stream for writing to the socket. - QDataStream _stream; /// The stored address. QHostAddress _address; diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index db6fc8e084..4a75994e12 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -24,8 +24,7 @@ const int SOCKET_ERROR_EXIT_CODE = 2; const int SOCKET_CHECK_INTERVAL_IN_MS = 30000; -HTTPManager::HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : - QTcpServer(parent), +HTTPManager::HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler) : _listenAddress(listenAddress), _documentRoot(documentRoot), _requestHandler(requestHandler), diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index cb76eed9f2..e0477bd797 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -33,7 +33,7 @@ class HTTPManager : public QTcpServer, public HTTPRequestHandler { Q_OBJECT public: /// Initializes the manager. - HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0); + HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL); bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override; diff --git a/libraries/embedded-webserver/src/HTTPSConnection.h b/libraries/embedded-webserver/src/HTTPSConnection.h index 7b53dc0063..3e6c752a08 100644 --- a/libraries/embedded-webserver/src/HTTPSConnection.h +++ b/libraries/embedded-webserver/src/HTTPSConnection.h @@ -23,4 +23,4 @@ protected slots: void handleSSLErrors(const QList& errors); }; -#endif // hifi_HTTPSConnection_h \ No newline at end of file +#endif // hifi_HTTPSConnection_h diff --git a/libraries/embedded-webserver/src/HTTPSManager.cpp b/libraries/embedded-webserver/src/HTTPSManager.cpp index 8ba44f98ac..95339a0dd6 100644 --- a/libraries/embedded-webserver/src/HTTPSManager.cpp +++ b/libraries/embedded-webserver/src/HTTPSManager.cpp @@ -16,8 +16,8 @@ #include "HTTPSConnection.h" HTTPSManager::HTTPSManager(QHostAddress listenAddress, quint16 port, const QSslCertificate& certificate, const QSslKey& privateKey, - const QString& documentRoot, HTTPSRequestHandler* requestHandler, QObject* parent) : - HTTPManager(listenAddress, port, documentRoot, requestHandler, parent), + const QString& documentRoot, HTTPSRequestHandler* requestHandler) : + HTTPManager(listenAddress, port, documentRoot, requestHandler), _certificate(certificate), _privateKey(privateKey), _sslRequestHandler(requestHandler) diff --git a/libraries/embedded-webserver/src/HTTPSManager.h b/libraries/embedded-webserver/src/HTTPSManager.h index 2d3cc9ed62..433dd35d7d 100644 --- a/libraries/embedded-webserver/src/HTTPSManager.h +++ b/libraries/embedded-webserver/src/HTTPSManager.h @@ -31,7 +31,7 @@ public: const QSslCertificate& certificate, const QSslKey& privateKey, const QString& documentRoot, - HTTPSRequestHandler* requestHandler = NULL, QObject* parent = 0); + HTTPSRequestHandler* requestHandler = NULL); void setCertificate(const QSslCertificate& certificate) { _certificate = certificate; } void setPrivateKey(const QSslKey& privateKey) { _privateKey = privateKey; } @@ -48,4 +48,4 @@ private: HTTPSRequestHandler* _sslRequestHandler; }; -#endif // hifi_HTTPSManager_h \ No newline at end of file +#endif // hifi_HTTPSManager_h From 19100c955020e5fc18ff7c860d1c36e70f05f0f6 Mon Sep 17 00:00:00 2001 From: Clement Date: Fri, 18 May 2018 15:02:57 -0700 Subject: [PATCH 08/38] Coding Standard Spacing Self-Sufficient Headers --- interface/src/SecondaryCamera.cpp | 8 ++++--- .../embedded-webserver/src/HTTPConnection.cpp | 2 +- .../embedded-webserver/src/HTTPConnection.h | 24 +++++++++---------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index db51cf99c8..b9a767f700 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -9,11 +9,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "Application.h" #include "SecondaryCamera.h" -#include -#include + #include +#include +#include + +#include "Application.h" using RenderArgsPointer = std::shared_ptr; diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp index fa4fd39ca8..12230e5e1d 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPConnection.cpp @@ -29,7 +29,7 @@ const char* HTTPConnection::StatusCode404 = "404 Not Found"; const char* HTTPConnection::StatusCode500 = "500 Internal server error"; const char* HTTPConnection::DefaultContentType = "text/plain; charset=ISO-8859-1"; -HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager) : +HTTPConnection::HTTPConnection(QTcpSocket* socket, HTTPManager* parentManager) : QObject(parentManager), _parentManager(parentManager), _socket(socket), diff --git a/libraries/embedded-webserver/src/HTTPConnection.h b/libraries/embedded-webserver/src/HTTPConnection.h index 31ffbc9641..44a523423a 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.h +++ b/libraries/embedded-webserver/src/HTTPConnection.h @@ -58,51 +58,51 @@ public: enum ReasonCode { NoReason = 0, NormalClosure = 1000, GoingAway = 1001 }; /// Initializes the connection. - HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager); + HTTPConnection(QTcpSocket* socket, HTTPManager* parentManager); /// Destroys the connection. - virtual ~HTTPConnection (); + virtual ~HTTPConnection(); /// Returns a pointer to the underlying socket, to which WebSocket message bodies should be written. - QTcpSocket* socket () const { return _socket; } + QTcpSocket* socket() const { return _socket; } /// Returns the request operation. - QNetworkAccessManager::Operation requestOperation () const { return _requestOperation; } + QNetworkAccessManager::Operation requestOperation() const { return _requestOperation; } /// Returns a reference to the request URL. - const QUrl& requestUrl () const { return _requestUrl; } + const QUrl& requestUrl() const { return _requestUrl; } /// 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; } + const QByteArray& requestContent() const { return _requestContent; } /// Parses the request content as form data, returning a list of header/content pairs. - QList parseFormData () const; + QList parseFormData() const; /// Parses the request content as a url encoded form, returning a hash of key/value pairs. /// Duplicate keys are not supported. QHash parseUrlEncodedForm(); /// Sends a response and closes the connection. - void respond (const char* code, const QByteArray& content = QByteArray(), + void respond(const char* code, const QByteArray& content = QByteArray(), const char* contentType = DefaultContentType, const Headers& headers = Headers()); - void respond (const char* code, std::unique_ptr device, + void respond(const char* code, std::unique_ptr device, const char* contentType = DefaultContentType, const Headers& headers = Headers()); protected slots: /// Reads the request line. - void readRequest (); + void readRequest(); /// Reads the headers. - void readHeaders (); + void readHeaders(); /// Reads the content. - void readContent (); + void readContent(); protected: void respondWithStatusAndHeaders(const char* code, const char* contentType, const Headers& headers, qint64 size); From 5f41505846a79ac26040a3faa0b29351491f4255 Mon Sep 17 00:00:00 2001 From: Clement Date: Fri, 18 May 2018 19:34:08 -0700 Subject: [PATCH 09/38] Store big web requests on disk --- domain-server/src/DomainGatekeeper.cpp | 2 + domain-server/src/DomainServer.cpp | 2 + ice-server/src/IceServer.cpp | 1 + .../embedded-webserver/src/HTTPConnection.cpp | 127 ++++++++++++++++-- .../embedded-webserver/src/HTTPConnection.h | 19 ++- 5 files changed, 135 insertions(+), 16 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 47b55bb5c2..b4685b907f 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -16,6 +16,8 @@ #include #include +#include + #include #include diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 991b8627d0..42a74aa2fc 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -384,6 +384,8 @@ DomainServer::~DomainServer() { _contentManager->terminate(); } + DependencyManager::destroy(); + // cleanup the AssetClient thread DependencyManager::destroy(); _assetClientThread.quit(); diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index 3cf1e1450e..6896c7a9c9 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -13,6 +13,7 @@ #include +#include #include #include #include diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp index 12230e5e1d..f65cd87f6e 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPConnection.cpp @@ -11,6 +11,8 @@ #include "HTTPConnection.h" +#include + #include #include #include @@ -29,6 +31,88 @@ const char* HTTPConnection::StatusCode404 = "404 Not Found"; const char* HTTPConnection::StatusCode500 = "500 Internal server error"; const char* HTTPConnection::DefaultContentType = "text/plain; charset=ISO-8859-1"; + +class MemoryStorage : public HTTPConnection::Storage { +public: + static std::unique_ptr make(qint64 size); + virtual ~MemoryStorage() = default; + + const QByteArray& content() const override { return _array; } + qint64 bytesLeftToWrite() const override { return _array.size() - _bytesWritten; } + void write(const QByteArray& data) override; + +private: + MemoryStorage(qint64 size) { _array.resize(size); } + + QByteArray _array; + qint64 _bytesWritten { 0 }; +}; + +std::unique_ptr MemoryStorage::make(qint64 size) { + return std::unique_ptr(new MemoryStorage(size)); +} + +void MemoryStorage::write(const QByteArray& data) { + assert(data.size() <= bytesLeftToWrite()); + memcpy(_array.data() + _bytesWritten, data.data(), data.size()); + _bytesWritten += data.size(); +} + + +class FileStorage : public HTTPConnection::Storage { +public: + static std::unique_ptr make(qint64 size); + virtual ~FileStorage(); + + const QByteArray& content() const override { return _wrapperArray; }; + qint64 bytesLeftToWrite() const override { return _mappedMemorySize - _bytesWritten; } + void write(const QByteArray& data) override; + +private: + FileStorage(std::unique_ptr file, uchar* mapped, qint64 size); + + // Byte array is const because any edit will trigger a deep copy + // and pull all the data we want to keep on disk in memory. + const QByteArray _wrapperArray; + std::unique_ptr _file; + + uchar* const _mappedMemoryAddress { nullptr }; + const qint64 _mappedMemorySize { 0 }; + qint64 _bytesWritten { 0 }; +}; + +std::unique_ptr FileStorage::make(qint64 size) { + auto file = std::unique_ptr(new QTemporaryFile()); + file->open(); // Open for resize + file->resize(size); + auto mapped = file->map(0, size); // map the entire file + + return std::unique_ptr(new FileStorage(std::move(file), mapped, size)); +} + +// Use QByteArray::fromRawData to avoid a new allocation and access the already existing +// memory directly as long as all operations on the array are const. +FileStorage::FileStorage(std::unique_ptr file, uchar* mapped, qint64 size) : + _wrapperArray(QByteArray::fromRawData(reinterpret_cast(mapped), size)), + _file(std::move(file)), + _mappedMemoryAddress(mapped), + _mappedMemorySize(size) +{ +} + +FileStorage::~FileStorage() { + _file->unmap(_mappedMemoryAddress); + _file->close(); +} + +void FileStorage::write(const QByteArray& data) { + assert(data.size() <= bytesLeftToWrite()); + // We write directly to the mapped memory + memcpy(_mappedMemoryAddress + _bytesWritten, data.data(), data.size()); + _bytesWritten += data.size(); +} + + HTTPConnection::HTTPConnection(QTcpSocket* socket, HTTPManager* parentManager) : QObject(parentManager), _parentManager(parentManager), @@ -61,7 +145,7 @@ QHash HTTPConnection::parseUrlEncodedForm() { return QHash(); } - QUrlQuery form { _requestContent }; + QUrlQuery form { _requestContent->content() }; QHash pairs; for (auto pair : form.queryItems()) { auto key = QUrl::fromPercentEncoding(pair.first.toLatin1().replace('+', ' ')); @@ -96,7 +180,7 @@ QList HTTPConnection::parseFormData() const { QByteArray end = "\r\n--" + boundary + "--\r\n"; QList data; - QBuffer buffer(const_cast(&_requestContent)); + QBuffer buffer(const_cast(&_requestContent->content())); buffer.open(QIODevice::ReadOnly); while (buffer.canReadLine()) { QByteArray line = buffer.readLine().trimmed(); @@ -106,12 +190,13 @@ QList HTTPConnection::parseFormData() const { QByteArray line = buffer.readLine().trimmed(); if (line.isEmpty()) { // content starts after this line - int idx = _requestContent.indexOf(end, buffer.pos()); + int idx = _requestContent->content().indexOf(end, buffer.pos()); if (idx == -1) { qWarning() << "Missing end boundary." << _address; return data; } - datum.second = _requestContent.mid(buffer.pos(), idx - buffer.pos()); + datum.second = QByteArray::fromRawData(_requestContent->content().data() + buffer.pos(), + idx - buffer.pos()); data.append(datum); buffer.seek(idx + end.length()); @@ -255,7 +340,24 @@ void HTTPConnection::readHeaders() { _parentManager->handleHTTPRequest(this, _requestUrl); } else { - _requestContent.resize(clength.toInt()); + bool success = false; + auto length = clength.toInt(&success); + if (!success) { + qWarning() << "Invalid header." << _address << trimmed; + respond("400 Bad Request", "The header was malformed."); + return; + } + + // Storing big requests in memory gets expensive, especially on servers + // with limited memory. So we store big requests in a temporary file on disk + // and map it to faster read/write access. + static const int MAX_CONTENT_SIZE_IN_MEMORY = 10 * 1000 * 1000; + if (length < MAX_CONTENT_SIZE_IN_MEMORY) { + _requestContent = MemoryStorage::make(length); + } else { + _requestContent = FileStorage::make(length); + } + connect(_socket, SIGNAL(readyRead()), SLOT(readContent())); // read any content immediately available @@ -284,12 +386,13 @@ void HTTPConnection::readHeaders() { } void HTTPConnection::readContent() { - int size = _requestContent.size(); - if (_socket->bytesAvailable() < size) { - return; - } - _socket->read(_requestContent.data(), size); - _socket->disconnect(this, SLOT(readContent())); + auto size = std::min(_socket->bytesAvailable(), _requestContent->bytesLeftToWrite()); - _parentManager->handleHTTPRequest(this, _requestUrl.path()); + _requestContent->write(_socket->read(size)); + + if (_requestContent->bytesLeftToWrite() == 0) { + _socket->disconnect(this, SLOT(readContent())); + + _parentManager->handleHTTPRequest(this, _requestUrl.path()); + } } diff --git a/libraries/embedded-webserver/src/HTTPConnection.h b/libraries/embedded-webserver/src/HTTPConnection.h index 44a523423a..4b42acf296 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.h +++ b/libraries/embedded-webserver/src/HTTPConnection.h @@ -16,14 +16,14 @@ #ifndef hifi_HTTPConnection_h #define hifi_HTTPConnection_h -#include #include -#include #include #include +#include #include #include #include +#include #include #include @@ -57,6 +57,17 @@ public: /// WebSocket close status codes. enum ReasonCode { NoReason = 0, NormalClosure = 1000, GoingAway = 1001 }; + class Storage { + public: + Storage() = default; + virtual ~Storage() = default; + + virtual const QByteArray& content() const = 0; + + virtual qint64 bytesLeftToWrite() const = 0; + virtual void write(const QByteArray& data) = 0; + }; + /// Initializes the connection. HTTPConnection(QTcpSocket* socket, HTTPManager* parentManager); @@ -76,7 +87,7 @@ public: 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; } + const QByteArray& requestContent() const { return _requestContent->content(); } /// Parses the request content as form data, returning a list of header/content pairs. QList parseFormData() const; @@ -129,7 +140,7 @@ protected: QByteArray _lastRequestHeader; /// The content of the request. - QByteArray _requestContent; + std::unique_ptr _requestContent; /// Response content std::unique_ptr _responseDevice; From 20f33d9cbc8e5880c85d14033e3b3d6a92f2ebfe Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 24 May 2018 17:45:10 -0700 Subject: [PATCH 10/38] CR --- libraries/embedded-webserver/src/HTTPManager.h | 2 +- libraries/embedded-webserver/src/HTTPSManager.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index e0477bd797..597f6921cc 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -33,7 +33,7 @@ class HTTPManager : public QTcpServer, public HTTPRequestHandler { Q_OBJECT public: /// Initializes the manager. - HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL); + HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = nullptr); bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override; diff --git a/libraries/embedded-webserver/src/HTTPSManager.h b/libraries/embedded-webserver/src/HTTPSManager.h index 433dd35d7d..9cea32862c 100644 --- a/libraries/embedded-webserver/src/HTTPSManager.h +++ b/libraries/embedded-webserver/src/HTTPSManager.h @@ -31,7 +31,7 @@ public: const QSslCertificate& certificate, const QSslKey& privateKey, const QString& documentRoot, - HTTPSRequestHandler* requestHandler = NULL); + HTTPSRequestHandler* requestHandler = nullptr); void setCertificate(const QSslCertificate& certificate) { _certificate = certificate; } void setPrivateKey(const QSslKey& privateKey) { _privateKey = privateKey; } From 03cec8cb54b31aa043d00aed560e0db9ffd9c650 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 30 May 2018 17:15:33 +1200 Subject: [PATCH 11/38] Fix properties not populating particle explorer tab --- scripts/system/edit.js | 11 +++----- .../particle_explorer/particleExplorerTool.js | 26 ++++++++++++++++--- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index f549c7dd85..9285023ed8 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2366,22 +2366,19 @@ var selectedParticleEntity = 0; var selectedParticleEntityID = null; function selectParticleEntity(entityID) { - var properties = Entities.getEntityProperties(entityID); selectedParticleEntityID = entityID; + + var properties = Entities.getEntityProperties(entityID); if (properties.emitOrientation) { properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation); } - var particleData = { - messageType: "particle_settings", - currentProperties: properties - }; + particleExplorerTool.destroyWebView(); particleExplorerTool.createWebView(); selectedParticleEntity = entityID; particleExplorerTool.setActiveParticleEntity(entityID); - - particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData)); + particleExplorerTool.setActiveParticleProperties(properties); // Switch to particle explorer var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); diff --git a/scripts/system/particle_explorer/particleExplorerTool.js b/scripts/system/particle_explorer/particleExplorerTool.js index d85fc169b1..016691e2b6 100644 --- a/scripts/system/particle_explorer/particleExplorerTool.js +++ b/scripts/system/particle_explorer/particleExplorerTool.js @@ -16,37 +16,55 @@ var PARTICLE_EXPLORER_HTML_URL = Script.resolvePath('particleExplorer.html'); ParticleExplorerTool = function() { var that = {}; + that.activeParticleEntity = 0; + that.activeParticleProperties = {}; + that.createWebView = function() { that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system"); that.webView.setVisible = function(value) {}; that.webView.webEventReceived.connect(that.webEventReceived); - } + }; that.destroyWebView = function() { if (!that.webView) { return; } that.activeParticleEntity = 0; + that.activeParticleProperties = {}; var messageData = { messageType: "particle_close" }; that.webView.emitScriptEvent(JSON.stringify(messageData)); + }; + + function sendActiveParticleProperies() { + that.webView.emitScriptEvent(JSON.stringify({ + messageType: "particle_settings", + currentProperties: that.activeParticleProperties + })); } that.webEventReceived = function(data) { - var data = JSON.parse(data); + data = JSON.parse(data); if (data.messageType === "settings_update") { if (data.updatedSettings.emitOrientation) { data.updatedSettings.emitOrientation = Quat.fromVec3Degrees(data.updatedSettings.emitOrientation); } Entities.editEntity(that.activeParticleEntity, data.updatedSettings); + } else if (data.messageType === "page_loaded") { + sendActiveParticleProperies(); } - } + }; that.setActiveParticleEntity = function(id) { that.activeParticleEntity = id; - } + }; + + that.setActiveParticleProperties = function(properties) { + that.activeParticleProperties = properties; + sendActiveParticleProperies(); + }; return that; }; From 8c9bdf51a7077b11347951c723d9b7e90a4fff6f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 30 May 2018 17:16:08 +1200 Subject: [PATCH 12/38] Remove superfluous variable --- scripts/system/edit.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 9285023ed8..05f5e3cb19 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2362,7 +2362,6 @@ var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); var propertiesTool = new PropertiesTool(); var particleExplorerTool = new ParticleExplorerTool(); -var selectedParticleEntity = 0; var selectedParticleEntityID = null; function selectParticleEntity(entityID) { @@ -2376,7 +2375,6 @@ function selectParticleEntity(entityID) { particleExplorerTool.destroyWebView(); particleExplorerTool.createWebView(); - selectedParticleEntity = entityID; particleExplorerTool.setActiveParticleEntity(entityID); particleExplorerTool.setActiveParticleProperties(properties); @@ -2401,13 +2399,13 @@ entityListTool.webView.webEventReceived.connect(function (data) { var ids = data.entityIds; if (ids.length === 1) { if (Entities.getEntityProperties(ids[0], "type").type === "ParticleEffect") { - if (JSON.stringify(selectedParticleEntity) === JSON.stringify(ids[0])) { + if (JSON.stringify(selectedParticleEntityID) === JSON.stringify(ids[0])) { // This particle entity is already selected, so return return; } // Destroy the old particles web view first } else { - selectedParticleEntity = 0; + selectedParticleEntityID = 0; particleExplorerTool.destroyWebView(); } } From 34120da63f9b578990f8ebc36bce10cbcd018f4b Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 30 May 2018 16:06:34 -0700 Subject: [PATCH 13/38] re-adding toggleSpaceMode --- .../system/libraries/entitySelectionTool.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index d03d4477f7..1b41559160 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1036,6 +1036,29 @@ SelectionDisplay = (function() { } }; + // FUNCTION: TOGGLE SPACE MODE + that.toggleSpaceMode = function() { + var wantDebug = false; + if (wantDebug) { + print("========> ToggleSpaceMode called. ========="); + } + if ((spaceMode === SPACE_WORLD) && (SelectionManager.selections.length > 1)) { + if (wantDebug) { + print("Local space editing is not available with multiple selections"); + } + return; + } + if (wantDebug) { + print("PreToggle: " + spaceMode); + } + spaceMode = (spaceMode === SPACE_LOCAL) ? SPACE_WORLD : SPACE_LOCAL; + that.updateHandles(); + if (wantDebug) { + print("PostToggle: " + spaceMode); + print("======== ToggleSpaceMode called. <========="); + } + }; + function addHandleTool(overlay, tool) { handleTools[overlay] = tool; return tool; From 688fad177698819e50115eef6a584abe5623e419 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Thu, 31 May 2018 14:11:28 -0300 Subject: [PATCH 14/38] Android - Fix joystick and view control bug that makes it bounce --- .../src/input-plugins/TouchscreenVirtualPadDevice.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp index 8d63b82911..32194e1b84 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp @@ -350,6 +350,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { if (idxMoveStartingPointCandidate != -1) { _moveCurrentTouchId = tPoints[idxMoveStartingPointCandidate].id(); _unusedTouches.erase(_moveCurrentTouchId); + thisPoint.x = tPoints[idxMoveStartingPointCandidate].pos().x(); + thisPoint.y = tPoints[idxMoveStartingPointCandidate].pos().y(); moveTouchBegin(thisPoint); } else { moveTouchEnd(); @@ -359,6 +361,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { if (idxViewStartingPointCandidate != -1) { _viewCurrentTouchId = tPoints[idxViewStartingPointCandidate].id(); _unusedTouches.erase(_viewCurrentTouchId); + thisPoint.x = tPoints[idxViewStartingPointCandidate].pos().x(); + thisPoint.y = tPoints[idxViewStartingPointCandidate].pos().y(); viewTouchBegin(thisPoint); } else { viewTouchEnd(); @@ -368,6 +372,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { if (idxJumpStartingPointCandidate != -1) { _jumpCurrentTouchId = tPoints[idxJumpStartingPointCandidate].id(); _unusedTouches.erase(_jumpCurrentTouchId); + thisPoint.x = tPoints[idxJumpStartingPointCandidate].pos().x(); + thisPoint.y = tPoints[idxJumpStartingPointCandidate].pos().y(); jumpTouchBegin(thisPoint); } else { if (_jumpHasValidTouch) { @@ -424,6 +430,7 @@ void TouchscreenVirtualPadDevice::moveTouchBegin(glm::vec2 touchPoint) { } else { _moveRefTouchPoint = touchPoint; } + _moveCurrentTouchPoint = touchPoint; _moveHasValidTouch = true; } } From 259803f85620a08b2c47ed3d2a5d4cd209a7c4c7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 31 May 2018 10:30:25 -0700 Subject: [PATCH 15/38] MS14615: Prevent memory overuse when minimized --- interface/src/Application.cpp | 2 +- interface/src/Application_render.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d4dc214fde..834cdda8f3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4238,7 +4238,7 @@ bool Application::acceptSnapshot(const QString& urlString) { static uint32_t _renderedFrameIndex { INVALID_FRAME }; bool Application::shouldPaint() const { - if (_aboutToQuit) { + if (_aboutToQuit || _window->isMinimized()) { return false; } diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index 2a16e8c33c..2208b3187c 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -30,9 +30,6 @@ void Application::editRenderArgs(RenderArgsEditor editor) { void Application::paintGL() { // Some plugins process message events, allowing paintGL to be called reentrantly. - if (_aboutToQuit || _window->isMinimized()) { - return; - } _renderFrameCount++; _lastTimeRendered.start(); From caa7f6486569130c64fdf88e6a6cefc20b4a03a7 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 31 May 2018 10:58:04 -0700 Subject: [PATCH 16/38] fixing show overlays menu item --- interface/src/Application.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d4dc214fde..e507af26f7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4798,12 +4798,15 @@ void Application::loadSettings() { // DONT CHECK IN //DependencyManager::get()->setAutomaticLODAdjust(false); - Menu::getInstance()->loadSettings(); + auto menu = Menu::getInstance(); + menu->loadSettings(); + + // override the menu option show overlays to always be true on startup + menu->setIsOptionChecked(MenuOption::Overlays, true); // If there is a preferred plugin, we probably messed it up with the menu settings, so fix it. auto pluginManager = PluginManager::getInstance(); auto plugins = pluginManager->getPreferredDisplayPlugins(); - auto menu = Menu::getInstance(); if (plugins.size() > 0) { for (auto plugin : plugins) { if (auto action = menu->getActionForOption(plugin->getName())) { From 60758dec8df809ddb8cd82d7ea5841e9cecc205d Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 31 May 2018 11:32:45 -0700 Subject: [PATCH 17/38] Check for null pointer --- libraries/render-utils/src/CauterizedModel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 1596f7ba83..eb7241b51f 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -87,7 +87,8 @@ void CauterizedModel::createVisibleRenderItemSet() { for (int partIndex = 0; partIndex < numParts; partIndex++) { auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast(ptr); - _modelMeshMaterialNames.push_back(getGeometry()->getShapeMaterial(shapeID)->getName()); + auto material = getGeometry()->getShapeMaterial(shapeID); + _modelMeshMaterialNames.push_back(material ? material->getName() : ""); _modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i }); shapeID++; } From 3c5a779a9843329d519d2d96cac168bd2a03fad3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 1 Jun 2018 09:36:28 +1200 Subject: [PATCH 18/38] Don't modify function parameter --- scripts/system/particle_explorer/particleExplorerTool.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/particle_explorer/particleExplorerTool.js b/scripts/system/particle_explorer/particleExplorerTool.js index 016691e2b6..0543cfc56b 100644 --- a/scripts/system/particle_explorer/particleExplorerTool.js +++ b/scripts/system/particle_explorer/particleExplorerTool.js @@ -45,8 +45,8 @@ ParticleExplorerTool = function() { })); } - that.webEventReceived = function(data) { - data = JSON.parse(data); + that.webEventReceived = function(message) { + var data = JSON.parse(message); if (data.messageType === "settings_update") { if (data.updatedSettings.emitOrientation) { data.updatedSettings.emitOrientation = Quat.fromVec3Degrees(data.updatedSettings.emitOrientation); From a06c9eccca0d4dd1ea32e299fe515510e0137c60 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 1 Jun 2018 00:16:42 +0200 Subject: [PATCH 19/38] - fix create-app camera orbit to not jump around on secondary monitor - style fixes entityCameraTool.js --- scripts/system/libraries/entityCameraTool.js | 40 ++++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/scripts/system/libraries/entityCameraTool.js b/scripts/system/libraries/entityCameraTool.js index 0e52353dfb..579df8ed7d 100644 --- a/scripts/system/libraries/entityCameraTool.js +++ b/scripts/system/libraries/entityCameraTool.js @@ -91,7 +91,7 @@ CameraManager = function() { } var CAPTURED_KEYS = []; - for (key in keyToActionMapping) { + for (var key in keyToActionMapping) { CAPTURED_KEYS.push(key); } @@ -99,9 +99,9 @@ CameraManager = function() { var action = keyToActionMapping[event.text]; if (action !== undefined) { if (event.isShifted) { - if (action == "orbitForward") { + if (action === "orbitForward") { action = "orbitUp"; - } else if (action == "orbitBackward") { + } else if (action === "orbitBackward") { action = "orbitDown"; } } @@ -133,7 +133,7 @@ CameraManager = function() { }; that.enable = function() { - if (Camera.mode == "independent" || that.enabled || HMD.active) { + if (Camera.mode === "independent" || that.enabled || HMD.active) { return; } @@ -235,7 +235,7 @@ CameraManager = function() { } that.setFocalPoint = function(pos) { - that.targetFocalPoint = pos + that.targetFocalPoint = pos; that.updateCamera(); } @@ -276,7 +276,7 @@ CameraManager = function() { } that.mouseMoveEvent = function(event) { - if (that.enabled && that.mode != MODE_INACTIVE) { + if (that.enabled && that.mode !== MODE_INACTIVE) { var x = Reticle.getPosition().x; var y = Reticle.getPosition().y; if (!hasDragged) { @@ -284,11 +284,11 @@ CameraManager = function() { that.lastMousePosition.y = y; hasDragged = true; } - if (that.mode == MODE_ORBIT) { + if (that.mode === MODE_ORBIT) { var diffX = x - that.lastMousePosition.x; var diffY = y - that.lastMousePosition.y; - that.targetYaw -= MOUSE_SENSITIVITY * (diffX / 5.0) - that.targetPitch += MOUSE_SENSITIVITY * (diffY / 10.0) + that.targetYaw -= MOUSE_SENSITIVITY * (diffX / 5.0); + that.targetPitch += MOUSE_SENSITIVITY * (diffY / 10.0); while (that.targetYaw > 180.0) that.targetYaw -= 360; while (that.targetYaw < -180.0) that.targetYaw += 360; @@ -297,7 +297,7 @@ CameraManager = function() { if (that.targetPitch < -90) that.targetPitch = -90; that.updateCamera(); - } else if (that.mode == MODE_PAN) { + } else if (that.mode === MODE_PAN) { var diffX = x - that.lastMousePosition.x; var diffY = y - that.lastMousePosition.y; @@ -316,19 +316,19 @@ CameraManager = function() { var newY = y; var updatePosition = false; - if (x <= Window.x) { - newX = Window.x + Window.innerWidth; + if (x < 0) { + newX = Window.innerWidth; updatePosition = true; - } else if (x >= (Window.x + Window.innerWidth)) { - newX = Window.x; + } else if (x > Window.innerWidth) { + newX = 0; updatePosition = true; } - if (y <= Window.y) { - newY = Window.y + Window.innerHeight; + if (y < 0) { + newY = Window.innerHeight; updatePosition = true; - } else if (y >= (Window.y + Window.innerHeight)) { - newY = Window.y; + } else if (y > Window.innerHeight) { + newY = 0; updatePosition = true; } @@ -410,7 +410,7 @@ CameraManager = function() { } that.updateCamera = function() { - if (!that.enabled || Camera.mode != "independent") { + if (!that.enabled || Camera.mode !== "independent") { cameraTool.update(); return; } @@ -464,7 +464,7 @@ CameraManager = function() { // Ease the position and orbit of the camera that.update = function(dt) { - if (Camera.mode != "independent") { + if (Camera.mode !== "independent") { that.updateCamera(); return; } From 3779a4780e7ce59cda61eb4b36eabc714e571aed Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 1 Jun 2018 02:07:02 +0200 Subject: [PATCH 20/38] Prevent the cursor from being stuck on the most outer edges of the screen --- scripts/system/libraries/entityCameraTool.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/libraries/entityCameraTool.js b/scripts/system/libraries/entityCameraTool.js index 579df8ed7d..fb808cc7ea 100644 --- a/scripts/system/libraries/entityCameraTool.js +++ b/scripts/system/libraries/entityCameraTool.js @@ -316,18 +316,18 @@ CameraManager = function() { var newY = y; var updatePosition = false; - if (x < 0) { + if (x <= 0) { newX = Window.innerWidth; updatePosition = true; - } else if (x > Window.innerWidth) { + } else if (x >= Window.innerWidth) { newX = 0; updatePosition = true; } - if (y < 0) { + if (y <= 0) { newY = Window.innerHeight; updatePosition = true; - } else if (y > Window.innerHeight) { + } else if (y >= Window.innerHeight) { newY = 0; updatePosition = true; } From 9ae19714b520d721c68cdd29bac0839fb7f9b3e5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 1 Jun 2018 16:36:44 -0700 Subject: [PATCH 21/38] also update queryAACube for children of worn entities --- interface/src/avatar/MyAvatar.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0783094e10..f956bc90c3 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -654,8 +654,8 @@ void MyAvatar::simulate(float deltaTime) { if (success) { moveOperator.addEntityToMoveList(entity, newCube); } - // send an edit packet to update the entity-server about the queryAABox. If it's an - // avatar-entity, don't. + // send an edit packet to update the entity-server about the queryAABox + // unless it is client-only if (packetSender && !entity->getClientOnly()) { EntityItemProperties properties = entity->getProperties(); properties.setQueryAACubeDirty(); @@ -663,6 +663,17 @@ void MyAvatar::simulate(float deltaTime) { packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entity->getID(), properties); entity->setLastBroadcast(usecTimestampNow()); + + entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { + EntityItemPointer entityDescendant = std::static_pointer_cast(descendant); + if (!entityDescendant->getClientOnly() && descendant->updateQueryAACube()) { + EntityItemProperties descendantProperties; + descendantProperties.setQueryAACube(descendant->getQueryAACube()); + descendantProperties.setLastEdited(now); + packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entityDescendant->getID(), descendantProperties); + entityDescendant->setLastBroadcast(now); // for debug/physics status icons + } + }); } } }); From db3520b33211732e208dc33e180bbaf5be35f8c5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 1 Jun 2018 16:38:03 -0700 Subject: [PATCH 22/38] also update queryAACube for 'Held' things --- libraries/physics/src/EntityMotionState.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index deb779b1f8..594ea476f6 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -446,17 +446,13 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { // this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor assert(!(_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID())); - // shouldSendUpdate() sould NOT be triggering updates to maintain the queryAACube of dynamic entities. - // The server is supposed to predict the transform of such moving things. The client performs a "double prediction" - // where it predicts what the the server is doing, and only sends updates whent the entity's true transform - // differs significantly. That is what the remoteSimulationOutOfSync() logic is all about. - if (_entity->dynamicDataNeedsTransmit() || - (!_entity->getDynamic() && _entity->queryAACubeNeedsUpdate())) { + if (_entity->dynamicDataNeedsTransmit()) { return true; } - if (_entity->shouldSuppressLocationEdits()) { - return false; + // "shouldSuppressLocationEdits" really means: "the entity has a 'Hold' action therefore + // we don't need send an update unless the entity is not contained by its queryAACube" + return _entity->queryAACubeNeedsUpdate(); } return remoteSimulationOutOfSync(simulationStep); From 074892591d87450aa5cb06844fb602743ccc6416 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 4 Jun 2018 11:10:29 -0700 Subject: [PATCH 23/38] fix auto-selecting subdir and not showing Choose when no row selected --- interface/resources/qml/dialogs/FileDialog.qml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 49bfe78434..6651af0db3 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -332,6 +332,7 @@ ModalWindow { } onFolderChanged: { + d.clearSelection(); fileTableModel.update(); // Update once the data from the folder change is available. } @@ -451,7 +452,7 @@ ModalWindow { rows = 0, i; - var newFilesModel = filesModelBuilder.createObject(root); + filesModel = filesModelBuilder.createObject(root); comparisonFunction = sortOrder === Qt.AscendingOrder ? function(a, b) { return a < b; } @@ -473,7 +474,7 @@ ModalWindow { while (lower < upper) { middle = Math.floor((lower + upper) / 2); var lessThan; - if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) { + if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) { lessThan = true; upper = middle; } else { @@ -482,7 +483,7 @@ ModalWindow { } } - newFilesModel.insert(lower, { + filesModel.insert(lower, { fileName: fileName, fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), fileSize: model.getItem(i, "fileSize"), @@ -493,9 +494,6 @@ ModalWindow { rows++; } - filesModel = newFilesModel; - - d.clearSelection(); } } From cf7a3d1af6f07e2d396495c05d2fd524c74f7584 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 4 Jun 2018 11:30:29 -0700 Subject: [PATCH 24/38] Fix MS7224: Add dimensions to printed Snapshot entity --- scripts/system/snapshot.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 29089e6597..f7e590c6f9 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -295,6 +295,7 @@ function printToPolaroid(image_url) { "description": "Printed from Snaps", "modelURL": POLAROID_MODEL_URL, + "dimensions": { "x": 0.5667, "y": 0.0212, "z": 0.4176 }, "position": model_pos, "rotation": model_rot, From 8af1beca8dd72288c27b63c1b7a8ec535b1a4615 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 4 Jun 2018 12:07:57 -0700 Subject: [PATCH 25/38] add isGroupCulled for model overlays --- interface/src/ui/overlays/Base3DOverlay.cpp | 20 +++++++++++++ interface/src/ui/overlays/Base3DOverlay.h | 1 + interface/src/ui/overlays/ModelOverlay.cpp | 22 ++++++++++++++ interface/src/ui/overlays/ModelOverlay.h | 4 +++ interface/src/ui/overlays/Overlay.cpp | 17 +++++++++++ interface/src/ui/overlays/Overlay.h | 1 + interface/src/ui/overlays/OverlaysPayload.cpp | 29 +------------------ libraries/render/src/render/Item.h | 1 + scripts/system/libraries/WebTablet.js | 3 +- 9 files changed, 69 insertions(+), 29 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index f4efd1301d..f59b513bd5 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -349,3 +349,23 @@ void Base3DOverlay::setVisible(bool visible) { Parent::setVisible(visible); notifyRenderVariableChange(); } + +render::ItemKey Base3DOverlay::getKey() { + auto builder = render::ItemKey::Builder(Overlay::getKey()); + + if (getDrawInFront()) { + builder.withLayer(render::hifi::LAYER_3D_FRONT); + } else if (getDrawHUDLayer()) { + builder.withLayer(render::hifi::LAYER_3D_HUD); + } else { + builder.withoutLayer(); + } + + builder.withoutViewSpace(); + + if (isTransparent()) { + builder.withTransparent(); + } + + return builder.build(); +} \ No newline at end of file diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index ab83a64273..2a63a6cb67 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -35,6 +35,7 @@ public: // getters virtual bool is3D() const override { return true; } + virtual render::ItemKey getKey() override; virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return (uint32_t) subItems.size(); } virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); } diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index a541b3365f..4ed7072f23 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -115,6 +115,10 @@ void ModelOverlay::update(float deltatime) { _drawInHUDDirty = false; _model->setLayeredInHUD(getDrawHUDLayer(), scene); } + if (_groupCulledDirty) { + _groupCulledDirty = false; + _model->setGroupCulled(_isGroupCulled); + } scene->enqueueTransaction(transaction); if (!_texturesLoaded && _model->getGeometry() && _model->getGeometry()->areTexturesLoaded()) { @@ -158,6 +162,11 @@ void ModelOverlay::setDrawHUDLayer(bool drawHUDLayer) { _drawInHUDDirty = true; } +void ModelOverlay::setGroupCulled(bool groupCulled) { + _isGroupCulled = groupCulled; + _groupCulledDirty = true; +} + void ModelOverlay::setProperties(const QVariantMap& properties) { auto origPosition = getWorldPosition(); auto origRotation = getWorldOrientation(); @@ -210,6 +219,11 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { Q_ARG(const QVariantMap&, textureMap)); } + auto groupCulledValue = properties["isGroupCulled"]; + if (groupCulledValue.isValid() && groupCulledValue.canConvert(QVariant::Bool)) { + setGroupCulled(groupCulledValue.toBool()); + } + // jointNames is read-only. // jointPositions is read-only. // jointOrientations is read-only. @@ -711,3 +725,11 @@ scriptable::ScriptableModelBase ModelOverlay::getScriptableModel() { } return result; } + +render::ItemKey ModelOverlay::getKey() { + auto builder = render::ItemKey::Builder(Base3DOverlay::getKey()); + if (_isGroupCulled) { + builder.withMetaCullGroup(); + } + return builder.build(); +} \ No newline at end of file diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 3ef3f23fec..334c9c06f1 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -33,6 +33,7 @@ public: virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override; + render::ItemKey getKey() override; void clearSubRenderItemIDs(); void setSubRenderItemIDs(const render::ItemIDs& ids); @@ -63,6 +64,7 @@ public: void setVisible(bool visible) override; void setDrawInFront(bool drawInFront) override; void setDrawHUDLayer(bool drawHUDLayer) override; + void setGroupCulled(bool groupCulled); void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override; void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override; @@ -121,6 +123,8 @@ private: bool _visibleDirty { true }; bool _drawInFrontDirty { false }; bool _drawInHUDDirty { false }; + bool _isGroupCulled { false }; + bool _groupCulledDirty { false }; void processMaterials(); diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 2c0c7c71b6..faa15ee2b4 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -244,4 +244,21 @@ void Overlay::addMaterial(graphics::MaterialLayer material, const std::string& p void Overlay::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) { std::lock_guard lock(_materialsLock); _materials[parentMaterialName].remove(material); +} + +render::ItemKey Overlay::getKey() { + auto builder = render::ItemKey::Builder().withTypeShape(); + + builder.withViewSpace(); + builder.withLayer(render::hifi::LAYER_2D); + + if (!getVisible()) { + builder.withInvisible(); + } + + // always visible in primary view. if isVisibleInSecondaryCamera, also draw in secondary view + render::hifi::Tag viewTagBits = getIsVisibleInSecondaryCamera() ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW; + builder.withTagBits(viewTagBits); + + return builder.build(); } \ No newline at end of file diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 890d5178e5..45fc77a452 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -40,6 +40,7 @@ public: virtual void update(float deltatime) {} virtual void render(RenderArgs* args) = 0; + virtual render::ItemKey getKey(); virtual AABox getBounds() const = 0; virtual bool supportsGetProperty() const { return true; } diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index 6b55b2b701..37fadef0b4 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -32,34 +32,7 @@ namespace render { template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) { - auto builder = ItemKey::Builder().withTypeShape(); - if (overlay->is3D()) { - auto overlay3D = std::static_pointer_cast(overlay); - if (overlay3D->getDrawInFront()) { - builder.withLayer(render::hifi::LAYER_3D_FRONT); - } else if (overlay3D->getDrawHUDLayer()) { - builder.withLayer(render::hifi::LAYER_3D_HUD); - } - - if (overlay->isTransparent()) { - builder.withTransparent(); - } - } else { - builder.withViewSpace(); - builder.withLayer(render::hifi::LAYER_2D); - } - - if (!overlay->getVisible()) { - builder.withInvisible(); - } - - // always visible in primary view. if isVisibleInSecondaryCamera, also draw in secondary view - uint32_t viewTagBits = render::hifi::TAG_MAIN_VIEW | - (overlay->getIsVisibleInSecondaryCamera() ? render::hifi::TAG_SECONDARY_VIEW : render::hifi::TAG_NONE); - - builder.withTagBits(viewTagBits); - - return builder.build(); + return overlay->getKey(); } template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) { return overlay->getBounds(); diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 1dfef73686..28994d82b6 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -149,6 +149,7 @@ public: Builder& withTypeMeta() { _flags.set(TYPE_META); return (*this); } Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); } Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } + Builder& withoutViewSpace() { _flags.reset(VIEW_SPACE); return (*this); } Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 783b91f5f0..fade6f6b26 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -115,7 +115,8 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { }), dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth }, parentID: MyAvatar.SELF_ID, - visible: visible + visible: visible, + isGroupCulled: true }; // compute position, rotation & parentJointIndex of the tablet From b4b63187aedcb2e9cd6037c123ff365675cb7587 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 5 Jun 2018 08:48:10 +1200 Subject: [PATCH 26/38] Fix typo --- scripts/system/particle_explorer/particleExplorerTool.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/particle_explorer/particleExplorerTool.js b/scripts/system/particle_explorer/particleExplorerTool.js index 0543cfc56b..5e5be9ead0 100644 --- a/scripts/system/particle_explorer/particleExplorerTool.js +++ b/scripts/system/particle_explorer/particleExplorerTool.js @@ -38,7 +38,7 @@ ParticleExplorerTool = function() { that.webView.emitScriptEvent(JSON.stringify(messageData)); }; - function sendActiveParticleProperies() { + function sendActiveParticleProperties() { that.webView.emitScriptEvent(JSON.stringify({ messageType: "particle_settings", currentProperties: that.activeParticleProperties @@ -53,7 +53,7 @@ ParticleExplorerTool = function() { } Entities.editEntity(that.activeParticleEntity, data.updatedSettings); } else if (data.messageType === "page_loaded") { - sendActiveParticleProperies(); + sendActiveParticleProperties(); } }; @@ -63,7 +63,7 @@ ParticleExplorerTool = function() { that.setActiveParticleProperties = function(properties) { that.activeParticleProperties = properties; - sendActiveParticleProperies(); + sendActiveParticleProperties(); }; return that; From 7407507f0bdf976c28c30f450bcf9fc7660f080e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 4 Jun 2018 14:13:25 -0700 Subject: [PATCH 27/38] Fix MS15574: Tiny memory leak in Wallet.h --- interface/src/commerce/Wallet.cpp | 2 ++ interface/src/commerce/Wallet.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 982adb4b5e..f427c3c6b0 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -314,6 +314,7 @@ Wallet::Wallet() { auto nodeList = DependencyManager::get(); auto ledger = DependencyManager::get(); auto& packetReceiver = nodeList->getPacketReceiver(); + _passphrase = new QString(""); packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket"); packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest, this, "handleChallengeOwnershipPacket"); @@ -359,6 +360,7 @@ void Wallet::clear() { // tell the provider we got nothing updateImageProvider(); _passphrase->clear(); + delete _passphrase; } Wallet::~Wallet() { diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 8a7d6b8c07..665afd9a23 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -78,7 +78,7 @@ private: QByteArray _salt; QByteArray _iv; QByteArray _ckey; - QString* _passphrase { new QString("") }; + QString* _passphrase { nullptr }; bool _isOverridingServer { false }; bool writeWallet(const QString& newPassphrase = QString("")); From 60aff7a224d0dd1bf2c353528cceec911d965b44 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 4 Jun 2018 14:41:18 -0700 Subject: [PATCH 28/38] Whoops --- interface/src/commerce/Wallet.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index f427c3c6b0..f7e749317d 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -360,13 +360,16 @@ void Wallet::clear() { // tell the provider we got nothing updateImageProvider(); _passphrase->clear(); - delete _passphrase; } Wallet::~Wallet() { if (_securityImage) { delete _securityImage; } + + if (_passphrase) { + delete _passphrase; + } } bool Wallet::setPassphrase(const QString& passphrase) { From 6b7d9586d1862b857bf89e79500d7e00e9bd5416 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 4 Jun 2018 15:56:10 -0700 Subject: [PATCH 29/38] apply fixes to other file dialogs and fix tablet dialog buttons --- .../qml/dialogs/+android/FileDialog.qml | 10 ++-- .../qml/dialogs/TabletFileDialog.qml | 12 ++-- .../tablet/tabletWindows/TabletFileDialog.qml | 60 ++++++++++++------- 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/interface/resources/qml/dialogs/+android/FileDialog.qml b/interface/resources/qml/dialogs/+android/FileDialog.qml index 86e6e1ef6c..be6524d2b8 100644 --- a/interface/resources/qml/dialogs/+android/FileDialog.qml +++ b/interface/resources/qml/dialogs/+android/FileDialog.qml @@ -331,6 +331,7 @@ ModalWindow { } onFolderChanged: { + d.clearSelection(); fileTableModel.update(); // Update once the data from the folder change is available. } @@ -450,7 +451,7 @@ ModalWindow { rows = 0, i; - var newFilesModel = filesModelBuilder.createObject(root); + filesModel = filesModelBuilder.createObject(root); comparisonFunction = sortOrder === Qt.AscendingOrder ? function(a, b) { return a < b; } @@ -472,7 +473,7 @@ ModalWindow { while (lower < upper) { middle = Math.floor((lower + upper) / 2); var lessThan; - if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) { + if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) { lessThan = true; upper = middle; } else { @@ -481,7 +482,7 @@ ModalWindow { } } - newFilesModel.insert(lower, { + filesModel.insert(lower, { fileName: fileName, fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), fileSize: model.getItem(i, "fileSize"), @@ -492,9 +493,6 @@ ModalWindow { rows++; } - filesModel = newFilesModel; - - d.clearSelection(); } } diff --git a/interface/resources/qml/dialogs/TabletFileDialog.qml b/interface/resources/qml/dialogs/TabletFileDialog.qml index e7c93e6d8e..4de0460796 100644 --- a/interface/resources/qml/dialogs/TabletFileDialog.qml +++ b/interface/resources/qml/dialogs/TabletFileDialog.qml @@ -295,7 +295,8 @@ TabletModalWindow { } onFolderChanged: { - fileTableModel.update() + d.clearSelection(); + fileTableModel.update(); } function getItem(index, field) { @@ -413,7 +414,7 @@ TabletModalWindow { rows = 0, i; - var newFilesModel = filesModelBuilder.createObject(root); + filesModel = filesModelBuilder.createObject(root); comparisonFunction = sortOrder === Qt.AscendingOrder ? function(a, b) { return a < b; } @@ -435,7 +436,7 @@ TabletModalWindow { while (lower < upper) { middle = Math.floor((lower + upper) / 2); var lessThan; - if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) { + if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) { lessThan = true; upper = middle; } else { @@ -444,7 +445,7 @@ TabletModalWindow { } } - newFilesModel.insert(lower, { + filesModel.insert(lower, { fileName: fileName, fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), fileSize: model.getItem(i, "fileSize"), @@ -455,9 +456,6 @@ TabletModalWindow { rows++; } - filesModel = newFilesModel; - - d.clearSelection(); } } diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml index f3f98f24e5..871d1c92a9 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/TabletFileDialog.qml @@ -8,12 +8,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick 2.7 import Qt.labs.folderlistmodel 2.1 import Qt.labs.settings 1.0 -import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs +import QtQuick.Controls 1.4 as QQC1 +import QtQuick.Controls 2.3 import ".." import "../../../controls-uit" @@ -30,6 +30,8 @@ Rectangle { color: hifi.colors.baseGray; + property var filesModel: ListModel { } + Settings { category: "FileDialog" property alias width: root.width @@ -149,7 +151,7 @@ Rectangle { ComboBox { id: pathSelector - anchors { + anchors { top: parent.top topMargin: hifi.dimensions.contentMargin.y left: navControls.right @@ -247,7 +249,9 @@ Rectangle { } currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath); - currentSelectionIsFolder = fileTableView.model.isFolder(row); + currentSelectionIsFolder = fileTableView.model !== filesModel ? + fileTableView.model.isFolder(row) : + fileTableModel.isFolder(row); if (root.selectDirectory || !currentSelectionIsFolder) { currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl)); } else { @@ -287,6 +291,7 @@ Rectangle { } onFolderChanged: { + d.clearSelection(); fileTableModel.update(); // Update once the data from the folder change is available. } @@ -327,7 +332,12 @@ Rectangle { } } - ListModel { + Component { + id: filesModelBuilder + ListModel { } + } + + QtObject { id: fileTableModel // FolderListModel has a couple of problems: @@ -379,7 +389,11 @@ Rectangle { if (row === -1) { return false; } - return get(row).fileIsDir; + return filesModel.get(row).fileIsDir; + } + + function get(row) { + return filesModel.get(row) } function update() { @@ -397,7 +411,7 @@ Rectangle { rows = 0, i; - clear(); + filesModel = filesModelBuilder.createObject(root); comparisonFunction = sortOrder === Qt.AscendingOrder ? function(a, b) { return a < b; } @@ -419,7 +433,7 @@ Rectangle { while (lower < upper) { middle = Math.floor((lower + upper) / 2); var lessThan; - if (comparisonFunction(sortValue, get(middle)[sortField])) { + if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) { lessThan = true; upper = middle; } else { @@ -428,7 +442,7 @@ Rectangle { } } - insert(lower, { + filesModel.insert(lower, { fileName: fileName, fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), fileSize: model.getItem(i, "fileSize"), @@ -439,8 +453,6 @@ Rectangle { rows++; } - - d.clearSelection(); } } @@ -465,12 +477,12 @@ Rectangle { sortIndicatorOrder: Qt.AscendingOrder sortIndicatorVisible: true - model: fileTableModel + model: filesModel function updateSort() { - model.sortOrder = sortIndicatorOrder; - model.sortColumn = sortIndicatorColumn; - model.update(); + fileTableModel.sortOrder = sortIndicatorOrder; + fileTableModel.sortColumn = sortIndicatorColumn; + fileTableModel.update(); } onSortIndicatorColumnChanged: { updateSort(); } @@ -522,7 +534,7 @@ Rectangle { } } - TableViewColumn { + QQC1.TableViewColumn { id: fileNameColumn role: "fileName" title: "Name" @@ -530,7 +542,7 @@ Rectangle { movable: false resizable: true } - TableViewColumn { + QQC1.TableViewColumn { id: fileMofifiedColumn role: "fileModified" title: "Date" @@ -539,7 +551,7 @@ Rectangle { resizable: true visible: !selectDirectory } - TableViewColumn { + QQC1.TableViewColumn { role: "fileSize" title: "Size" width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width @@ -554,11 +566,12 @@ Rectangle { } function navigateToCurrentRow() { + var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel var row = fileTableView.currentRow - var isFolder = model.isFolder(row); - var file = model.get(row).filePath; + var isFolder = currentModel.isFolder(row); + var file = currentModel.get(row).filePath; if (isFolder) { - fileTableView.model.folder = helper.pathToUrl(file); + currentModel.folder = helper.pathToUrl(file); } else { okAction.trigger(); } @@ -573,7 +586,8 @@ Rectangle { var newPrefix = prefix + event.text.toLowerCase(); var matchedIndex = -1; for (var i = 0; i < model.count; ++i) { - var name = model.get(i).fileName.toLowerCase(); + var name = model !== filesModel ? model.get(i).fileName.toLowerCase() : + filesModel.get(i).fileName.toLowerCase(); if (0 === name.indexOf(newPrefix)) { matchedIndex = i; break; From a7a0cfeef2e48ec12f71c51c7443e9a7c45ce410 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 5 Jun 2018 10:56:19 +1200 Subject: [PATCH 30/38] Remove some deprecated functions from the Controller API --- .../scripting/ControllerScriptingInterface.h | 3 -- .../src/controllers/ScriptingInterface.cpp | 12 ------ .../src/controllers/ScriptingInterface.h | 37 ------------------- 3 files changed, 52 deletions(-) diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index e16383e234..051a372aad 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -105,9 +105,6 @@ class ScriptEngine; *
  • {@link Controller.getValue|getValue}
  • *
  • {@link Controller.getAxisValue|getAxisValue}
  • *
  • {@link Controller.getPoseValue|getgetPoseValue}
  • - *
  • {@link Controller.getButtonValue|getButtonValue} for a particular device
  • - *
  • {@link Controller.getAxisValue(0)|getAxisValue} for a particular device
  • - *
  • {@link Controller.getPoseValue(0)|getPoseValue} for a particular device
  • *
  • {@link Controller.getActionValue|getActionValue}
  • * * diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 16db22401f..f49b41cbe6 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -92,28 +92,16 @@ namespace controller { return userInputMapper->getValue(Input((uint32_t)source)); } - float ScriptingInterface::getButtonValue(StandardButtonChannel source, uint16_t device) const { - return getValue(Input(device, source, ChannelType::BUTTON).getID()); - } - float ScriptingInterface::getAxisValue(int source) const { auto userInputMapper = DependencyManager::get(); return userInputMapper->getValue(Input((uint32_t)source)); } - float ScriptingInterface::getAxisValue(StandardAxisChannel source, uint16_t device) const { - return getValue(Input(device, source, ChannelType::AXIS).getID()); - } - Pose ScriptingInterface::getPoseValue(const int& source) const { auto userInputMapper = DependencyManager::get(); return userInputMapper->getPose(Input((uint32_t)source)); } - Pose ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const { - return getPoseValue(Input(device, source, ChannelType::POSE).getID()); - } - QVector ScriptingInterface::getAllActions() { return DependencyManager::get()->getAllActions(); } diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index dacb0c8568..b0004bc12d 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -206,43 +206,6 @@ namespace controller { */ Q_INVOKABLE Pose getPoseValue(const int& source) const; - /**jsdoc - * Get the value of a button on a particular device. - * @function Controller.getButtonValue - * @param {StandardButtonChannel} source - The button to get the value of. - * @param {number} [device=0] - The ID of the hardware device to get the value from. The default value of - * 0 corresponds to Standard. - * @returns {number} The current value of the button if the parameters are valid, otherwise 0. - * @deprecated This function no longer works. - */ - // FIXME: This function causes a JavaScript crash: https://highfidelity.manuscript.com/f/cases/edit/14139 - Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const; - - /**jsdoc - * Get the value of an axis control on a particular device. - * @function Controller.getAxisValue - * @variation 0 - * @param {StandardAxisChannel} source - The axis to get the value of. - * @param {number} [device=0] - The ID of the hardware device to get the value from. The default value of - * 0 corresponds to Standard. - * @returns {number} The current value of the axis if the parameters are valid, otherwise 0. - * @deprecated This function no longer works. - */ - Q_INVOKABLE float getAxisValue(StandardAxisChannel source, uint16_t device = 0) const; - - /**jsdoc - * Get the value of an pose control on a particular device. - * @function Controller.getPoseValue - * @variation 0 - * @param {StandardPoseChannel} source - The pose to get the value of. - * @param {number} [device=0] - The ID of the hardware device to get the value from. The default value of - * 0 corresponds to Standard. - * @returns {Pose} The current value of the controller pose output if the parameters are valid, otherwise an invalid - * pose with Pose.valid == false. - * @deprecated This function no longer works. - */ - Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const; - /**jsdoc * Triggers a haptic pulse on connected and enabled devices that have the capability. * @function Controller.triggerHapticPulse From 968758a0222994250fd559d744b01a6f7065a5f5 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 5 Jun 2018 09:35:17 -0700 Subject: [PATCH 31/38] CR --- interface/src/ui/overlays/ModelOverlay.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index a4ae8bc815..fbb5aae84c 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -153,18 +153,24 @@ void ModelOverlay::setVisible(bool visible) { } void ModelOverlay::setDrawInFront(bool drawInFront) { - Base3DOverlay::setDrawInFront(drawInFront); - _drawInFrontDirty = true; + if (drawInFront != getDrawInFront()) { + Base3DOverlay::setDrawInFront(drawInFront); + _drawInFrontDirty = true; + } } void ModelOverlay::setDrawHUDLayer(bool drawHUDLayer) { - Base3DOverlay::setDrawHUDLayer(drawHUDLayer); - _drawInHUDDirty = true; + if (drawHUDLayer != getDrawHUDLayer()) { + Base3DOverlay::setDrawHUDLayer(drawHUDLayer); + _drawInHUDDirty = true; + } } void ModelOverlay::setGroupCulled(bool groupCulled) { - _isGroupCulled = groupCulled; - _groupCulledDirty = true; + if (groupCulled != _isGroupCulled) { + _isGroupCulled = groupCulled; + _groupCulledDirty = true; + } } void ModelOverlay::setProperties(const QVariantMap& properties) { From c1dceda4ac43f7d4d0afec5772e35f979ea2867c Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 5 Jun 2018 12:01:06 -0700 Subject: [PATCH 32/38] enable gamepad by default --- plugins/hifiSdl2/src/SDL2Manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hifiSdl2/src/SDL2Manager.cpp b/plugins/hifiSdl2/src/SDL2Manager.cpp index 664e53d115..df0cef06c8 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.cpp +++ b/plugins/hifiSdl2/src/SDL2Manager.cpp @@ -47,7 +47,7 @@ static_assert( const char* SDL2Manager::NAME = "SDL2"; const char* SDL2Manager::SDL2_ID_STRING = "SDL2"; -const bool DEFAULT_ENABLED = false; +const bool DEFAULT_ENABLED = true; SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) { SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller); From b954ac67598681dafc96d4dba2e3dfeae6fa84a2 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 6 Jun 2018 03:43:27 +0300 Subject: [PATCH 33/38] FB15569 - Debug default scripts no longer on developer menu --- scripts/defaultScripts.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 59a51830be..045dff1295 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -57,7 +57,6 @@ if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_I menuItemName: MENU_ITEM, isCheckable: true, isChecked: previousSetting, - grouping: "Advanced" }); } From 5897c9948d8e52daf077a03a9256e6e2bfab9ef2 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 6 Jun 2018 03:52:22 +0300 Subject: [PATCH 34/38] terminate thread if it didn't complete during MAX_SCRIPT_QUITTING_TIME --- libraries/script-engine/src/ScriptEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f98bffb739..f8c99b192f 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -419,7 +419,7 @@ void ScriptEngine::waitTillDoneRunning() { // Wait for the scripting thread to stop running, as // flooding it with aborts/exceptions will persist it longer static const auto MAX_SCRIPT_QUITTING_TIME = 0.5 * MSECS_PER_SECOND; - if (workerThread->wait(MAX_SCRIPT_QUITTING_TIME)) { + if (!workerThread->wait(MAX_SCRIPT_QUITTING_TIME)) { workerThread->terminate(); } } From 686971802517df3929b2c96fbe0c84f727c81928 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 6 Jun 2018 14:41:33 -0700 Subject: [PATCH 35/38] Fix MS14925: Don't double-call signal for display plugin change --- interface/src/Application.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cbe713127d..41160afb4d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -8041,7 +8041,6 @@ void Application::switchDisplayMode() { setActiveDisplayPlugin(DESKTOP_DISPLAY_PLUGIN_NAME); startHMDStandBySession(); } - emit activeDisplayPluginChanged(); } _previousHMDWornStatus = currentHMDWornStatus; } From fa51c1584981db26c4b547f748a86423dc9f7bf8 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 6 Jun 2018 14:29:53 -0700 Subject: [PATCH 36/38] Use binary resources on OSX --- interface/CMakeLists.txt | 42 ++++++++++++------------- interface/src/Application.cpp | 10 +----- libraries/shared/src/PathUtils.cpp | 50 +++++++++++++----------------- libraries/shared/src/PathUtils.h | 1 + 4 files changed, 45 insertions(+), 58 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index ac9441319b..4204718976 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -133,13 +133,7 @@ if (APPLE) # set where in the bundle to put the resources file set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/icon/${INTERFACE_ICON_FILENAME} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - set(DISCOVERED_RESOURCES "") - - # use the add_resources_to_os_x_bundle macro to recurse into resources - add_resources_to_os_x_bundle("${CMAKE_CURRENT_SOURCE_DIR}/resources") - # append the discovered resources to our list of interface sources - list(APPEND INTERFACE_SRCS ${DISCOVERED_RESOURCES}) list(APPEND INTERFACE_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/icon/${INTERFACE_ICON_FILENAME}) endif() @@ -316,18 +310,27 @@ if (APPLE) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources") set(RESOURCES_DEV_DIR "$/../Resources") - # copy script files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + # copy script files beside the executable COMMAND "${CMAKE_COMMAND}" -E copy_directory - "${CMAKE_SOURCE_DIR}/scripts" - "${RESOURCES_DEV_DIR}/scripts" - ) - - # copy JSDoc files beside the executable - add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + "${CMAKE_SOURCE_DIR}/scripts" + "${RESOURCES_DEV_DIR}/scripts" + # copy JSDoc files beside the executable COMMAND "${CMAKE_COMMAND}" -E copy_directory - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" - "${RESOURCES_DEV_DIR}/jsdoc" + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" + "${RESOURCES_DEV_DIR}/jsdoc" + # copy the resources files beside the executable + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "${RESOURCES_RCC}" + "${RESOURCES_DEV_DIR}" + # FIXME, the edit script code loads HTML from the scripts folder + # which in turn relies on CSS that refers to the fonts. In theory + # we should be able to modify the CSS to reference the QRC path to + # the ttf files, but doing so generates a CORS policy violation, + # so we have to retain a copy of the fonts outside of the resources binary + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${PROJECT_SOURCE_DIR}/resources/fonts" + "${RESOURCES_DEV_DIR}/fonts" ) # call the fixup_interface macro to add required bundling commands for installation @@ -356,13 +359,10 @@ else() COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${PROJECT_SOURCE_DIR}/resources/serverless/tutorial.json" "${RESOURCES_DEV_DIR}/serverless/tutorial.json" - ) - - # copy JSDoc files beside the executable - add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + # copy JSDoc files beside the executable COMMAND "${CMAKE_COMMAND}" -E copy_directory - "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" - "${INTERFACE_EXEC_DIR}/jsdoc" + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" + "${INTERFACE_EXEC_DIR}/jsdoc" ) # link target to external libraries diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cbe713127d..baafdd1fcd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -802,15 +802,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { qApp->setProperty(hifi::properties::APP_LOCAL_DATA_PATH, cacheDir); } - // FIXME fix the OSX installer to install the resources.rcc binary instead of resource files and remove - // this conditional exclusion -#if !defined(Q_OS_OSX) { -#if defined(Q_OS_ANDROID) - const QString resourcesBinaryFile = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources.rcc"; -#else - const QString resourcesBinaryFile = QCoreApplication::applicationDirPath() + "/resources.rcc"; -#endif + const QString resourcesBinaryFile = PathUtils::getRccPath(); if (!QFile::exists(resourcesBinaryFile)) { throw std::runtime_error("Unable to find primary resources"); } @@ -818,7 +811,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { throw std::runtime_error("Unable to load primary resources"); } } -#endif // Tell the plugin manager about our statically linked plugins auto pluginManager = PluginManager::getInstance(); diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index b5c76257ef..7fc94db6af 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -32,15 +32,13 @@ #include "shared/GlobalAppProperties.h" #include "SharedUtil.h" + // Format: AppName-PID-Timestamp // Example: ... QString TEMP_DIR_FORMAT { "%1-%2-%3" }; #if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) static bool USE_SOURCE_TREE_RESOURCES() { -#if defined(Q_OS_OSX) - return true; -#else static bool result = false; static std::once_flag once; std::call_once(once, [&] { @@ -48,10 +46,28 @@ static bool USE_SOURCE_TREE_RESOURCES() { result = QProcessEnvironment::systemEnvironment().contains(USE_SOURCE_TREE_RESOURCES_FLAG); }); return result; -#endif } #endif +const QString& PathUtils::getRccPath() { + static QString rccLocation; + static std::once_flag once; + std::call_once(once, [&] { + static const QString rccName{ "/resources.rcc" }; +#if defined(Q_OS_OSX) + char buffer[8192]; + uint32_t bufferSize = sizeof(buffer); + _NSGetExecutablePath(buffer, &bufferSize); + rccLocation = QDir::cleanPath(QFileInfo(buffer).dir().absoluteFilePath("../Resources")) + rccName; +#elif defined(Q_OS_ANDROID) + rccLocation = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + rccName; +#else + rccLocation = QCoreApplication::applicationDirPath() + rccName; +#endif + }); + return rccLocation; +} + #ifdef DEV_BUILD const QString& PathUtils::projectRootPath() { static QString sourceFolder; @@ -65,23 +81,9 @@ const QString& PathUtils::projectRootPath() { #endif const QString& PathUtils::resourcesPath() { - static QString staticResourcePath; + static QString staticResourcePath{ ":/" }; static std::once_flag once; std::call_once(once, [&]{ - -#if defined(Q_OS_OSX) - // FIXME fix the OSX installer to install the resources.rcc instead of the - // individual resource files - // FIXME the first call to fetch the resources location seems to return - // nothing for QCoreApplication::applicationDirPath() - char buffer[8192]; - uint32_t bufferSize = sizeof(buffer); - _NSGetExecutablePath(buffer, &bufferSize); - staticResourcePath = QDir::cleanPath(QFileInfo(buffer).dir().absoluteFilePath("../Resources")) + "/"; -#else - staticResourcePath = ":/"; -#endif - #if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) if (USE_SOURCE_TREE_RESOURCES()) { // For dev builds, optionally load content from the Git source tree @@ -90,21 +92,13 @@ const QString& PathUtils::resourcesPath() { #endif qDebug() << "Resource path resolved to " << staticResourcePath; }); - return staticResourcePath; } const QString& PathUtils::resourcesUrl() { - static QString staticResourcePath; + static QString staticResourcePath{ "qrc:///" }; static std::once_flag once; std::call_once(once, [&]{ - -#if defined(Q_OS_OSX) - staticResourcePath = QUrl::fromLocalFile(resourcesPath()).toString(); -#else - staticResourcePath = "qrc:///"; -#endif - #if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) if (USE_SOURCE_TREE_RESOURCES()) { // For dev builds, optionally load content from the Git source tree diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index fc933b6b8c..2247f4cc6a 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -37,6 +37,7 @@ class PathUtils : public QObject, public Dependency { Q_PROPERTY(QString resources READ resourcesPath CONSTANT) Q_PROPERTY(QUrl defaultScripts READ defaultScriptsLocation CONSTANT) public: + static const QString& getRccPath(); static const QString& resourcesUrl(); static QUrl resourcesUrl(const QString& relative); static const QString& resourcesPath(); From 32c21a1397b048d7aff3fde7a77aec40f966dcfc Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 7 Jun 2018 12:22:56 +1200 Subject: [PATCH 37/38] Fix particle property value reverting to old value when switch tabs --- scripts/system/particle_explorer/particleExplorerTool.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/system/particle_explorer/particleExplorerTool.js b/scripts/system/particle_explorer/particleExplorerTool.js index 5e5be9ead0..de2cb0bd8b 100644 --- a/scripts/system/particle_explorer/particleExplorerTool.js +++ b/scripts/system/particle_explorer/particleExplorerTool.js @@ -52,6 +52,13 @@ ParticleExplorerTool = function() { data.updatedSettings.emitOrientation = Quat.fromVec3Degrees(data.updatedSettings.emitOrientation); } Entities.editEntity(that.activeParticleEntity, data.updatedSettings); + + for (var key in data.updatedSettings) { + if (that.activeParticleProperties.hasOwnProperty(key)) { + that.activeParticleProperties[key] = data.updatedSettings[key]; + } + } + } else if (data.messageType === "page_loaded") { sendActiveParticleProperties(); } From e38059797dd8d9e8450e97eec8a3a85dc28f7c9d Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 6 Jun 2018 11:57:36 -0700 Subject: [PATCH 38/38] Update Oculus integration --- cmake/externals/LibOVR/CMakeLists.txt | 4 +- cmake/externals/LibOVR/LibOVRCMakeLists.txt | 2 +- .../oculus/src/OculusBaseDisplayPlugin.cpp | 66 ++--- plugins/oculus/src/OculusBaseDisplayPlugin.h | 19 +- .../oculus/src/OculusControllerManager.cpp | 164 +++++------ plugins/oculus/src/OculusControllerManager.h | 3 - plugins/oculus/src/OculusDisplayPlugin.cpp | 18 +- plugins/oculus/src/OculusHelpers.cpp | 274 ++++++++++-------- plugins/oculus/src/OculusHelpers.h | 166 +++++------ 9 files changed, 359 insertions(+), 357 deletions(-) diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index 97508be0c5..ed76f181e7 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -17,8 +17,8 @@ if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://static.oculus.com/sdk-downloads/1.11.0/Public/1486063832/ovr_sdk_win_1.11.0_public.zip - URL_MD5 ea484403757cbfdfa743b6577fb1f9d2 + URL http://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.26.0_public.zip + URL_MD5 06804ff9727b910dcd04a37c800053b5 CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" /CMakeLists.txt LOG_DOWNLOAD 1 diff --git a/cmake/externals/LibOVR/LibOVRCMakeLists.txt b/cmake/externals/LibOVR/LibOVRCMakeLists.txt index 556533f0c2..a52cff5463 100644 --- a/cmake/externals/LibOVR/LibOVRCMakeLists.txt +++ b/cmake/externals/LibOVR/LibOVRCMakeLists.txt @@ -4,7 +4,7 @@ project(LibOVR) include_directories(LibOVR/Include LibOVR/Src) file(GLOB HEADER_FILES LibOVR/Include/*.h) file(GLOB EXTRA_HEADER_FILES LibOVR/Include/Extras/*.h) -file(GLOB_RECURSE SOURCE_FILES LibOVR/Src/*.c LibOVR/Src/*.cpp) +file(GLOB_RECURSE SOURCE_FILES LibOVR/Shim/*.c LibOVR/Shim/*.cpp) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DOVR_BUILD_DEBUG") add_library(LibOVR STATIC ${SOURCE_FILES} ${HEADER_FILES} ${EXTRA_HEADER_FILES}) set_target_properties(LibOVR PROPERTIES DEBUG_POSTFIX "d") diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 7e337070d3..5aa1e45943 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -15,19 +15,25 @@ #include "OculusHelpers.h" +using namespace hifi; + void OculusBaseDisplayPlugin::resetSensors() { ovr_RecenterTrackingOrigin(_session); - _currentRenderFrameInfo.renderPose = glm::mat4(); // identity } bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) { - handleOVREvents(); - if (quitRequested()) { + ovrSessionStatus status{}; + if (!OVR_SUCCESS(ovr_GetSessionStatus(_session, &status))) { + qCWarning(oculusLog) << "Unable to fetch Oculus session status" << ovr::getError(); + return false; + } + + if (ovr::quitRequested(status)) { QMetaObject::invokeMethod(qApp, "quit"); return false; } - if (reorientRequested()) { + if (ovr::reorientRequested(status)) { emit resetSensorsRequested(); } @@ -35,18 +41,18 @@ bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) { _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds(); _currentRenderFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, frameIndex); auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrTrue); - _currentRenderFrameInfo.renderPose = toGlm(trackingState.HeadPose.ThePose); + _currentRenderFrameInfo.renderPose = ovr::toGlm(trackingState.HeadPose.ThePose); _currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose; std::array handPoses; // Make controller poses available to the presentation thread - ovr_for_each_hand([&](ovrHandType hand) { + ovr::for_each_hand([&](ovrHandType hand) { static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked | ovrStatus_PositionTracked; if (REQUIRED_HAND_STATUS != (trackingState.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) { return; } - auto correctedPose = ovrControllerPoseToHandPose(hand, trackingState.HandPoses[hand]); + auto correctedPose = ovr::toControllerPose(hand, trackingState.HandPoses[hand]); static const glm::quat HAND_TO_LASER_ROTATION = glm::rotation(Vectors::UNIT_Z, Vectors::UNIT_NEG_Y); handPoses[hand] = glm::translate(glm::mat4(), correctedPose.translation) * glm::mat4_cast(correctedPose.rotation * HAND_TO_LASER_ROTATION); }); @@ -58,7 +64,7 @@ bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) { } bool OculusBaseDisplayPlugin::isSupported() const { - return oculusAvailable(); + return ovr::available(); } glm::mat4 OculusBaseDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& baseProjection) const { @@ -71,7 +77,7 @@ glm::mat4 OculusBaseDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& ba ovrFovPort fovPort = _hmdDesc.DefaultEyeFov[eye]; ovrEyeRenderDesc& erd = ovr_GetRenderDesc(_session, ovrEye, fovPort); ovrMatrix4f ovrPerspectiveProjection = ovrMatrix4f_Projection(erd.Fov, baseNearClip, baseFarClip, ovrProjection_ClipRangeOpenGL); - return toGlm(ovrPerspectiveProjection); + return ovr::toGlm(ovrPerspectiveProjection); } else { return baseProjection; } @@ -85,7 +91,7 @@ glm::mat4 OculusBaseDisplayPlugin::getCullingProjection(const glm::mat4& basePro float baseFarClip = baseFrustum.getFarClip(); auto combinedFov = _eyeFovs[0]; combinedFov.LeftTan = combinedFov.RightTan = std::max(combinedFov.LeftTan, combinedFov.RightTan); - return toGlm(ovrMatrix4f_Projection(combinedFov, baseNearClip, baseFarClip, ovrProjection_ClipRangeOpenGL)); + return ovr::toGlm(ovrMatrix4f_Projection(combinedFov, baseNearClip, baseFarClip, ovrProjection_ClipRangeOpenGL)); } else { return baseProjection; } @@ -102,7 +108,7 @@ void OculusBaseDisplayPlugin::uncustomizeContext() { } bool OculusBaseDisplayPlugin::internalActivate() { - _session = acquireOculusSession(); + _session = ovr::acquireRenderSession(); if (!_session) { return false; } @@ -113,21 +119,21 @@ bool OculusBaseDisplayPlugin::internalActivate() { _viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; _ipd = 0; - ovr_for_each_eye([&](ovrEyeType eye) { + ovr::for_each_eye([&](ovrEyeType eye) { _eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye]; ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_session, eye, _eyeFovs[eye]); ovrMatrix4f ovrPerspectiveProjection = ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_ClipRangeOpenGL); - _eyeProjections[eye] = toGlm(ovrPerspectiveProjection); - _eyeOffsets[eye] = glm::translate(mat4(), toGlm(erd.HmdToEyeOffset)); - eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_session, eye, erd.Fov, 1.0f)); - _viewScaleDesc.HmdToEyeOffset[eye] = erd.HmdToEyeOffset; - _ipd += glm::abs(glm::length(toGlm(erd.HmdToEyeOffset))); + _eyeProjections[eye] = ovr::toGlm(ovrPerspectiveProjection); + _eyeOffsets[eye] = ovr::toGlm(erd.HmdToEyePose); + eyeSizes[eye] = ovr::toGlm(ovr_GetFovTextureSize(_session, eye, erd.Fov, 1.0f)); + _viewScaleDesc.HmdToEyePose[eye] = erd.HmdToEyePose; + _ipd += glm::abs(glm::length(ovr::toGlm(erd.HmdToEyePose.Position))); }); auto combinedFov = _eyeFovs[0]; combinedFov.LeftTan = combinedFov.RightTan = std::max(combinedFov.LeftTan, combinedFov.RightTan); - _cullingProjection = toGlm(ovrMatrix4f_Projection(combinedFov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_ClipRangeOpenGL)); + _cullingProjection = ovr::toGlm(ovrMatrix4f_Projection(combinedFov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_ClipRangeOpenGL)); _renderTargetSize = uvec2( eyeSizes[0].x + eyeSizes[1].x, @@ -136,7 +142,7 @@ bool OculusBaseDisplayPlugin::internalActivate() { memset(&_sceneLayer, 0, sizeof(ovrLayerEyeFov)); _sceneLayer.Header.Type = ovrLayerType_EyeFov; _sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; - ovr_for_each_eye([&](ovrEyeType eye) { + ovr::for_each_eye([&](ovrEyeType eye) { ovrFovPort & fov = _sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov; ovrSizei & size = _sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_session, eye, fov, 1.0f); _sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 }; @@ -150,28 +156,14 @@ bool OculusBaseDisplayPlugin::internalActivate() { void OculusBaseDisplayPlugin::internalDeactivate() { Parent::internalDeactivate(); + ovr::releaseRenderSession(_session); } -bool OculusBaseDisplayPlugin::activateStandBySession() { - if (!_session) { - _session = acquireOculusSession(); - } - return _session; -} -void OculusBaseDisplayPlugin::deactivateSession() { - // FIXME - // Switching to Qt 5.9 exposed a race condition or similar issue that caused a crash when putting on an Rift - // while already in VR mode. Commenting these out is a workaround. - //releaseOculusSession(); - //_session = nullptr; -} void OculusBaseDisplayPlugin::updatePresentPose() { + ovrTrackingState trackingState; _currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds(); _currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, 0); - auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse); - _currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose); + trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse); + _currentPresentFrameInfo.presentPose = ovr::toGlm(trackingState.HeadPose.ThePose); _currentPresentFrameInfo.renderPose = _currentPresentFrameInfo.presentPose; } - -OculusBaseDisplayPlugin::~OculusBaseDisplayPlugin() { -} diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index b3b99c0ad1..d70d14dc28 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -16,37 +16,32 @@ class OculusBaseDisplayPlugin : public HmdDisplayPlugin { using Parent = HmdDisplayPlugin; public: - ~OculusBaseDisplayPlugin(); bool isSupported() const override; + bool hasAsyncReprojection() const override { return true; } + bool getSupportsAutoSwitch() override final { return true; } glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override; glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override; - bool hasAsyncReprojection() const override { return true; } - - // Stereo specific methods void resetSensors() override final; bool beginFrameRender(uint32_t frameIndex) override; float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; } - bool getSupportsAutoSwitch() override final { return true; } - - + protected: void customizeContext() override; void uncustomizeContext() override; bool internalActivate() override; void internalDeactivate() override; - bool activateStandBySession() override; - void deactivateSession() override; void updatePresentPose() override; protected: - ovrSession _session { nullptr }; + ovrSession _session{ nullptr }; ovrGraphicsLuid _luid; - ovrEyeRenderDesc _eyeRenderDescs[2]; - ovrFovPort _eyeFovs[2]; + std::array _eyeRenderDescs; + std::array _eyeFovs; ovrHmdDesc _hmdDesc; ovrLayerEyeFov _sceneLayer; ovrViewScaleDesc _viewScaleDesc; + // ovrLayerEyeFovDepth _depthLayer; }; diff --git a/plugins/oculus/src/OculusControllerManager.cpp b/plugins/oculus/src/OculusControllerManager.cpp index 7a176d36fb..943af301a2 100644 --- a/plugins/oculus/src/OculusControllerManager.cpp +++ b/plugins/oculus/src/OculusControllerManager.cpp @@ -22,34 +22,23 @@ #include #include -#include +#include #include "OculusHelpers.h" -Q_DECLARE_LOGGING_CATEGORY(oculus) - - -static const char* MENU_PARENT = "Avatar"; -static const char* MENU_NAME = "Oculus Touch Controllers"; -static const char* MENU_PATH = "Avatar" ">" "Oculus Touch Controllers"; +using namespace hifi; const char* OculusControllerManager::NAME = "Oculus"; const quint64 LOST_TRACKING_DELAY = 3000000; bool OculusControllerManager::isSupported() const { - return oculusAvailable(); + return hifi::ovr::available(); } bool OculusControllerManager::activate() { InputPlugin::activate(); - if (!_session) { - _session = acquireOculusSession(); - } - Q_ASSERT(_session); - checkForConnectedDevices(); - return true; } @@ -58,33 +47,30 @@ void OculusControllerManager::checkForConnectedDevices() { return; } - unsigned int controllerConnected = ovr_GetConnectedControllerTypes(_session); + ovr::withSession([&] (ovrSession session) { + unsigned int controllerConnected = ovr_GetConnectedControllerTypes(session); - if (!_remote && (controllerConnected & ovrControllerType_Remote) == ovrControllerType_Remote) { - if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Remote, &_inputState))) { - auto userInputMapper = DependencyManager::get(); - _remote = std::make_shared(*this); - userInputMapper->registerDevice(_remote); + if (!_remote && (controllerConnected & ovrControllerType_Remote) == ovrControllerType_Remote) { + if (OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Remote, &_inputState))) { + auto userInputMapper = DependencyManager::get(); + _remote = std::make_shared(*this); + userInputMapper->registerDevice(_remote); + } } - } - if (!_touch && (controllerConnected & ovrControllerType_Touch) != 0) { - if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) { - auto userInputMapper = DependencyManager::get(); - _touch = std::make_shared(*this); - userInputMapper->registerDevice(_touch); + if (!_touch && (controllerConnected & ovrControllerType_Touch) != 0) { + if (OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Touch, &_inputState))) { + auto userInputMapper = DependencyManager::get(); + _touch = std::make_shared(*this); + userInputMapper->registerDevice(_touch); + } } - } + }); } void OculusControllerManager::deactivate() { InputPlugin::deactivate(); - if (_session) { - releaseOculusSession(); - _session = nullptr; - } - // unregister with UserInputMapper auto userInputMapper = DependencyManager::get(); if (_touch) { @@ -100,20 +86,30 @@ void OculusControllerManager::pluginUpdate(float deltaTime, const controller::In checkForConnectedDevices(); - if (_touch) { - if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) { - _touch->update(deltaTime, inputCalibrationData); - } else { - qCWarning(oculus) << "Unable to read Oculus touch input state"; + bool updateRemote = false, updateTouch = false; + + ovr::withSession([&](ovrSession session) { + if (_touch) { + updateTouch = OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Touch, &_inputState)); + if (!updateTouch) { + qCWarning(oculusLog) << "Unable to read Oculus touch input state" << ovr::getError(); + } } + if (_remote) { + updateRemote = OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Remote, &_inputState)); + if (!updateRemote) { + qCWarning(oculusLog) << "Unable to read Oculus remote input state" << ovr::getError(); + } + } + }); + + + if (_touch && updateTouch) { + _touch->update(deltaTime, inputCalibrationData); } - if (_remote) { - if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Remote, &_inputState))) { - _remote->update(deltaTime, inputCalibrationData); - } else { - qCWarning(oculus) << "Unable to read Oculus remote input state"; - } + if (_remote && updateRemote) { + _remote->update(deltaTime, inputCalibrationData); } } @@ -210,15 +206,6 @@ void OculusControllerManager::RemoteDevice::focusOutEvent() { _buttonPressedMap.clear(); } -bool OculusControllerManager::isHeadControllerMounted() const { - ovrSessionStatus status; - bool success = OVR_SUCCESS(ovr_GetSessionStatus(_session, &status)); - if (!success) { - return false; - } - return status.HmdMounted == ovrTrue; -} - void OculusControllerManager::TouchDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { _buttonPressedMap.clear(); @@ -226,10 +213,19 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, int numTrackedControllers = 0; quint64 currentTime = usecTimestampNow(); static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked | ovrStatus_PositionTracked; - auto tracking = ovr_GetTrackingState(_parent._session, 0, false); - ovr_for_each_hand([&](ovrHandType hand) { + bool hasInputFocus = ovr::hasInputFocus(); + auto tracking = ovr::getTrackingState(); // ovr_GetTrackingState(_parent._session, 0, false); + ovr::for_each_hand([&](ovrHandType hand) { ++numTrackedControllers; int controller = (hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND); + + // Disable hand tracking while in Oculus Dash (Dash renders it's own hands) + if (!hasInputFocus) { + _poseStateMap.erase(controller); + _poseStateMap[controller].valid = false; + return; + } + if (REQUIRED_HAND_STATUS == (tracking.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) { _poseStateMap.erase(controller); handlePose(deltaTime, inputCalibrationData, hand, tracking.HandPoses[hand]); @@ -253,7 +249,7 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, handleRotationForUntrackedHand(inputCalibrationData, hand, tracking.HandPoses[hand]); }); - if (_parent.isHeadControllerMounted()) { + if (ovr::hmdMounted()) { handleHeadPose(deltaTime, inputCalibrationData, tracking.HeadPose); } else { _poseStateMap[controller::HEAD].valid = false; @@ -311,7 +307,7 @@ void OculusControllerManager::TouchDevice::handlePose(float deltaTime, ovrHandType hand, const ovrPoseStatef& handPose) { auto poseId = hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND; auto& pose = _poseStateMap[poseId]; - pose = ovrControllerPoseToHandPose(hand, handPose); + pose = ovr::toControllerPose(hand, handPose); // transform into avatar frame glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; pose = pose.transform(controllerToAvatar); @@ -320,15 +316,15 @@ void OculusControllerManager::TouchDevice::handlePose(float deltaTime, void OculusControllerManager::TouchDevice::handleHeadPose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const ovrPoseStatef& headPose) { - glm::mat4 mat = createMatFromQuatAndPos(toGlm(headPose.ThePose.Orientation), - toGlm(headPose.ThePose.Position)); + glm::mat4 mat = createMatFromQuatAndPos(ovr::toGlm(headPose.ThePose.Orientation), + ovr::toGlm(headPose.ThePose.Position)); //perform a 180 flip to make the HMD face the +z instead of -z, beacuse the head faces +z glm::mat4 matYFlip = mat * Matrices::Y_180; controller::Pose pose(extractTranslation(matYFlip), glmExtractRotation(matYFlip), - toGlm(headPose.LinearVelocity), // XXX * matYFlip ? - toGlm(headPose.AngularVelocity)); + ovr::toGlm(headPose.LinearVelocity), // XXX * matYFlip ? + ovr::toGlm(headPose.AngularVelocity)); glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; glm::mat4 defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) * @@ -343,7 +339,7 @@ void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const auto poseId = (hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND); auto& pose = _poseStateMap[poseId]; auto lastHandPose = _lastControllerPose[poseId]; - pose = ovrControllerRotationToHandRotation(hand, handPose, lastHandPose); + pose = ovr::toControllerPose(hand, handPose, lastHandPose); glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; pose = pose.transform(controllerToAvatar); } @@ -351,36 +347,40 @@ void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { Locker locker(_lock); bool toReturn = true; - if (hand == controller::BOTH || hand == controller::LEFT) { - if (strength == 0.0f) { - _leftHapticStrength = 0.0f; - _leftHapticDuration = 0.0f; - } else { - _leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength; - if (ovr_SetControllerVibration(_parent._session, ovrControllerType_LTouch, 1.0f, _leftHapticStrength) != ovrSuccess) { - toReturn = false; + ovr::withSession([&](ovrSession session) { + if (hand == controller::BOTH || hand == controller::LEFT) { + if (strength == 0.0f) { + _leftHapticStrength = 0.0f; + _leftHapticDuration = 0.0f; + } else { + _leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength; + if (ovr_SetControllerVibration(session, ovrControllerType_LTouch, 1.0f, _leftHapticStrength) != ovrSuccess) { + toReturn = false; + } + _leftHapticDuration = std::max(duration, _leftHapticDuration); } - _leftHapticDuration = std::max(duration, _leftHapticDuration); } - } - if (hand == controller::BOTH || hand == controller::RIGHT) { - if (strength == 0.0f) { - _rightHapticStrength = 0.0f; - _rightHapticDuration = 0.0f; - } else { - _rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength; - if (ovr_SetControllerVibration(_parent._session, ovrControllerType_RTouch, 1.0f, _rightHapticStrength) != ovrSuccess) { - toReturn = false; + if (hand == controller::BOTH || hand == controller::RIGHT) { + if (strength == 0.0f) { + _rightHapticStrength = 0.0f; + _rightHapticDuration = 0.0f; + } else { + _rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength; + if (ovr_SetControllerVibration(session, ovrControllerType_RTouch, 1.0f, _rightHapticStrength) != ovrSuccess) { + toReturn = false; + } + _rightHapticDuration = std::max(duration, _rightHapticDuration); } - _rightHapticDuration = std::max(duration, _rightHapticDuration); } - } + }); return toReturn; } void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) { auto handType = (leftHand ? ovrControllerType_LTouch : ovrControllerType_RTouch); - ovr_SetControllerVibration(_parent._session, handType, 0.0f, 0.0f); + ovr::withSession([&](ovrSession session) { + ovr_SetControllerVibration(session, handType, 0.0f, 0.0f); + }); } /**jsdoc diff --git a/plugins/oculus/src/OculusControllerManager.h b/plugins/oculus/src/OculusControllerManager.h index 69187f94a6..b08d54babe 100644 --- a/plugins/oculus/src/OculusControllerManager.h +++ b/plugins/oculus/src/OculusControllerManager.h @@ -26,10 +26,8 @@ public: // Plugin functions bool isSupported() const override; const QString getName() const override { return NAME; } - bool isHandController() const override { return _touch != nullptr; } bool isHeadController() const override { return true; } - bool isHeadControllerMounted() const; QStringList getSubdeviceNames() override; bool activate() override; @@ -105,7 +103,6 @@ private: void checkForConnectedDevices(); - ovrSession _session { nullptr }; ovrInputState _inputState {}; RemoteDevice::Pointer _remote; TouchDevice::Pointer _touch; diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index 0fd8467e38..308652cacd 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -19,6 +19,8 @@ #include "OculusHelpers.h" +using namespace hifi; + const char* OculusDisplayPlugin::NAME { "Oculus Rift" }; static ovrPerfHudMode currentDebugMode = ovrPerfHud_Off; @@ -63,7 +65,7 @@ void OculusDisplayPlugin::cycleDebugOutput() { void OculusDisplayPlugin::customizeContext() { Parent::customizeContext(); - _outputFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OculusOutput", gpu::Element::COLOR_SRGBA_32, _renderTargetSize.x, _renderTargetSize.y)); + _outputFramebuffer.reset(gpu::Framebuffer::create("OculusOutput", gpu::Element::COLOR_SRGBA_32, _renderTargetSize.x, _renderTargetSize.y)); ovrTextureSwapChainDesc desc = { }; desc.Type = ovrTexture_2D; desc.ArraySize = 1; @@ -76,14 +78,14 @@ void OculusDisplayPlugin::customizeContext() { ovrResult result = ovr_CreateTextureSwapChainGL(_session, &desc, &_textureSwapChain); if (!OVR_SUCCESS(result)) { - logCritical("Failed to create swap textures"); + qCritical(oculusLog) << "Failed to create swap textures" << ovr::getError(); return; } int length = 0; result = ovr_GetTextureSwapChainLength(_session, _textureSwapChain, &length); if (!OVR_SUCCESS(result) || !length) { - logCritical("Unable to count swap chain textures"); + qCritical(oculusLog) << "Unable to count swap chain textures" << ovr::getError(); return; } for (int i = 0; i < length; ++i) { @@ -164,8 +166,8 @@ void OculusDisplayPlugin::hmdPresent() { auto result = ovr_CommitTextureSwapChain(_session, _textureSwapChain); Q_ASSERT(OVR_SUCCESS(result)); _sceneLayer.SensorSampleTime = _currentPresentFrameInfo.sensorSampleTime; - _sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose); - _sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose); + _sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovr::poseFromGlm(_currentPresentFrameInfo.renderPose); + _sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovr::poseFromGlm(_currentPresentFrameInfo.renderPose); auto submitStart = usecTimestampNow(); uint64_t nonSubmitInterval = 0; @@ -192,7 +194,7 @@ void OculusDisplayPlugin::hmdPresent() { } if (!OVR_SUCCESS(result)) { - logWarning("Failed to present"); + qWarning(oculusLog) << "Failed to present" << ovr::getError(); } static int compositorDroppedFrames = 0; @@ -234,9 +236,7 @@ QJsonObject OculusDisplayPlugin::getHardwareStats() const { } bool OculusDisplayPlugin::isHmdMounted() const { - ovrSessionStatus status; - return (OVR_SUCCESS(ovr_GetSessionStatus(_session, &status)) && - (ovrFalse != status.HmdMounted)); + return ovr::hmdMounted(); } QString OculusDisplayPlugin::getPreferredAudioInDevice() const { diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index 4632c8ab76..511984c657 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -24,41 +24,17 @@ #include Q_LOGGING_CATEGORY(displayplugins, "hifi.plugins.display") -Q_LOGGING_CATEGORY(oculus, "hifi.plugins.display.oculus") - -static std::atomic refCount { 0 }; -static ovrSession session { nullptr }; - -static bool _quitRequested { false }; -static bool _reorientRequested { false }; - -inline ovrErrorInfo getError() { - ovrErrorInfo error; - ovr_GetLastErrorInfo(&error); - return error; -} - -void logWarning(const char* what) { - qWarning(oculus) << what << ":" << getError().ErrorString; -} - -void logCritical(const char* what) { - std::string error("[oculus] "); - error += what; - error += ": "; - error += getError().ErrorString; - qCritical(error.c_str()); -} +Q_LOGGING_CATEGORY(oculusLog, "hifi.plugins.display.oculus") +using namespace hifi; static wchar_t* REQUIRED_OCULUS_DLL = L"LibOVRRT64_1.dll"; static wchar_t FOUND_PATH[MAX_PATH]; -bool oculusAvailable() { +bool ovr::available() { static std::once_flag once; - static bool result { false }; + static bool result{ false }; std::call_once(once, [&] { - static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR"); static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); if (enableDebugOpenVR) { @@ -81,113 +57,107 @@ bool oculusAvailable() { return result; } -ovrSession acquireOculusSession() { - if (!session && !oculusAvailable()) { - qCDebug(oculus) << "oculus: no runtime or HMD present"; +class ovrImpl { + using Mutex = std::mutex; + using Lock = std::unique_lock; + std::mutex mutex; + ovrSession session{ nullptr }; + size_t renderCount{ 0 }; + +private: + void setupSession(bool render) { + if (session) { + return; + } + ovrInitParams initParams{ ovrInit_RequestVersion | ovrInit_FocusAware, OVR_MINOR_VERSION, nullptr, 0, 0 }; + if (render) { + initParams.Flags |= ovrInit_MixedRendering; + } else { + initParams.Flags |= ovrInit_Invisible; + } + + if (!OVR_SUCCESS(ovr_Initialize(&initParams))) { + qCWarning(oculusLog) << "Failed to initialze Oculus SDK" << ovr::getError(); + return; + } + + ovrGraphicsLuid luid; + if (!OVR_SUCCESS(ovr_Create(&session, &luid))) { + qCWarning(oculusLog) << "Failed to acquire Oculus session" << ovr::getError(); + return; + } + } + + void releaseSession() { + if (!session) { + return; + } + ovr_Destroy(session); + session = nullptr; + ovr_Shutdown(); + } + +public: + void withSession(const std::function& f) { + Lock lock(mutex); + if (!session) { + setupSession(false); + } + f(session); + } + + ovrSession acquireRenderSession() { + Lock lock(mutex); + if (renderCount++ == 0) { + releaseSession(); + setupSession(true); + } return session; } - if (!session) { - ovrInitParams initParams { - ovrInit_RequestVersion | ovrInit_MixedRendering, OVR_MINOR_VERSION, nullptr, 0, 0 - }; - - if (!OVR_SUCCESS(ovr_Initialize(&initParams))) { - logWarning("Failed to initialize Oculus SDK"); - return session; - } - -#ifdef OCULUS_APP_ID - if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) { - if (ovr_PlatformInitializeWindows(OCULUS_APP_ID) != ovrPlatformInitialize_Success) { - // we were unable to initialize the platform for entitlement check - fail the check - _quitRequested = true; - } else { - qCDebug(oculus) << "Performing Oculus Platform entitlement check"; - ovr_Entitlement_GetIsViewerEntitled(); - } - } -#endif - - Q_ASSERT(0 == refCount); - ovrGraphicsLuid luid; - if (!OVR_SUCCESS(ovr_Create(&session, &luid))) { - logWarning("Failed to acquire Oculus session"); - return session; + void releaseRenderSession(ovrSession session) { + Lock lock(mutex); + if (--renderCount == 0) { + releaseSession(); } } +} _ovr; - ++refCount; - return session; +ovrSession ovr::acquireRenderSession() { + return _ovr.acquireRenderSession(); } -void releaseOculusSession() { - Q_ASSERT(refCount > 0 && session); - // HACK the Oculus runtime doesn't seem to play well with repeated shutdown / restart. - // So for now we'll just hold on to the session -#if 0 - if (!--refCount) { - qCDebug(oculus) << "oculus: zero refcount, shutdown SDK and session"; - ovr_Destroy(session); - ovr_Shutdown(); - session = nullptr; - } -#endif +void ovr::releaseRenderSession(ovrSession session) { + _ovr.releaseRenderSession(session); } -void handleOVREvents() { - if (!session) { - return; - } +void ovr::withSession(const std::function& f) { + _ovr.withSession(f); +} - ovrSessionStatus status; - if (!OVR_SUCCESS(ovr_GetSessionStatus(session, &status))) { - return; - } - - _quitRequested = status.ShouldQuit; - _reorientRequested = status.ShouldRecenter; - - #ifdef OCULUS_APP_ID - - if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) { - // pop messages to see if we got a return for an entitlement check - ovrMessageHandle message = ovr_PopMessage(); - - while (message) { - switch (ovr_Message_GetType(message)) { - case ovrMessage_Entitlement_GetIsViewerEntitled: { - if (!ovr_Message_IsError(message)) { - // this viewer is entitled, no need to flag anything - qCDebug(oculus) << "Oculus Platform entitlement check succeeded, proceeding normally"; - } else { - // we failed the entitlement check, set our flag so the app can stop - qCDebug(oculus) << "Oculus Platform entitlement check failed, app will now quit" << OCULUS_APP_ID; - _quitRequested = true; - } - } - } - - // free the message handle to cleanup and not leak - ovr_FreeMessage(message); - - // pop the next message to check, if there is one - message = ovr_PopMessage(); +ovrSessionStatus ovr::getStatus() { + ovrSessionStatus status{}; + withSession([&](ovrSession session) { + if (!OVR_SUCCESS(ovr_GetSessionStatus(session, &status))) { + qCWarning(oculusLog) << "Failed to get session status" << ovr::getError(); } - } -#endif + }); + return status; } -bool quitRequested() { - return _quitRequested; -} -bool reorientRequested() { - return _reorientRequested; +ovrTrackingState ovr::getTrackingState() { + ovrTrackingState result{}; + withSession([&](ovrSession session) { result = ovr_GetTrackingState(session, 0, ovrFalse); }); + return result; } -controller::Pose ovrControllerPoseToHandPose( - ovrHandType hand, - const ovrPoseStatef& handPose) { +QString ovr::getError() { + static ovrErrorInfo error; + ovr_GetLastErrorInfo(&error); + return QString(error.ErrorString); +} + +controller::Pose hifi::ovr::toControllerPose(ovrHandType hand, const ovrPoseStatef& handPose) { // When the sensor-to-world rotation is identity the coordinate axes look like this: // // user @@ -247,9 +217,8 @@ controller::Pose ovrControllerPoseToHandPose( static const glm::quat rightRotationOffset = glm::inverse(rightQuarterZ) * touchToHand; static const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches - static const glm::vec3 CONTROLLER_OFFSET = glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f, - -CONTROLLER_LENGTH_OFFSET / 2.0f, - CONTROLLER_LENGTH_OFFSET * 1.5f); + static const glm::vec3 CONTROLLER_OFFSET = + glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f, -CONTROLLER_LENGTH_OFFSET / 2.0f, CONTROLLER_LENGTH_OFFSET * 1.5f); static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET; static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET; @@ -268,12 +237,13 @@ controller::Pose ovrControllerPoseToHandPose( return pose; } -controller::Pose ovrControllerRotationToHandRotation(ovrHandType hand, const ovrPoseStatef& handPose, - const ovrPoseStatef& lastHandPose) { +controller::Pose hifi::ovr::toControllerPose(ovrHandType hand, + const ovrPoseStatef& handPose, + const ovrPoseStatef& lastHandPose) { static const glm::quat yFlip = glm::angleAxis(PI, Vectors::UNIT_Y); static const glm::quat quarterX = glm::angleAxis(PI_OVER_TWO, Vectors::UNIT_X); static const glm::quat touchToHand = yFlip * quarterX; - + static const glm::quat leftQuarterZ = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_Z); static const glm::quat rightQuarterZ = glm::angleAxis(PI_OVER_TWO, Vectors::UNIT_Z); @@ -281,9 +251,8 @@ controller::Pose ovrControllerRotationToHandRotation(ovrHandType hand, const ovr static const glm::quat rightRotationOffset = glm::inverse(rightQuarterZ) * touchToHand; static const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches - static const glm::vec3 CONTROLLER_OFFSET = glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f, - -CONTROLLER_LENGTH_OFFSET / 2.0f, - CONTROLLER_LENGTH_OFFSET * 1.5f); + static const glm::vec3 CONTROLLER_OFFSET = + glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f, -CONTROLLER_LENGTH_OFFSET / 2.0f, CONTROLLER_LENGTH_OFFSET * 1.5f); static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET; static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET; @@ -301,3 +270,52 @@ controller::Pose ovrControllerRotationToHandRotation(ovrHandType hand, const ovr pose.valid = true; return pose; } + +// FIXME These should be moved to an oculusPlatform plugin, they don't interact with the controller or session state +#if 0 +void handleOVREvents() { + updateSessionStatus(true); + +#ifdef OCULUS_APP_ID + + if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) { + // pop messages to see if we got a return for an entitlement check + ovrMessageHandle message = ovr_PopMessage(); + + while (message) { + switch (ovr_Message_GetType(message)) { + case ovrMessage_Entitlement_GetIsViewerEntitled: + { + if (!ovr_Message_IsError(message)) { + // this viewer is entitled, no need to flag anything + qCDebug(oculus) << "Oculus Platform entitlement check succeeded, proceeding normally"; + } else { + // we failed the entitlement check, set our flag so the app can stop + qCDebug(oculus) << "Oculus Platform entitlement check failed, app will now quit" << OCULUS_APP_ID; + _quitRequested = true; + } + } + } + + // free the message handle to cleanup and not leak + ovr_FreeMessage(message); + + // pop the next message to check, if there is one + message = ovr_PopMessage(); + } + } +#endif +} + +#ifdef OCULUS_APP_ID +if (qApp->property(hifi::properties::OCULUS_STORE).toBool()) { + if (ovr_PlatformInitializeWindows(OCULUS_APP_ID) != ovrPlatformInitialize_Success) { + // we were unable to initialize the platform for entitlement check - fail the check + _quitRequested = true; + } else { + qCDebug(oculusLog) << "Performing Oculus Platform entitlement check"; + ovr_Entitlement_GetIsViewerEntitled(); + } +} +#endif +#endif diff --git a/plugins/oculus/src/OculusHelpers.h b/plugins/oculus/src/OculusHelpers.h index 5743f8576b..6588d9754b 100644 --- a/plugins/oculus/src/OculusHelpers.h +++ b/plugins/oculus/src/OculusHelpers.h @@ -7,10 +7,9 @@ // #pragma once - #include -#include +#include #include #include #include @@ -18,106 +17,107 @@ #include Q_DECLARE_LOGGING_CATEGORY(displayplugins) -Q_DECLARE_LOGGING_CATEGORY(oculus) +Q_DECLARE_LOGGING_CATEGORY(oculusLog) -void logWarning(const char* what); -void logCritical(const char* what); -bool oculusAvailable(); -ovrSession acquireOculusSession(); -void releaseOculusSession(); +namespace hifi { + +struct ovr { + static bool available(); + static ovrSession acquireRenderSession(); + static void releaseRenderSession(ovrSession session); + static void withSession(const std::function& f); + static ovrSessionStatus getStatus(); + static ovrTrackingState getTrackingState(); + static QString getError(); -void handleOVREvents(); -bool quitRequested(); -bool reorientRequested(); + static inline bool quitRequested() { return quitRequested(getStatus()); } + static inline bool reorientRequested() { return reorientRequested(getStatus()); } + static inline bool hmdMounted() { return hmdMounted(getStatus()); } + static inline bool hasInputFocus() { return hasInputFocus(getStatus()); } -// Convenience method for looping over each eye with a lambda -template -inline void ovr_for_each_eye(Function function) { - for (ovrEyeType eye = ovrEyeType::ovrEye_Left; - eye < ovrEyeType::ovrEye_Count; - eye = static_cast(eye + 1)) { - function(eye); + static inline bool quitRequested(const ovrSessionStatus& status) { return status.ShouldQuit != ovrFalse; } + static inline bool reorientRequested(const ovrSessionStatus& status) { return status.ShouldRecenter != ovrFalse; } + static inline bool hmdMounted(const ovrSessionStatus& status) { return status.HmdMounted != ovrFalse; } + static inline bool hasInputFocus(const ovrSessionStatus& status) { return status.HasInputFocus != ovrFalse; } + + // Convenience method for looping over each eye with a lambda + static inline void for_each_eye(const std::function& f) { + for (ovrEyeType eye = ovrEye_Left; eye < ovrEye_Count; eye = static_cast(eye + 1)) { + f(eye); + } } -} -template -inline void ovr_for_each_hand(Function function) { - for (ovrHandType hand = ovrHandType::ovrHand_Left; - hand <= ovrHandType::ovrHand_Right; - hand = static_cast(hand + 1)) { - function(hand); + static inline void for_each_hand(const std::function& f) { + for (ovrHandType hand = ovrHand_Left; hand < ovrHand_Count; hand = static_cast(hand + 1)) { + f(hand); + } } -} + static inline glm::mat4 toGlm(const ovrMatrix4f& om) { + return glm::transpose(glm::make_mat4(&om.M[0][0])); + } + static inline glm::mat4 toGlm(const ovrFovPort& fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) { + return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true)); + } + static inline glm::vec3 toGlm(const ovrVector3f& ov) { + return glm::make_vec3(&ov.x); + } + static inline glm::vec2 toGlm(const ovrVector2f& ov) { + return glm::make_vec2(&ov.x); + } -inline glm::mat4 toGlm(const ovrMatrix4f & om) { - return glm::transpose(glm::make_mat4(&om.M[0][0])); -} + static inline glm::uvec2 toGlm(const ovrSizei& ov) { + return glm::uvec2(ov.w, ov.h); + } -inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) { - return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true)); -} + static inline glm::quat toGlm(const ovrQuatf& oq) { + return glm::make_quat(&oq.x); + } -inline glm::vec3 toGlm(const ovrVector3f & ov) { - return glm::make_vec3(&ov.x); -} + static inline glm::mat4 toGlm(const ovrPosef& op) { + glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation)); + glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position)); + return translation * orientation; + } -inline glm::vec2 toGlm(const ovrVector2f & ov) { - return glm::make_vec2(&ov.x); -} + static inline ovrMatrix4f fromGlm(const glm::mat4& m) { + ovrMatrix4f result; + glm::mat4 transposed(glm::transpose(m)); + memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16); + return result; + } -inline glm::uvec2 toGlm(const ovrSizei & ov) { - return glm::uvec2(ov.w, ov.h); -} + static inline ovrVector3f fromGlm(const glm::vec3& v) { + return { v.x, v.y, v.z }; + } -inline glm::quat toGlm(const ovrQuatf & oq) { - return glm::make_quat(&oq.x); -} + static inline ovrVector2f fromGlm(const glm::vec2& v) { + return { v.x, v.y }; + } -inline glm::mat4 toGlm(const ovrPosef & op) { - glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation)); - glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position)); - return translation * orientation; -} + static inline ovrSizei fromGlm(const glm::uvec2& v) { + return { (int)v.x, (int)v.y }; + } -inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) { - ovrMatrix4f result; - glm::mat4 transposed(glm::transpose(m)); - memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16); - return result; -} + static inline ovrQuatf fromGlm(const glm::quat& q) { + return { q.x, q.y, q.z, q.w }; + } -inline ovrVector3f ovrFromGlm(const glm::vec3 & v) { - return{ v.x, v.y, v.z }; -} + static inline ovrPosef poseFromGlm(const glm::mat4& m) { + glm::vec3 translation = glm::vec3(m[3]) / m[3].w; + glm::quat orientation = glm::quat_cast(m); + ovrPosef result; + result.Orientation = fromGlm(orientation); + result.Position = fromGlm(translation); + return result; + } -inline ovrVector2f ovrFromGlm(const glm::vec2 & v) { - return{ v.x, v.y }; -} + static controller::Pose toControllerPose(ovrHandType hand, const ovrPoseStatef& handPose); + static controller::Pose toControllerPose(ovrHandType hand, const ovrPoseStatef& handPose, const ovrPoseStatef& lastHandPose); -inline ovrSizei ovrFromGlm(const glm::uvec2 & v) { - return{ (int)v.x, (int)v.y }; -} +}; -inline ovrQuatf ovrFromGlm(const glm::quat & q) { - return{ q.x, q.y, q.z, q.w }; -} - -inline ovrPosef ovrPoseFromGlm(const glm::mat4 & m) { - glm::vec3 translation = glm::vec3(m[3]) / m[3].w; - glm::quat orientation = glm::quat_cast(m); - ovrPosef result; - result.Orientation = ovrFromGlm(orientation); - result.Position = ovrFromGlm(translation); - return result; -} - -controller::Pose ovrControllerPoseToHandPose( - ovrHandType hand, - const ovrPoseStatef& handPose); - -controller::Pose ovrControllerRotationToHandRotation(ovrHandType hand, - const ovrPoseStatef& handPose, const ovrPoseStatef& lastHandPose); +} // namespace hifi