Merge remote-tracking branch 'upstream/master' into breakpad_android

This commit is contained in:
Gabriel Calero 2018-06-07 19:00:16 -03:00
commit 3911a93a38
67 changed files with 1132 additions and 721 deletions

View file

@ -66,19 +66,19 @@ ext {
def baseFolder = new File(HIFI_ANDROID_PRECOMPILED) def baseFolder = new File(HIFI_ANDROID_PRECOMPILED)
def appDir = new File(projectDir, 'app') def appDir = new File(projectDir, 'app')
def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a') def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a')
def baseUrl = '' def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/'
def qtFile='https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_linux_armv8-libcpp_openssl.tgz' def qtFile='qt-5.9.3_linux_armv8-libcpp_openssl.tgz'
def qtChecksum='04599670ccca84bd2b15f6915568eb2d' def qtChecksum='04599670ccca84bd2b15f6915568eb2d'
def qtVersionId='PeoqzN31n.YvLfs9JE2SgHgZ4.IaKAlt' def qtVersionId='8QbCma4ryEPgBYn_8kgYgB10IvNx9I1W'
if (Os.isFamily(Os.FAMILY_MAC)) { if (Os.isFamily(Os.FAMILY_MAC)) {
qtFile = 'https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_osx_armv8-libcpp_openssl.tgz' qtFile = 'qt-5.9.3_osx_armv8-libcpp_openssl.tgz'
qtChecksum='4b02de9d67d6bfb202355a808d2d9c59' qtChecksum='4b02de9d67d6bfb202355a808d2d9c59'
qtVersionId='HygCmtMLPYioyil0DfXckGVzhw2SXZA9' qtVersionId='2gfgoYCggJGyXxKiazaPGsMs1Gn9j4og'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) { } else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
qtFile = 'https://hifi-public.s3.amazonaws.com/austin/android/qt-5.9.3_win_armv8-libcpp_openssl.tgz' qtFile = 'qt-5.9.3_win_armv8-libcpp_openssl.tgz'
qtChecksum='c3e25db64002d0f43cf565e0ef708911' qtChecksum='c3e25db64002d0f43cf565e0ef708911'
qtVersionId='HeVObSVLCBoc7yY7He1oBMvPIH0VkClT' qtVersionId='xKIteC6HO0xrmcWeMmhQcmKyPEsnUrcZ'
} }
def packages = [ def packages = [
@ -88,79 +88,84 @@ def packages = [
checksum: qtChecksum, checksum: qtChecksum,
], ],
bullet: [ bullet: [
file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/bullet-2.88_armv8-libcpp.tgz', file: 'bullet-2.88_armv8-libcpp.tgz',
versionId: 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg', versionId: 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg',
checksum: '81642779ccb110f8c7338e8739ac38a0', checksum: '81642779ccb110f8c7338e8739ac38a0',
], ],
draco: [ draco: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/draco_armv8-libcpp.tgz', file: 'draco_armv8-libcpp.tgz',
versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m', versionId: '3.B.uBj31kWlgND3_R2xwQzT_TP6Dz_8',
checksum: '617a80d213a5ec69fbfa21a1f2f738cd', checksum: '617a80d213a5ec69fbfa21a1f2f738cd',
], ],
glad: [ glad: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/glad_armv8-libcpp.zip', file: 'glad_armv8-libcpp.zip',
versionId: 'Q9szthzeye8fFyAA.cY26Lgn2B8kezEE', versionId: 'r5Zran.JSCtvrrB6Q4KaqfIoALPw3lYY',
checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449', checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449',
], ],
glm: [ glm: [
file: 'https://hifi-public.s3.amazonaws.com/dependencies/android/glm-0.9.8.5-patched.tgz', file: 'glm-0.9.8.5-patched.tgz',
versionId: 'cskfMoJrFlAeqI3WPxemyO_Cxt7rT9EJ', versionId: 'cskfMoJrFlAeqI3WPxemyO_Cxt7rT9EJ',
checksum: '067b5fe16b220b5b1a1039ba51b062ae', checksum: '067b5fe16b220b5b1a1039ba51b062ae',
], ],
gvr: [ gvr: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/gvrsdk_v1.101.0.tgz', file: 'gvrsdk_v1.101.0.tgz',
versionId: 'UTberAIFraEfF9IVjoV66u1DTPTopgeY', versionId: 'nqBV_j81Uc31rC7bKIrlya_Hah4v3y5r',
checksum: '57fd02baa069176ba18597a29b6b4fc7', checksum: '57fd02baa069176ba18597a29b6b4fc7',
], ],
nvtt: [ nvtt: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/nvtt_armv8-libcpp.zip', file: 'nvtt_armv8-libcpp.zip',
versionId: 'vLqrqThvpq4gp75BHMAqO6HhfTXaa0An', versionId: 'lmkBVR5t4UF1UUwMwEirnk9H_8Nt90IO',
checksum: 'eb46d0b683e66987190ed124aabf8910', checksum: 'eb46d0b683e66987190ed124aabf8910',
sharedLibFolder: 'lib', sharedLibFolder: 'lib',
includeLibs: ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'], includeLibs: ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'],
], ],
openssl: [ openssl: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/openssl-1.1.0g_armv8.tgz', file: 'openssl-1.1.0g_armv8.tgz',
versionId: 'DmahmSGFS4ltpHyTdyQvv35WOeUOiib9', versionId: 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW',
checksum: 'cabb681fbccd79594f65fcc266e02f32', checksum: 'cabb681fbccd79594f65fcc266e02f32',
], ],
polyvox: [ polyvox: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/polyvox_armv8-libcpp.tgz', file: 'polyvox_armv8-libcpp.tgz',
versionId: 'LDJtzMTvdm4SAc2KYg8Cg6uwWk4Vq3e3', versionId: 'A2kbKiNhpIenGq23bKRRzg7IMAI5BI92',
checksum: '349ad5b72aaf2749ca95d847e60c5314', checksum: 'dba88b3a098747af4bb169e9eb9af57e',
sharedLibFolder: 'lib', sharedLibFolder: 'lib',
includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'], includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'],
], ],
tbb: [ tbb: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/tbb-2018_U1_armv8_libcpp.tgz', file: 'tbb-2018_U1_armv8_libcpp.tgz',
versionId: 'YZliDD8.Menh1IVXKEuLPeO3xAjJ1UdF', versionId: 'mrRbWnv4O4evcM1quRH43RJqimlRtaKB',
checksum: '20768f298f53b195e71b414b0ae240c4', checksum: '20768f298f53b195e71b414b0ae240c4',
sharedLibFolder: 'lib/release', sharedLibFolder: 'lib/release',
includeLibs: ['libtbb.so', 'libtbbmalloc.so'], includeLibs: ['libtbb.so', 'libtbbmalloc.so'],
], ],
hifiAC: [ hifiAC: [
file: 'https://hifi-public.s3.amazonaws.com/austin/android/libplugins_libhifiCodec.zip', file: 'libplugins_libhifiCodec.zip',
versionId: 'mzKhsRCgVmloqq5bvE.0IwYK1NjGQc_G', versionId: 'i31pW.qNbvFOXRxbyiJUxg3sphaFNmZU',
checksum: '9412a8e12c88a4096c1fc843bb9fe52d', checksum: '9412a8e12c88a4096c1fc843bb9fe52d',
sharedLibFolder: '', sharedLibFolder: '',
includeLibs: ['libplugins_libhifiCodec.so'] includeLibs: ['libplugins_libhifiCodec.so']
],
etc2comp: [
file: 'etc2comp-patched-armv8-libcpp.tgz',
versionId: 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU',
checksum: '14b02795d774457a33bbc60e00a786bc'
] ]
] ]
def scribeLocalFile='scribe' + EXEC_SUFFIX def scribeLocalFile='scribe' + EXEC_SUFFIX
def scribeFile='https://hifi-public.s3.amazonaws.com/austin/android/scribe_linux_x86_64' def scribeFile='scribe_linux_x86_64'
def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6' def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6'
def scribeVersion='wgpf4dB2Ltzg4Lb2jJ4nPFsHoDkmK_OO' def scribeVersion='u_iTrJDaE95i2abTPXOpPZckGBIim53G'
if (Os.isFamily(Os.FAMILY_MAC)) { if (Os.isFamily(Os.FAMILY_MAC)) {
scribeFile = 'https://hifi-public.s3.amazonaws.com/austin/android/scribe_osx_x86_64' scribeFile = 'scribe_osx_x86_64'
scribeChecksum='72db9d32d4e1e50add755570ac5eb749' scribeChecksum='72db9d32d4e1e50add755570ac5eb749'
scribeVersion='o_NbPrktzEYtBkQf3Tn7zc1nZWzM52w6' scribeVersion='DAW0DmnjCRib4MD8x93bgc2Z2MpPojZC'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) { } else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
scribeFile = 'https://hifi-public.s3.amazonaws.com/austin/android/scribe_win32_x86_64.exe' scribeFile = 'scribe_win32_x86_64.exe'
scribeChecksum='678e43d290c90fda670c6fefe038a06d' scribeChecksum='678e43d290c90fda670c6fefe038a06d'
scribeVersion='GCCJxlmd2irvNOFWfZR0U1UCLHndHQrC' scribeVersion='PuullrA_bPlO9kXZRt8rLe536X1UI.m7'
} }
def options = [ 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 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 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 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) { } task verifyDependencyDownloads(dependsOn: downloadDependencies) { }
verifyDependencyDownloads.dependsOn verifyQt verifyDependencyDownloads.dependsOn verifyQt
@ -371,6 +377,7 @@ verifyDependencyDownloads.dependsOn verifyOpenSSL
verifyDependencyDownloads.dependsOn verifyPolyvox verifyDependencyDownloads.dependsOn verifyPolyvox
verifyDependencyDownloads.dependsOn verifyTBB verifyDependencyDownloads.dependsOn verifyTBB
verifyDependencyDownloads.dependsOn verifyHifiAC verifyDependencyDownloads.dependsOn verifyHifiAC
verifyDependencyDownloads.dependsOn verifyEtc2Comp
task extractDependencies(dependsOn: verifyDependencyDownloads) { task extractDependencies(dependsOn: verifyDependencyDownloads) {
doLast { doLast {
@ -608,4 +615,4 @@ task testElf (dependsOn: 'externalNativeBuildDebug') {
} }
} }
} }
*/ */

View file

@ -233,7 +233,6 @@ OctreeServer::OctreeServer(ReceivedMessage& message) :
_argc(0), _argc(0),
_argv(NULL), _argv(NULL),
_parsedArgV(NULL), _parsedArgV(NULL),
_httpManager(NULL),
_statusPort(0), _statusPort(0),
_packetsPerClientPerInterval(10), _packetsPerClientPerInterval(10),
_packetsTotalPerInterval(DEFAULT_PACKETS_PER_INTERVAL), _packetsTotalPerInterval(DEFAULT_PACKETS_PER_INTERVAL),
@ -285,7 +284,7 @@ void OctreeServer::initHTTPManager(int port) {
QString documentRoot = QString("%1/web").arg(PathUtils::getAppDataPath()); QString documentRoot = QString("%1/web").arg(PathUtils::getAppDataPath());
// setup an httpManager with us as the request handler and the parent // 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) { bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) {

View file

@ -183,7 +183,7 @@ protected:
bool _isShuttingDown = false; bool _isShuttingDown = false;
HTTPManager* _httpManager; std::unique_ptr<HTTPManager> _httpManager;
int _statusPort; int _statusPort;
QString _statusHost; QString _statusHost;

View file

@ -17,8 +17,8 @@ if (WIN32)
ExternalProject_Add( ExternalProject_Add(
${EXTERNAL_NAME} ${EXTERNAL_NAME}
URL https://static.oculus.com/sdk-downloads/1.11.0/Public/1486063832/ovr_sdk_win_1.11.0_public.zip URL http://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.26.0_public.zip
URL_MD5 ea484403757cbfdfa743b6577fb1f9d2 URL_MD5 06804ff9727b910dcd04a37c800053b5
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" <SOURCE_DIR>/CMakeLists.txt PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" <SOURCE_DIR>/CMakeLists.txt
LOG_DOWNLOAD 1 LOG_DOWNLOAD 1

View file

@ -4,7 +4,7 @@ project(LibOVR)
include_directories(LibOVR/Include LibOVR/Src) include_directories(LibOVR/Include LibOVR/Src)
file(GLOB HEADER_FILES LibOVR/Include/*.h) file(GLOB HEADER_FILES LibOVR/Include/*.h)
file(GLOB EXTRA_HEADER_FILES LibOVR/Include/Extras/*.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") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DOVR_BUILD_DEBUG")
add_library(LibOVR STATIC ${SOURCE_FILES} ${HEADER_FILES} ${EXTRA_HEADER_FILES}) add_library(LibOVR STATIC ${SOURCE_FILES} ${HEADER_FILES} ${EXTRA_HEADER_FILES})
set_target_properties(LibOVR PROPERTIES DEBUG_POSTFIX "d") set_target_properties(LibOVR PROPERTIES DEBUG_POSTFIX "d")

55
cmake/externals/etc2comp/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,55 @@
set(EXTERNAL_NAME etc2comp)
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 4c96153eb179acbe619e3d99d3330595
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} ${EXTRA_CMAKE_FLAGS}
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 "$<$<CONFIG:RelWithDebInfo>:build/EtcLib/RelWithDebInfo>")
set(_LIB_FOLDER "${_LIB_FOLDER}$<$<CONFIG:MinSizeRel>:build/EtcLib/MinSizeRel>")
set(_LIB_FOLDER "${_LIB_FOLDER}$<$<OR:$<CONFIG:Release>,$<CONFIG:Debug>>: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")
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

View file

@ -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()

View file

@ -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)

View file

@ -16,6 +16,8 @@
#include <openssl/x509.h> #include <openssl/x509.h>
#include <random> #include <random>
#include <QDataStream>
#include <AccountManager.h> #include <AccountManager.h>
#include <Assignment.h> #include <Assignment.h>

View file

@ -149,7 +149,6 @@ DomainServer::DomainServer(int argc, char* argv[]) :
QCoreApplication(argc, argv), QCoreApplication(argc, argv),
_gatekeeper(this), _gatekeeper(this),
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this), _httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
_httpsManager(NULL),
_allAssignments(), _allAssignments(),
_unfulfilledAssignments(), _unfulfilledAssignments(),
_isUsingDTLS(false), _isUsingDTLS(false),
@ -385,6 +384,8 @@ DomainServer::~DomainServer() {
_contentManager->terminate(); _contentManager->terminate();
} }
DependencyManager::destroy<AccountManager>();
// cleanup the AssetClient thread // cleanup the AssetClient thread
DependencyManager::destroy<AssetClient>(); DependencyManager::destroy<AssetClient>();
_assetClientThread.quit(); _assetClientThread.quit();
@ -439,7 +440,7 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() {
QSslCertificate sslCertificate(&certFile); QSslCertificate sslCertificate(&certFile);
QSslKey privateKey(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, keyPassphraseString.toUtf8()); 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; qDebug() << "TCP server listening for HTTPS connections on" << DOMAIN_SERVER_HTTPS_PORT;

View file

@ -220,7 +220,7 @@ private:
DomainGatekeeper _gatekeeper; DomainGatekeeper _gatekeeper;
HTTPManager _httpManager; HTTPManager _httpManager;
HTTPSManager* _httpsManager; std::unique_ptr<HTTPSManager> _httpsManager;
QHash<QUuid, SharedAssignmentPointer> _allAssignments; QHash<QUuid, SharedAssignmentPointer> _allAssignments;
QQueue<SharedAssignmentPointer> _unfulfilledAssignments; QQueue<SharedAssignmentPointer> _unfulfilledAssignments;

View file

@ -13,6 +13,7 @@
#include <openssl/x509.h> #include <openssl/x509.h>
#include <QtCore/QDataStream>
#include <QtCore/QJsonDocument> #include <QtCore/QJsonDocument>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>

View file

@ -133,13 +133,7 @@ if (APPLE)
# set where in the bundle to put the resources file # 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_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 # 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}) list(APPEND INTERFACE_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/icon/${INTERFACE_ICON_FILENAME})
endif() endif()
@ -317,18 +311,27 @@ if (APPLE)
set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources") set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_APP_PATH}/Contents/Resources")
set(RESOURCES_DEV_DIR "$<TARGET_FILE_DIR:${TARGET_NAME}>/../Resources") set(RESOURCES_DEV_DIR "$<TARGET_FILE_DIR:${TARGET_NAME}>/../Resources")
# copy script files beside the executable
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
# copy script files beside the executable
COMMAND "${CMAKE_COMMAND}" -E copy_directory COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${CMAKE_SOURCE_DIR}/scripts" "${CMAKE_SOURCE_DIR}/scripts"
"${RESOURCES_DEV_DIR}/scripts" "${RESOURCES_DEV_DIR}/scripts"
) # copy JSDoc files beside the executable
# copy JSDoc files beside the executable
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_directory COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${CMAKE_SOURCE_DIR}/tools/jsdoc/out" "${CMAKE_SOURCE_DIR}/tools/jsdoc/out"
"${RESOURCES_DEV_DIR}/jsdoc" "${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 # call the fixup_interface macro to add required bundling commands for installation
@ -357,13 +360,10 @@ else()
COMMAND "${CMAKE_COMMAND}" -E copy_if_different COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${PROJECT_SOURCE_DIR}/resources/serverless/tutorial.json" "${PROJECT_SOURCE_DIR}/resources/serverless/tutorial.json"
"${RESOURCES_DEV_DIR}/serverless/tutorial.json" "${RESOURCES_DEV_DIR}/serverless/tutorial.json"
) # copy JSDoc files beside the executable
# copy JSDoc files beside the executable
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_directory COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${CMAKE_SOURCE_DIR}/tools/jsdoc/out" "${CMAKE_SOURCE_DIR}/tools/jsdoc/out"
"${INTERFACE_EXEC_DIR}/jsdoc" "${INTERFACE_EXEC_DIR}/jsdoc"
) )
# link target to external libraries # link target to external libraries

View file

@ -331,6 +331,7 @@ ModalWindow {
} }
onFolderChanged: { onFolderChanged: {
d.clearSelection();
fileTableModel.update(); // Update once the data from the folder change is available. fileTableModel.update(); // Update once the data from the folder change is available.
} }
@ -450,7 +451,7 @@ ModalWindow {
rows = 0, rows = 0,
i; i;
var newFilesModel = filesModelBuilder.createObject(root); filesModel = filesModelBuilder.createObject(root);
comparisonFunction = sortOrder === Qt.AscendingOrder comparisonFunction = sortOrder === Qt.AscendingOrder
? function(a, b) { return a < b; } ? function(a, b) { return a < b; }
@ -472,7 +473,7 @@ ModalWindow {
while (lower < upper) { while (lower < upper) {
middle = Math.floor((lower + upper) / 2); middle = Math.floor((lower + upper) / 2);
var lessThan; var lessThan;
if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) { if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) {
lessThan = true; lessThan = true;
upper = middle; upper = middle;
} else { } else {
@ -481,7 +482,7 @@ ModalWindow {
} }
} }
newFilesModel.insert(lower, { filesModel.insert(lower, {
fileName: fileName, fileName: fileName,
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
fileSize: model.getItem(i, "fileSize"), fileSize: model.getItem(i, "fileSize"),
@ -492,9 +493,6 @@ ModalWindow {
rows++; rows++;
} }
filesModel = newFilesModel;
d.clearSelection();
} }
} }

View file

@ -332,6 +332,7 @@ ModalWindow {
} }
onFolderChanged: { onFolderChanged: {
d.clearSelection();
fileTableModel.update(); // Update once the data from the folder change is available. fileTableModel.update(); // Update once the data from the folder change is available.
} }
@ -451,7 +452,7 @@ ModalWindow {
rows = 0, rows = 0,
i; i;
var newFilesModel = filesModelBuilder.createObject(root); filesModel = filesModelBuilder.createObject(root);
comparisonFunction = sortOrder === Qt.AscendingOrder comparisonFunction = sortOrder === Qt.AscendingOrder
? function(a, b) { return a < b; } ? function(a, b) { return a < b; }
@ -473,7 +474,7 @@ ModalWindow {
while (lower < upper) { while (lower < upper) {
middle = Math.floor((lower + upper) / 2); middle = Math.floor((lower + upper) / 2);
var lessThan; var lessThan;
if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) { if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) {
lessThan = true; lessThan = true;
upper = middle; upper = middle;
} else { } else {
@ -482,7 +483,7 @@ ModalWindow {
} }
} }
newFilesModel.insert(lower, { filesModel.insert(lower, {
fileName: fileName, fileName: fileName,
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
fileSize: model.getItem(i, "fileSize"), fileSize: model.getItem(i, "fileSize"),
@ -493,9 +494,6 @@ ModalWindow {
rows++; rows++;
} }
filesModel = newFilesModel;
d.clearSelection();
} }
} }

View file

@ -295,7 +295,8 @@ TabletModalWindow {
} }
onFolderChanged: { onFolderChanged: {
fileTableModel.update() d.clearSelection();
fileTableModel.update();
} }
function getItem(index, field) { function getItem(index, field) {
@ -413,7 +414,7 @@ TabletModalWindow {
rows = 0, rows = 0,
i; i;
var newFilesModel = filesModelBuilder.createObject(root); filesModel = filesModelBuilder.createObject(root);
comparisonFunction = sortOrder === Qt.AscendingOrder comparisonFunction = sortOrder === Qt.AscendingOrder
? function(a, b) { return a < b; } ? function(a, b) { return a < b; }
@ -435,7 +436,7 @@ TabletModalWindow {
while (lower < upper) { while (lower < upper) {
middle = Math.floor((lower + upper) / 2); middle = Math.floor((lower + upper) / 2);
var lessThan; var lessThan;
if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) { if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) {
lessThan = true; lessThan = true;
upper = middle; upper = middle;
} else { } else {
@ -444,7 +445,7 @@ TabletModalWindow {
} }
} }
newFilesModel.insert(lower, { filesModel.insert(lower, {
fileName: fileName, fileName: fileName,
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
fileSize: model.getItem(i, "fileSize"), fileSize: model.getItem(i, "fileSize"),
@ -455,9 +456,6 @@ TabletModalWindow {
rows++; rows++;
} }
filesModel = newFilesModel;
d.clearSelection();
} }
} }

View file

@ -8,12 +8,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
import QtQuick 2.5 import QtQuick 2.7
import QtQuick.Controls 1.4
import Qt.labs.folderlistmodel 2.1 import Qt.labs.folderlistmodel 2.1
import Qt.labs.settings 1.0 import Qt.labs.settings 1.0
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs import QtQuick.Dialogs 1.2 as OriginalDialogs
import QtQuick.Controls 1.4 as QQC1
import QtQuick.Controls 2.3
import ".." import ".."
import "../../../controls-uit" import "../../../controls-uit"
@ -30,6 +30,8 @@ Rectangle {
color: hifi.colors.baseGray; color: hifi.colors.baseGray;
property var filesModel: ListModel { }
Settings { Settings {
category: "FileDialog" category: "FileDialog"
property alias width: root.width property alias width: root.width
@ -149,7 +151,7 @@ Rectangle {
ComboBox { ComboBox {
id: pathSelector id: pathSelector
anchors { anchors {
top: parent.top top: parent.top
topMargin: hifi.dimensions.contentMargin.y topMargin: hifi.dimensions.contentMargin.y
left: navControls.right left: navControls.right
@ -247,7 +249,9 @@ Rectangle {
} }
currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath); 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) { if (root.selectDirectory || !currentSelectionIsFolder) {
currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl)); currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl));
} else { } else {
@ -287,6 +291,7 @@ Rectangle {
} }
onFolderChanged: { onFolderChanged: {
d.clearSelection();
fileTableModel.update(); // Update once the data from the folder change is available. 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 id: fileTableModel
// FolderListModel has a couple of problems: // FolderListModel has a couple of problems:
@ -379,7 +389,11 @@ Rectangle {
if (row === -1) { if (row === -1) {
return false; return false;
} }
return get(row).fileIsDir; return filesModel.get(row).fileIsDir;
}
function get(row) {
return filesModel.get(row)
} }
function update() { function update() {
@ -397,7 +411,7 @@ Rectangle {
rows = 0, rows = 0,
i; i;
clear(); filesModel = filesModelBuilder.createObject(root);
comparisonFunction = sortOrder === Qt.AscendingOrder comparisonFunction = sortOrder === Qt.AscendingOrder
? function(a, b) { return a < b; } ? function(a, b) { return a < b; }
@ -419,7 +433,7 @@ Rectangle {
while (lower < upper) { while (lower < upper) {
middle = Math.floor((lower + upper) / 2); middle = Math.floor((lower + upper) / 2);
var lessThan; var lessThan;
if (comparisonFunction(sortValue, get(middle)[sortField])) { if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) {
lessThan = true; lessThan = true;
upper = middle; upper = middle;
} else { } else {
@ -428,7 +442,7 @@ Rectangle {
} }
} }
insert(lower, { filesModel.insert(lower, {
fileName: fileName, fileName: fileName,
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
fileSize: model.getItem(i, "fileSize"), fileSize: model.getItem(i, "fileSize"),
@ -439,8 +453,6 @@ Rectangle {
rows++; rows++;
} }
d.clearSelection();
} }
} }
@ -465,12 +477,12 @@ Rectangle {
sortIndicatorOrder: Qt.AscendingOrder sortIndicatorOrder: Qt.AscendingOrder
sortIndicatorVisible: true sortIndicatorVisible: true
model: fileTableModel model: filesModel
function updateSort() { function updateSort() {
model.sortOrder = sortIndicatorOrder; fileTableModel.sortOrder = sortIndicatorOrder;
model.sortColumn = sortIndicatorColumn; fileTableModel.sortColumn = sortIndicatorColumn;
model.update(); fileTableModel.update();
} }
onSortIndicatorColumnChanged: { updateSort(); } onSortIndicatorColumnChanged: { updateSort(); }
@ -522,7 +534,7 @@ Rectangle {
} }
} }
TableViewColumn { QQC1.TableViewColumn {
id: fileNameColumn id: fileNameColumn
role: "fileName" role: "fileName"
title: "Name" title: "Name"
@ -530,7 +542,7 @@ Rectangle {
movable: false movable: false
resizable: true resizable: true
} }
TableViewColumn { QQC1.TableViewColumn {
id: fileMofifiedColumn id: fileMofifiedColumn
role: "fileModified" role: "fileModified"
title: "Date" title: "Date"
@ -539,7 +551,7 @@ Rectangle {
resizable: true resizable: true
visible: !selectDirectory visible: !selectDirectory
} }
TableViewColumn { QQC1.TableViewColumn {
role: "fileSize" role: "fileSize"
title: "Size" title: "Size"
width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width
@ -554,11 +566,12 @@ Rectangle {
} }
function navigateToCurrentRow() { function navigateToCurrentRow() {
var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel
var row = fileTableView.currentRow var row = fileTableView.currentRow
var isFolder = model.isFolder(row); var isFolder = currentModel.isFolder(row);
var file = model.get(row).filePath; var file = currentModel.get(row).filePath;
if (isFolder) { if (isFolder) {
fileTableView.model.folder = helper.pathToUrl(file); currentModel.folder = helper.pathToUrl(file);
} else { } else {
okAction.trigger(); okAction.trigger();
} }
@ -573,7 +586,8 @@ Rectangle {
var newPrefix = prefix + event.text.toLowerCase(); var newPrefix = prefix + event.text.toLowerCase();
var matchedIndex = -1; var matchedIndex = -1;
for (var i = 0; i < model.count; ++i) { 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)) { if (0 === name.indexOf(newPrefix)) {
matchedIndex = i; matchedIndex = i;
break; break;

View file

@ -802,15 +802,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
qApp->setProperty(hifi::properties::APP_LOCAL_DATA_PATH, cacheDir); 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 = PathUtils::getRccPath();
const QString resourcesBinaryFile = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources.rcc";
#else
const QString resourcesBinaryFile = QCoreApplication::applicationDirPath() + "/resources.rcc";
#endif
if (!QFile::exists(resourcesBinaryFile)) { if (!QFile::exists(resourcesBinaryFile)) {
throw std::runtime_error("Unable to find primary resources"); 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"); throw std::runtime_error("Unable to load primary resources");
} }
} }
#endif
// Tell the plugin manager about our statically linked plugins // Tell the plugin manager about our statically linked plugins
auto pluginManager = PluginManager::getInstance(); auto pluginManager = PluginManager::getInstance();
@ -3674,9 +3666,21 @@ void Application::keyPressEvent(QKeyEvent* event) {
} }
break; break;
case Qt::Key_1: case Qt::Key_1: {
case Qt::Key_2: Menu* menu = Menu::getInstance();
case Qt::Key_3: 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_4:
case Qt::Key_5: case Qt::Key_5:
case Qt::Key_6: case Qt::Key_6:
@ -4187,7 +4191,7 @@ bool Application::acceptSnapshot(const QString& urlString) {
static uint32_t _renderedFrameIndex { INVALID_FRAME }; static uint32_t _renderedFrameIndex { INVALID_FRAME };
bool Application::shouldPaint() const { bool Application::shouldPaint() const {
if (_aboutToQuit) { if (_aboutToQuit || _window->isMinimized()) {
return false; return false;
} }
@ -4747,12 +4751,15 @@ void Application::loadSettings() {
// DONT CHECK IN // DONT CHECK IN
//DependencyManager::get<LODManager>()->setAutomaticLODAdjust(false); //DependencyManager::get<LODManager>()->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. // If there is a preferred plugin, we probably messed it up with the menu settings, so fix it.
auto pluginManager = PluginManager::getInstance(); auto pluginManager = PluginManager::getInstance();
auto plugins = pluginManager->getPreferredDisplayPlugins(); auto plugins = pluginManager->getPreferredDisplayPlugins();
auto menu = Menu::getInstance();
if (plugins.size() > 0) { if (plugins.size() > 0) {
for (auto plugin : plugins) { for (auto plugin : plugins) {
if (auto action = menu->getActionForOption(plugin->getName())) { if (auto action = menu->getActionForOption(plugin->getName())) {
@ -8041,7 +8048,6 @@ void Application::switchDisplayMode() {
setActiveDisplayPlugin(DESKTOP_DISPLAY_PLUGIN_NAME); setActiveDisplayPlugin(DESKTOP_DISPLAY_PLUGIN_NAME);
startHMDStandBySession(); startHMDStandBySession();
} }
emit activeDisplayPluginChanged();
} }
_previousHMDWornStatus = currentHMDWornStatus; _previousHMDWornStatus = currentHMDWornStatus;
} }

View file

@ -30,9 +30,6 @@ void Application::editRenderArgs(RenderArgsEditor editor) {
void Application::paintGL() { void Application::paintGL() {
// Some plugins process message events, allowing paintGL to be called reentrantly. // Some plugins process message events, allowing paintGL to be called reentrantly.
if (_aboutToQuit || _window->isMinimized()) {
return;
}
_renderFrameCount++; _renderFrameCount++;
_lastTimeRendered.start(); _lastTimeRendered.start();

View file

@ -217,21 +217,21 @@ Menu::Menu() {
// View > First Person // View > First Person
auto firstPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( auto firstPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
viewMenu, MenuOption::FirstPerson, Qt::Key_1, viewMenu, MenuOption::FirstPerson, 0,
true, qApp, SLOT(cameraMenuChanged()))); true, qApp, SLOT(cameraMenuChanged())));
firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
// View > Third Person // View > Third Person
auto thirdPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( auto thirdPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
viewMenu, MenuOption::ThirdPerson, Qt::Key_3, viewMenu, MenuOption::ThirdPerson, 0,
false, qApp, SLOT(cameraMenuChanged()))); false, qApp, SLOT(cameraMenuChanged())));
thirdPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); thirdPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
// View > Mirror // View > Mirror
auto viewMirrorAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( auto viewMirrorAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
viewMenu, MenuOption::FullscreenMirror, Qt::Key_2, viewMenu, MenuOption::FullscreenMirror, 0,
false, qApp, SLOT(cameraMenuChanged()))); false, qApp, SLOT(cameraMenuChanged())));
viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));

View file

@ -9,11 +9,13 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "Application.h"
#include "SecondaryCamera.h" #include "SecondaryCamera.h"
#include <TextureCache.h>
#include <gpu/Context.h>
#include <glm/gtx/transform.hpp> #include <glm/gtx/transform.hpp>
#include <gpu/Context.h>
#include <TextureCache.h>
#include "Application.h"
using RenderArgsPointer = std::shared_ptr<RenderArgs>; using RenderArgsPointer = std::shared_ptr<RenderArgs>;

View file

@ -654,8 +654,8 @@ void MyAvatar::simulate(float deltaTime) {
if (success) { if (success) {
moveOperator.addEntityToMoveList(entity, newCube); moveOperator.addEntityToMoveList(entity, newCube);
} }
// send an edit packet to update the entity-server about the queryAABox. If it's an // send an edit packet to update the entity-server about the queryAABox
// avatar-entity, don't. // unless it is client-only
if (packetSender && !entity->getClientOnly()) { if (packetSender && !entity->getClientOnly()) {
EntityItemProperties properties = entity->getProperties(); EntityItemProperties properties = entity->getProperties();
properties.setQueryAACubeDirty(); properties.setQueryAACubeDirty();
@ -663,6 +663,17 @@ void MyAvatar::simulate(float deltaTime) {
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entity->getID(), properties); packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entity->getID(), properties);
entity->setLastBroadcast(usecTimestampNow()); entity->setLastBroadcast(usecTimestampNow());
entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(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
}
});
} }
} }
}); });

View file

@ -314,6 +314,7 @@ Wallet::Wallet() {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
auto ledger = DependencyManager::get<Ledger>(); auto ledger = DependencyManager::get<Ledger>();
auto& packetReceiver = nodeList->getPacketReceiver(); auto& packetReceiver = nodeList->getPacketReceiver();
_passphrase = new QString("");
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket"); packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket");
packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest, this, "handleChallengeOwnershipPacket"); packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest, this, "handleChallengeOwnershipPacket");
@ -365,6 +366,10 @@ Wallet::~Wallet() {
if (_securityImage) { if (_securityImage) {
delete _securityImage; delete _securityImage;
} }
if (_passphrase) {
delete _passphrase;
}
} }
bool Wallet::setPassphrase(const QString& passphrase) { bool Wallet::setPassphrase(const QString& passphrase) {

View file

@ -78,7 +78,7 @@ private:
QByteArray _salt; QByteArray _salt;
QByteArray _iv; QByteArray _iv;
QByteArray _ckey; QByteArray _ckey;
QString* _passphrase { new QString("") }; QString* _passphrase { nullptr };
bool _isOverridingServer { false }; bool _isOverridingServer { false };
bool writeWallet(const QString& newPassphrase = QString("")); bool writeWallet(const QString& newPassphrase = QString(""));

View file

@ -105,9 +105,6 @@ class ScriptEngine;
* <li>{@link Controller.getValue|getValue}</li> * <li>{@link Controller.getValue|getValue}</li>
* <li>{@link Controller.getAxisValue|getAxisValue}</li> * <li>{@link Controller.getAxisValue|getAxisValue}</li>
* <li>{@link Controller.getPoseValue|getgetPoseValue}</li> * <li>{@link Controller.getPoseValue|getgetPoseValue}</li>
* <li>{@link Controller.getButtonValue|getButtonValue} for a particular device</li>
* <li>{@link Controller.getAxisValue(0)|getAxisValue} for a particular device</li>
* <li>{@link Controller.getPoseValue(0)|getPoseValue} for a particular device</li>
* <li>{@link Controller.getActionValue|getActionValue}</li> * <li>{@link Controller.getActionValue|getActionValue}</li>
* </ul> * </ul>
* *

View file

@ -349,3 +349,23 @@ void Base3DOverlay::setVisible(bool visible) {
Parent::setVisible(visible); Parent::setVisible(visible);
notifyRenderVariableChange(); 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();
}

View file

@ -35,6 +35,7 @@ public:
// getters // getters
virtual bool is3D() const override { return true; } 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 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(); } virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); }

View file

@ -115,6 +115,10 @@ void ModelOverlay::update(float deltatime) {
_drawInHUDDirty = false; _drawInHUDDirty = false;
_model->setLayeredInHUD(getDrawHUDLayer(), scene); _model->setLayeredInHUD(getDrawHUDLayer(), scene);
} }
if (_groupCulledDirty) {
_groupCulledDirty = false;
_model->setGroupCulled(_isGroupCulled);
}
scene->enqueueTransaction(transaction); scene->enqueueTransaction(transaction);
if (!_texturesLoaded && _model->getGeometry() && _model->getGeometry()->areTexturesLoaded()) { if (!_texturesLoaded && _model->getGeometry() && _model->getGeometry()->areTexturesLoaded()) {
@ -149,13 +153,24 @@ void ModelOverlay::setVisible(bool visible) {
} }
void ModelOverlay::setDrawInFront(bool drawInFront) { void ModelOverlay::setDrawInFront(bool drawInFront) {
Base3DOverlay::setDrawInFront(drawInFront); if (drawInFront != getDrawInFront()) {
_drawInFrontDirty = true; Base3DOverlay::setDrawInFront(drawInFront);
_drawInFrontDirty = true;
}
} }
void ModelOverlay::setDrawHUDLayer(bool drawHUDLayer) { void ModelOverlay::setDrawHUDLayer(bool drawHUDLayer) {
Base3DOverlay::setDrawHUDLayer(drawHUDLayer); if (drawHUDLayer != getDrawHUDLayer()) {
_drawInHUDDirty = true; Base3DOverlay::setDrawHUDLayer(drawHUDLayer);
_drawInHUDDirty = true;
}
}
void ModelOverlay::setGroupCulled(bool groupCulled) {
if (groupCulled != _isGroupCulled) {
_isGroupCulled = groupCulled;
_groupCulledDirty = true;
}
} }
void ModelOverlay::setProperties(const QVariantMap& properties) { void ModelOverlay::setProperties(const QVariantMap& properties) {
@ -210,6 +225,11 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
Q_ARG(const QVariantMap&, textureMap)); Q_ARG(const QVariantMap&, textureMap));
} }
auto groupCulledValue = properties["isGroupCulled"];
if (groupCulledValue.isValid() && groupCulledValue.canConvert(QVariant::Bool)) {
setGroupCulled(groupCulledValue.toBool());
}
// jointNames is read-only. // jointNames is read-only.
// jointPositions is read-only. // jointPositions is read-only.
// jointOrientations is read-only. // jointOrientations is read-only.
@ -347,6 +367,8 @@ vectorType ModelOverlay::mapJoints(mapFunction<itemType> function) const {
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't * @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities. * have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
* @property {boolean} isGroupCulled=false - If <code>true</code>, the mesh parts of the model are LOD culled as a group.
* If <code>false</code>, separate mesh parts will be LOD culled individually.
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
@ -711,3 +733,11 @@ scriptable::ScriptableModelBase ModelOverlay::getScriptableModel() {
} }
return result; return result;
} }
render::ItemKey ModelOverlay::getKey() {
auto builder = render::ItemKey::Builder(Base3DOverlay::getKey());
if (_isGroupCulled) {
builder.withMetaCullGroup();
}
return builder.build();
}

View file

@ -33,6 +33,7 @@ public:
virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override; virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override;
render::ItemKey getKey() override;
void clearSubRenderItemIDs(); void clearSubRenderItemIDs();
void setSubRenderItemIDs(const render::ItemIDs& ids); void setSubRenderItemIDs(const render::ItemIDs& ids);
@ -63,6 +64,7 @@ public:
void setVisible(bool visible) override; void setVisible(bool visible) override;
void setDrawInFront(bool drawInFront) override; void setDrawInFront(bool drawInFront) override;
void setDrawHUDLayer(bool drawHUDLayer) override; void setDrawHUDLayer(bool drawHUDLayer) override;
void setGroupCulled(bool groupCulled);
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override; void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override;
void removeMaterial(graphics::MaterialPointer 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 _visibleDirty { true };
bool _drawInFrontDirty { false }; bool _drawInFrontDirty { false };
bool _drawInHUDDirty { false }; bool _drawInHUDDirty { false };
bool _isGroupCulled { false };
bool _groupCulledDirty { false };
void processMaterials(); void processMaterials();

View file

@ -244,4 +244,21 @@ void Overlay::addMaterial(graphics::MaterialLayer material, const std::string& p
void Overlay::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) { void Overlay::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {
std::lock_guard<std::mutex> lock(_materialsLock); std::lock_guard<std::mutex> lock(_materialsLock);
_materials[parentMaterialName].remove(material); _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();
} }

View file

@ -40,6 +40,7 @@ public:
virtual void update(float deltatime) {} virtual void update(float deltatime) {}
virtual void render(RenderArgs* args) = 0; virtual void render(RenderArgs* args) = 0;
virtual render::ItemKey getKey();
virtual AABox getBounds() const = 0; virtual AABox getBounds() const = 0;
virtual bool supportsGetProperty() const { return true; } virtual bool supportsGetProperty() const { return true; }

View file

@ -32,32 +32,7 @@
namespace render { namespace render {
template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) { template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) {
auto builder = ItemKey::Builder().withTypeShape(); return overlay->getKey();
if (overlay->is3D()) {
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(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
render::hifi::Tag viewTagBits = overlay->getIsVisibleInSecondaryCamera() ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW;
builder.withTagBits(viewTagBits);
return builder.build();
} }
template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) { template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) {
return overlay->getBounds(); return overlay->getBounds();

View file

@ -92,28 +92,16 @@ namespace controller {
return userInputMapper->getValue(Input((uint32_t)source)); 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 { float ScriptingInterface::getAxisValue(int source) const {
auto userInputMapper = DependencyManager::get<UserInputMapper>(); auto userInputMapper = DependencyManager::get<UserInputMapper>();
return userInputMapper->getValue(Input((uint32_t)source)); 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 { Pose ScriptingInterface::getPoseValue(const int& source) const {
auto userInputMapper = DependencyManager::get<UserInputMapper>(); auto userInputMapper = DependencyManager::get<UserInputMapper>();
return userInputMapper->getPose(Input((uint32_t)source)); 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<Action> ScriptingInterface::getAllActions() { QVector<Action> ScriptingInterface::getAllActions() {
return DependencyManager::get<UserInputMapper>()->getAllActions(); return DependencyManager::get<UserInputMapper>()->getAllActions();
} }

View file

@ -206,43 +206,6 @@ namespace controller {
*/ */
Q_INVOKABLE Pose getPoseValue(const int& source) const; 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
* <code>0</code> corresponds to <code>Standard</code>.
* @returns {number} The current value of the button if the parameters are valid, otherwise <code>0</code>.
* @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
* <code>0</code> corresponds to <code>Standard</code>.
* @returns {number} The current value of the axis if the parameters are valid, otherwise <code>0</code>.
* @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
* <code>0</code> corresponds to <code>Standard</code>.
* @returns {Pose} The current value of the controller pose output if the parameters are valid, otherwise an invalid
* pose with <code>Pose.valid == false</code>.
* @deprecated This function no longer works.
*/
Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const;
/**jsdoc /**jsdoc
* Triggers a haptic pulse on connected and enabled devices that have the capability. * Triggers a haptic pulse on connected and enabled devices that have the capability.
* @function Controller.triggerHapticPulse * @function Controller.triggerHapticPulse

View file

@ -11,6 +11,8 @@
#include "HTTPConnection.h" #include "HTTPConnection.h"
#include <assert.h>
#include <QBuffer> #include <QBuffer>
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QTcpSocket> #include <QTcpSocket>
@ -29,11 +31,92 @@ const char* HTTPConnection::StatusCode404 = "404 Not Found";
const char* HTTPConnection::StatusCode500 = "500 Internal server error"; const char* HTTPConnection::StatusCode500 = "500 Internal server error";
const char* HTTPConnection::DefaultContentType = "text/plain; charset=ISO-8859-1"; const char* HTTPConnection::DefaultContentType = "text/plain; charset=ISO-8859-1";
HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager) :
class MemoryStorage : public HTTPConnection::Storage {
public:
static std::unique_ptr<MemoryStorage> 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> MemoryStorage::make(qint64 size) {
return std::unique_ptr<MemoryStorage>(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<FileStorage> 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<QTemporaryFile> 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<QTemporaryFile> _file;
uchar* const _mappedMemoryAddress { nullptr };
const qint64 _mappedMemorySize { 0 };
qint64 _bytesWritten { 0 };
};
std::unique_ptr<FileStorage> FileStorage::make(qint64 size) {
auto file = std::unique_ptr<QTemporaryFile>(new QTemporaryFile());
file->open(); // Open for resize
file->resize(size);
auto mapped = file->map(0, size); // map the entire file
return std::unique_ptr<FileStorage>(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<QTemporaryFile> file, uchar* mapped, qint64 size) :
_wrapperArray(QByteArray::fromRawData(reinterpret_cast<char*>(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), QObject(parentManager),
_parentManager(parentManager), _parentManager(parentManager),
_socket(socket), _socket(socket),
_stream(socket),
_address(socket->peerAddress()) _address(socket->peerAddress())
{ {
// take over ownership of the socket // take over ownership of the socket
@ -62,7 +145,7 @@ QHash<QString, QString> HTTPConnection::parseUrlEncodedForm() {
return QHash<QString, QString>(); return QHash<QString, QString>();
} }
QUrlQuery form { _requestContent }; QUrlQuery form { _requestContent->content() };
QHash<QString, QString> pairs; QHash<QString, QString> pairs;
for (auto pair : form.queryItems()) { for (auto pair : form.queryItems()) {
auto key = QUrl::fromPercentEncoding(pair.first.toLatin1().replace('+', ' ')); auto key = QUrl::fromPercentEncoding(pair.first.toLatin1().replace('+', ' '));
@ -97,7 +180,7 @@ QList<FormData> HTTPConnection::parseFormData() const {
QByteArray end = "\r\n--" + boundary + "--\r\n"; QByteArray end = "\r\n--" + boundary + "--\r\n";
QList<FormData> data; QList<FormData> data;
QBuffer buffer(const_cast<QByteArray*>(&_requestContent)); QBuffer buffer(const_cast<QByteArray*>(&_requestContent->content()));
buffer.open(QIODevice::ReadOnly); buffer.open(QIODevice::ReadOnly);
while (buffer.canReadLine()) { while (buffer.canReadLine()) {
QByteArray line = buffer.readLine().trimmed(); QByteArray line = buffer.readLine().trimmed();
@ -107,12 +190,13 @@ QList<FormData> HTTPConnection::parseFormData() const {
QByteArray line = buffer.readLine().trimmed(); QByteArray line = buffer.readLine().trimmed();
if (line.isEmpty()) { if (line.isEmpty()) {
// content starts after this line // content starts after this line
int idx = _requestContent.indexOf(end, buffer.pos()); int idx = _requestContent->content().indexOf(end, buffer.pos());
if (idx == -1) { if (idx == -1) {
qWarning() << "Missing end boundary." << _address; qWarning() << "Missing end boundary." << _address;
return data; 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); data.append(datum);
buffer.seek(idx + end.length()); buffer.seek(idx + end.length());
@ -256,7 +340,24 @@ void HTTPConnection::readHeaders() {
_parentManager->handleHTTPRequest(this, _requestUrl); _parentManager->handleHTTPRequest(this, _requestUrl);
} else { } 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())); connect(_socket, SIGNAL(readyRead()), SLOT(readContent()));
// read any content immediately available // read any content immediately available
@ -285,12 +386,13 @@ void HTTPConnection::readHeaders() {
} }
void HTTPConnection::readContent() { void HTTPConnection::readContent() {
int size = _requestContent.size(); auto size = std::min(_socket->bytesAvailable(), _requestContent->bytesLeftToWrite());
if (_socket->bytesAvailable() < size) {
return;
}
_socket->read(_requestContent.data(), size);
_socket->disconnect(this, SLOT(readContent()));
_parentManager->handleHTTPRequest(this, _requestUrl.path()); _requestContent->write(_socket->read(size));
if (_requestContent->bytesLeftToWrite() == 0) {
_socket->disconnect(this, SLOT(readContent()));
_parentManager->handleHTTPRequest(this, _requestUrl.path());
}
} }

View file

@ -16,14 +16,14 @@
#ifndef hifi_HTTPConnection_h #ifndef hifi_HTTPConnection_h
#define hifi_HTTPConnection_h #define hifi_HTTPConnection_h
#include <QDataStream>
#include <QHash> #include <QHash>
#include <QtNetwork/QHostAddress>
#include <QIODevice> #include <QIODevice>
#include <QList> #include <QList>
#include <QtNetwork/QHostAddress>
#include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkAccessManager>
#include <QObject> #include <QObject>
#include <QPair> #include <QPair>
#include <QTemporaryFile>
#include <QUrl> #include <QUrl>
#include <memory> #include <memory>
@ -57,52 +57,63 @@ public:
/// WebSocket close status codes. /// WebSocket close status codes.
enum ReasonCode { NoReason = 0, NormalClosure = 1000, GoingAway = 1001 }; 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. /// Initializes the connection.
HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager); HTTPConnection(QTcpSocket* socket, HTTPManager* parentManager);
/// Destroys the connection. /// Destroys the connection.
virtual ~HTTPConnection (); virtual ~HTTPConnection();
/// Returns a pointer to the underlying socket, to which WebSocket message bodies should be written. /// 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. /// Returns the request operation.
QNetworkAccessManager::Operation requestOperation () const { return _requestOperation; } QNetworkAccessManager::Operation requestOperation() const { return _requestOperation; }
/// Returns a reference to the request URL. /// 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. /// 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()); } QByteArray requestHeader(const QString& key) const { return _requestHeaders.value(key.toLower().toLocal8Bit()); }
/// Returns a reference to the request content. /// 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. /// Parses the request content as form data, returning a list of header/content pairs.
QList<FormData> parseFormData () const; QList<FormData> parseFormData() const;
/// Parses the request content as a url encoded form, returning a hash of key/value pairs. /// Parses the request content as a url encoded form, returning a hash of key/value pairs.
/// Duplicate keys are not supported. /// Duplicate keys are not supported.
QHash<QString, QString> parseUrlEncodedForm(); QHash<QString, QString> parseUrlEncodedForm();
/// Sends a response and closes the connection. /// 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 char* contentType = DefaultContentType,
const Headers& headers = Headers()); const Headers& headers = Headers());
void respond (const char* code, std::unique_ptr<QIODevice> device, void respond(const char* code, std::unique_ptr<QIODevice> device,
const char* contentType = DefaultContentType, const char* contentType = DefaultContentType,
const Headers& headers = Headers()); const Headers& headers = Headers());
protected slots: protected slots:
/// Reads the request line. /// Reads the request line.
void readRequest (); void readRequest();
/// Reads the headers. /// Reads the headers.
void readHeaders (); void readHeaders();
/// Reads the content. /// Reads the content.
void readContent (); void readContent();
protected: protected:
void respondWithStatusAndHeaders(const char* code, const char* contentType, const Headers& headers, qint64 size); void respondWithStatusAndHeaders(const char* code, const char* contentType, const Headers& headers, qint64 size);
@ -112,9 +123,6 @@ protected:
/// The underlying socket. /// The underlying socket.
QTcpSocket* _socket; QTcpSocket* _socket;
/// The data stream for writing to the socket.
QDataStream _stream;
/// The stored address. /// The stored address.
QHostAddress _address; QHostAddress _address;
@ -132,7 +140,7 @@ protected:
QByteArray _lastRequestHeader; QByteArray _lastRequestHeader;
/// The content of the request. /// The content of the request.
QByteArray _requestContent; std::unique_ptr<Storage> _requestContent;
/// Response content /// Response content
std::unique_ptr<QIODevice> _responseDevice; std::unique_ptr<QIODevice> _responseDevice;

View file

@ -24,8 +24,7 @@
const int SOCKET_ERROR_EXIT_CODE = 2; const int SOCKET_ERROR_EXIT_CODE = 2;
const int SOCKET_CHECK_INTERVAL_IN_MS = 30000; const int SOCKET_CHECK_INTERVAL_IN_MS = 30000;
HTTPManager::HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : HTTPManager::HTTPManager(const QHostAddress& listenAddress, quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler) :
QTcpServer(parent),
_listenAddress(listenAddress), _listenAddress(listenAddress),
_documentRoot(documentRoot), _documentRoot(documentRoot),
_requestHandler(requestHandler), _requestHandler(requestHandler),

View file

@ -33,7 +33,7 @@ class HTTPManager : public QTcpServer, public HTTPRequestHandler {
Q_OBJECT Q_OBJECT
public: public:
/// Initializes the manager. /// 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 = nullptr);
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override; bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override;

View file

@ -23,4 +23,4 @@ protected slots:
void handleSSLErrors(const QList<QSslError>& errors); void handleSSLErrors(const QList<QSslError>& errors);
}; };
#endif // hifi_HTTPSConnection_h #endif // hifi_HTTPSConnection_h

View file

@ -16,8 +16,8 @@
#include "HTTPSConnection.h" #include "HTTPSConnection.h"
HTTPSManager::HTTPSManager(QHostAddress listenAddress, quint16 port, const QSslCertificate& certificate, const QSslKey& privateKey, HTTPSManager::HTTPSManager(QHostAddress listenAddress, quint16 port, const QSslCertificate& certificate, const QSslKey& privateKey,
const QString& documentRoot, HTTPSRequestHandler* requestHandler, QObject* parent) : const QString& documentRoot, HTTPSRequestHandler* requestHandler) :
HTTPManager(listenAddress, port, documentRoot, requestHandler, parent), HTTPManager(listenAddress, port, documentRoot, requestHandler),
_certificate(certificate), _certificate(certificate),
_privateKey(privateKey), _privateKey(privateKey),
_sslRequestHandler(requestHandler) _sslRequestHandler(requestHandler)

View file

@ -31,7 +31,7 @@ public:
const QSslCertificate& certificate, const QSslCertificate& certificate,
const QSslKey& privateKey, const QSslKey& privateKey,
const QString& documentRoot, const QString& documentRoot,
HTTPSRequestHandler* requestHandler = NULL, QObject* parent = 0); HTTPSRequestHandler* requestHandler = nullptr);
void setCertificate(const QSslCertificate& certificate) { _certificate = certificate; } void setCertificate(const QSslCertificate& certificate) { _certificate = certificate; }
void setPrivateKey(const QSslKey& privateKey) { _privateKey = privateKey; } void setPrivateKey(const QSslKey& privateKey) { _privateKey = privateKey; }
@ -48,4 +48,4 @@ private:
HTTPSRequestHandler* _sslRequestHandler; HTTPSRequestHandler* _sslRequestHandler;
}; };
#endif // hifi_HTTPSManager_h #endif // hifi_HTTPSManager_h

View file

@ -684,9 +684,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
PROFILE_RANGE(render_gpu, "sphericalHarmonicsFromTexture"); PROFILE_RANGE(render_gpu, "sphericalHarmonicsFromTexture");
#ifndef USE_GLES
auto mipFormat = cubeTexture.getStoredMipFormat(); auto mipFormat = cubeTexture.getStoredMipFormat();
std::function<glm::vec3(uint32)> unpackFunc; std::function<glm::vec3(uint32)> unpackFunc;
switch (mipFormat.getSemantic()) { switch (mipFormat.getSemantic()) {
case gpu::R11G11B10: case gpu::R11G11B10:
unpackFunc = glm::unpackF2x11_1x10; unpackFunc = glm::unpackF2x11_1x10;
@ -698,6 +698,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
assert(false); assert(false);
break; break;
} }
#endif
const uint sqOrder = order*order; 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++) { for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
PROFILE_RANGE(render_gpu, "ProcessFace"); PROFILE_RANGE(render_gpu, "ProcessFace");
#ifndef USE_GLES
auto data = reinterpret_cast<const uint32*>( cubeTexture.accessStoredMipFace(0, face)->readData() ); auto data = reinterpret_cast<const uint32*>( cubeTexture.accessStoredMipFace(0, face)->readData() );
#else
auto data = cubeTexture.accessStoredMipFace(0, face)->readData();
#endif
if (data == nullptr) { if (data == nullptr) {
continue; continue;
} }
@ -816,8 +821,15 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
glm::vec3 color{ 0.0f, 0.0f, 0.0f }; glm::vec3 color{ 0.0f, 0.0f, 0.0f };
for (int i = 0; i < stride; ++i) { for (int i = 0; i < stride; ++i) {
for (int j = 0; j < stride; ++j) { for (int j = 0; j < stride; ++j) {
#ifndef USE_GLES
int k = (int)(x + i - halfStride + (y + j - halfStride) * width); int k = (int)(x + i - halfStride + (y + j - halfStride) * width);
color += unpackFunc(data[k]); 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
} }
} }

View file

@ -352,7 +352,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
if (!Texture::evalKTXFormat(mipFormat, texelFormat, header)) { if (!Texture::evalKTXFormat(mipFormat, texelFormat, header)) {
return nullptr; return nullptr;
} }
// Set Dimensions // Set Dimensions
uint32_t numFaces = 1; uint32_t numFaces = 1;
switch (texture.getType()) { switch (texture.getType()) {

View file

@ -2,3 +2,4 @@ set(TARGET_NAME image)
setup_hifi_library() setup_hifi_library()
link_hifi_libraries(shared gpu) link_hifi_libraries(shared gpu)
target_nvtt() target_nvtt()
target_etc2comp()

View file

@ -31,8 +31,17 @@ using namespace gpu;
#define CPU_MIPMAPS 1 #define CPU_MIPMAPS 1
#include <nvtt/nvtt.h> #include <nvtt/nvtt.h>
#ifdef USE_GLES
#include <Etc.h>
#include <EtcFilter.h>
#endif
static const glm::uvec2 SPARSE_PAGE_SIZE(128); static const glm::uvec2 SPARSE_PAGE_SIZE(128);
#ifdef Q_OS_ANDROID
static const glm::uvec2 MAX_TEXTURE_SIZE(2048);
#else
static const glm::uvec2 MAX_TEXTURE_SIZE(4096); static const glm::uvec2 MAX_TEXTURE_SIZE(4096);
#endif
bool DEV_DECIMATE_TEXTURES = false; bool DEV_DECIMATE_TEXTURES = false;
std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 }; std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 };
std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 }; std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 };
@ -75,7 +84,13 @@ const QStringList getSupportedFormats() {
return stringFormats; return stringFormats;
} }
// On GLES, we don't use HDR skyboxes
#ifndef USE_GLES
QImage::Format QIMAGE_HDR_FORMAT = QImage::Format_RGB30; 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) { TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) {
switch (type) { switch (type) {
@ -548,13 +563,15 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter // https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter
QImage localCopy = std::move(image); QImage localCopy = std::move(image);
if (localCopy.format() != QImage::Format_ARGB32) { if (localCopy.format() != QImage::Format_ARGB32 && localCopy.format() != QIMAGE_HDR_FORMAT) {
localCopy = localCopy.convertToFormat(QImage::Format_ARGB32); localCopy = localCopy.convertToFormat(QImage::Format_ARGB32);
} }
const int width = localCopy.width(), height = localCopy.height(); const int width = localCopy.width(), height = localCopy.height();
const void* data = static_cast<const void*>(localCopy.constBits()); auto mipFormat = texture->getStoredMipFormat();
#ifndef USE_GLES
const void* data = static_cast<const void*>(localCopy.constBits());
nvtt::TextureType textureType = nvtt::TextureType_2D; nvtt::TextureType textureType = nvtt::TextureType_2D;
nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB; nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB;
nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror; nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror;
@ -584,8 +601,6 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
nvtt::CompressionOptions compressionOptions; nvtt::CompressionOptions compressionOptions;
compressionOptions.setQuality(nvtt::Quality_Production); compressionOptions.setQuality(nvtt::Quality_Production);
// TODO: gles: generate ETC mips instead?
auto mipFormat = texture->getStoredMipFormat();
if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGB) { if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGB) {
compressionOptions.setFormat(nvtt::Format_BC1); compressionOptions.setFormat(nvtt::Format_BC1);
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_MASK) { } else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_MASK) {
@ -669,21 +684,94 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
nvtt::Compressor compressor; nvtt::Compressor compressor;
compressor.setTaskDispatcher(&dispatcher); compressor.setTaskDispatcher(&dispatcher);
compressor.process(inputOptions, compressionOptions, outputOptions); compressor.process(inputOptions, compressionOptions, outputOptions);
#else
int numMips = 1 + (int)log2(std::max(width, height));
Etc::RawImage *mipMaps = new Etc::RawImage[numMips];
Etc::Image::Format etcFormat = Etc::Image::Format::DEFAULT;
if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB) {
etcFormat = Etc::Image::Format::RGB8;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB) {
etcFormat = Etc::Image::Format::SRGB8;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA) {
etcFormat = Etc::Image::Format::RGB8A1;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA) {
etcFormat = Etc::Image::Format::SRGB8A1;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGBA) {
etcFormat = Etc::Image::Format::RGBA8;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA) {
etcFormat = Etc::Image::Format::SRGBA8;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED) {
etcFormat = Etc::Image::Format::R11;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED_SIGNED) {
etcFormat = Etc::Image::Format::SIGNED_R11;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY) {
etcFormat = Etc::Image::Format::RG11;
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY_SIGNED) {
etcFormat = Etc::Image::Format::SIGNED_RG11;
} else {
qCWarning(imagelogging) << "Unknown mip format";
Q_UNREACHABLE();
return;
}
const Etc::ErrorMetric errorMetric = Etc::ErrorMetric::RGBA;
const float effort = 1.0f;
const int numEncodeThreads = 4;
int encodingTime;
const float MAX_COLOR = 255.0f;
std::vector<vec4> 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)) / MAX_COLOR;
}
}
// free up the memory afterward to avoid bloating the heap
localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one.
Etc::EncodeMipmaps(
(float *)floatData.data(), width, height,
etcFormat, errorMetric, effort,
numEncodeThreads, numEncodeThreads,
numMips, Etc::FILTER_WRAP_NONE,
mipMaps, &encodingTime
);
for (int i = 0; i < numMips; i++) {
if (mipMaps[i].paucEncodingBits.get()) {
if (face >= 0) {
texture->assignStoredMipFace(i, face, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
} else {
texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
}
}
}
delete[] mipMaps;
#endif
} }
#endif #endif
void generateMips(gpu::Texture* texture, QImage&& image, const std::atomic<bool>& abortProcessing = false, int face = -1) { void generateMips(gpu::Texture* texture, QImage&& image, const std::atomic<bool>& abortProcessing = false, int face = -1) {
#if CPU_MIPMAPS #if CPU_MIPMAPS
PROFILE_RANGE(resource_parse, "generateMips"); PROFILE_RANGE(resource_parse, "generateMips");
#ifndef USE_GLES
if (image.format() == QIMAGE_HDR_FORMAT) { if (image.format() == QIMAGE_HDR_FORMAT) {
generateHDRMips(texture, std::move(image), abortProcessing, face); generateHDRMips(texture, std::move(image), abortProcessing, face);
} else { } else {
generateLDRMips(texture, std::move(image), abortProcessing, face); generateLDRMips(texture, std::move(image), abortProcessing, face);
} }
#else
generateLDRMips(texture, std::move(image), abortProcessing, face);
#endif
#else #else
texture->setAutoGenerateMips(true); texture->setAutoGenerateMips(true);
#endif #endif
@ -738,7 +826,6 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
gpu::Element formatMip; gpu::Element formatMip;
gpu::Element formatGPU; gpu::Element formatGPU;
if (isColorTexturesCompressionEnabled()) { if (isColorTexturesCompressionEnabled()) {
#ifndef USE_GLES
if (validAlpha) { if (validAlpha) {
// NOTE: This disables BC1a compression because it was producing odd artifacts on text textures // 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). // for the tutorial. Instead we use BC3 (which is larger) but doesn't produce the same artifacts).
@ -746,22 +833,15 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
} else { } else {
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGB; 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 { } else {
#ifdef USE_GLES #ifdef USE_GLES
// GLES does not support GL_BGRA // GLES does not support GL_BGRA
formatMip = gpu::Element::COLOR_SRGBA_32; formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA;
formatMip = formatGPU;
#else #else
formatGPU = gpu::Element::COLOR_SRGBA_32;
formatMip = gpu::Element::COLOR_SBGRA_32; formatMip = gpu::Element::COLOR_SBGRA_32;
#endif #endif
formatGPU = gpu::Element::COLOR_SRGBA_32;
} }
if (isStrict) { if (isStrict) {
@ -876,16 +956,18 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr
gpu::TexturePointer theTexture = nullptr; gpu::TexturePointer theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
gpu::Element formatMip = gpu::Element::VEC2NU8_XY; gpu::Element formatMip;
gpu::Element formatGPU = gpu::Element::VEC2NU8_XY; gpu::Element formatGPU;
if (isNormalTexturesCompressionEnabled()) { if (isNormalTexturesCompressionEnabled()) {
#ifndef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY; formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY;
#else } else {
#ifdef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY; formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY;
#else
formatGPU = gpu::Element::VEC2NU8_XY;
#endif #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 = 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); theTexture->setSource(srcImageName);
@ -917,16 +999,15 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr
gpu::Element formatMip; gpu::Element formatMip;
gpu::Element formatGPU; gpu::Element formatGPU;
if (isGrayscaleTexturesCompressionEnabled()) { if (isGrayscaleTexturesCompressionEnabled()) {
#ifndef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED; formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED;
#else
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED;
#endif
formatMip = formatGPU;
} else { } else {
formatMip = gpu::Element::COLOR_R_8; #ifdef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED;
#else
formatGPU = gpu::Element::COLOR_R_8; 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 = 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); theTexture->setSource(srcImageName);
@ -1283,19 +1364,25 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
QImage image = processSourceImage(std::move(localCopy), true); QImage image = processSourceImage(std::move(localCopy), true);
if (image.format() != QIMAGE_HDR_FORMAT) { if (image.format() != QIMAGE_HDR_FORMAT) {
#ifndef USE_GLES
image = convertToHDRFormat(std::move(image), HDR_FORMAT); image = convertToHDRFormat(std::move(image), HDR_FORMAT);
#else
image = image.convertToFormat(QImage::Format_RGB32);
#endif
} }
gpu::Element formatMip; gpu::Element formatMip;
gpu::Element formatGPU; gpu::Element formatGPU;
if (isCubeTexturesCompressionEnabled()) { if (isCubeTexturesCompressionEnabled()) {
// TODO: gles: pick HDR ETC format
formatMip = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB;
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB; formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB;
} else { } else {
formatMip = HDR_FORMAT; #ifdef USE_GLES
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB;
#else
formatGPU = HDR_FORMAT; formatGPU = HDR_FORMAT;
#endif
} }
formatMip = formatGPU;
// Find the layout of the cubemap in the 2D image // Find the layout of the cubemap in the 2D image
// Use the original image size since processSourceImage may have altered the size / aspect ratio // Use the original image size since processSourceImage may have altered the size / aspect ratio
@ -1342,9 +1429,16 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
// Generate irradiance while we are at it // Generate irradiance while we are at it
if (generateIrradiance) { if (generateIrradiance) {
PROFILE_RANGE(resource_parse, "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->setSource(srcImageName);
irradianceTexture->setStoredMipFormat(HDR_FORMAT); irradianceTexture->setStoredMipFormat(irradianceFormat);
for (uint8 face = 0; face < faces.size(); ++face) { for (uint8 face = 0; face < faces.size(); ++face) {
irradianceTexture->assignStoredMipFace(0, face, faces[face].byteCount(), faces[face].constBits()); irradianceTexture->assignStoredMipFace(0, face, faces[face].byteCount(), faces[face].constBits());
} }

View file

@ -350,6 +350,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) {
if (idxMoveStartingPointCandidate != -1) { if (idxMoveStartingPointCandidate != -1) {
_moveCurrentTouchId = tPoints[idxMoveStartingPointCandidate].id(); _moveCurrentTouchId = tPoints[idxMoveStartingPointCandidate].id();
_unusedTouches.erase(_moveCurrentTouchId); _unusedTouches.erase(_moveCurrentTouchId);
thisPoint.x = tPoints[idxMoveStartingPointCandidate].pos().x();
thisPoint.y = tPoints[idxMoveStartingPointCandidate].pos().y();
moveTouchBegin(thisPoint); moveTouchBegin(thisPoint);
} else { } else {
moveTouchEnd(); moveTouchEnd();
@ -359,6 +361,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) {
if (idxViewStartingPointCandidate != -1) { if (idxViewStartingPointCandidate != -1) {
_viewCurrentTouchId = tPoints[idxViewStartingPointCandidate].id(); _viewCurrentTouchId = tPoints[idxViewStartingPointCandidate].id();
_unusedTouches.erase(_viewCurrentTouchId); _unusedTouches.erase(_viewCurrentTouchId);
thisPoint.x = tPoints[idxViewStartingPointCandidate].pos().x();
thisPoint.y = tPoints[idxViewStartingPointCandidate].pos().y();
viewTouchBegin(thisPoint); viewTouchBegin(thisPoint);
} else { } else {
viewTouchEnd(); viewTouchEnd();
@ -368,6 +372,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) {
if (idxJumpStartingPointCandidate != -1) { if (idxJumpStartingPointCandidate != -1) {
_jumpCurrentTouchId = tPoints[idxJumpStartingPointCandidate].id(); _jumpCurrentTouchId = tPoints[idxJumpStartingPointCandidate].id();
_unusedTouches.erase(_jumpCurrentTouchId); _unusedTouches.erase(_jumpCurrentTouchId);
thisPoint.x = tPoints[idxJumpStartingPointCandidate].pos().x();
thisPoint.y = tPoints[idxJumpStartingPointCandidate].pos().y();
jumpTouchBegin(thisPoint); jumpTouchBegin(thisPoint);
} else { } else {
if (_jumpHasValidTouch) { if (_jumpHasValidTouch) {
@ -424,6 +430,7 @@ void TouchscreenVirtualPadDevice::moveTouchBegin(glm::vec2 touchPoint) {
} else { } else {
_moveRefTouchPoint = touchPoint; _moveRefTouchPoint = touchPoint;
} }
_moveCurrentTouchPoint = touchPoint;
_moveHasValidTouch = true; _moveHasValidTouch = true;
} }
} }

View file

@ -446,17 +446,13 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
// this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor // this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor
assert(!(_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID())); assert(!(_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()));
// shouldSendUpdate() sould NOT be triggering updates to maintain the queryAACube of dynamic entities. if (_entity->dynamicDataNeedsTransmit()) {
// 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())) {
return true; return true;
} }
if (_entity->shouldSuppressLocationEdits()) { 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); return remoteSimulationOutOfSync(simulationStep);

View file

@ -87,7 +87,8 @@ void CauterizedModel::createVisibleRenderItemSet() {
for (int partIndex = 0; partIndex < numParts; partIndex++) { for (int partIndex = 0; partIndex < numParts; partIndex++) {
auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset); auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
_modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr); _modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(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 }); _modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
shapeID++; shapeID++;
} }

View file

@ -149,6 +149,7 @@ public:
Builder& withTypeMeta() { _flags.set(TYPE_META); return (*this); } Builder& withTypeMeta() { _flags.set(TYPE_META); return (*this); }
Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); } Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); }
Builder& withViewSpace() { _flags.set(VIEW_SPACE); 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& withDynamic() { _flags.set(DYNAMIC); return (*this); }
Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } Builder& withDeformed() { _flags.set(DEFORMED); return (*this); }
Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }

View file

@ -419,7 +419,7 @@ void ScriptEngine::waitTillDoneRunning() {
// Wait for the scripting thread to stop running, as // Wait for the scripting thread to stop running, as
// flooding it with aborts/exceptions will persist it longer // flooding it with aborts/exceptions will persist it longer
static const auto MAX_SCRIPT_QUITTING_TIME = 0.5 * MSECS_PER_SECOND; 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(); workerThread->terminate();
} }
} }

View file

@ -32,15 +32,13 @@
#include "shared/GlobalAppProperties.h" #include "shared/GlobalAppProperties.h"
#include "SharedUtil.h" #include "SharedUtil.h"
// Format: AppName-PID-Timestamp // Format: AppName-PID-Timestamp
// Example: ... // Example: ...
QString TEMP_DIR_FORMAT { "%1-%2-%3" }; QString TEMP_DIR_FORMAT { "%1-%2-%3" };
#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD) #if !defined(Q_OS_ANDROID) && defined(DEV_BUILD)
static bool USE_SOURCE_TREE_RESOURCES() { static bool USE_SOURCE_TREE_RESOURCES() {
#if defined(Q_OS_OSX)
return true;
#else
static bool result = false; static bool result = false;
static std::once_flag once; static std::once_flag once;
std::call_once(once, [&] { std::call_once(once, [&] {
@ -48,10 +46,28 @@ static bool USE_SOURCE_TREE_RESOURCES() {
result = QProcessEnvironment::systemEnvironment().contains(USE_SOURCE_TREE_RESOURCES_FLAG); result = QProcessEnvironment::systemEnvironment().contains(USE_SOURCE_TREE_RESOURCES_FLAG);
}); });
return result; return result;
#endif
} }
#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 #ifdef DEV_BUILD
const QString& PathUtils::projectRootPath() { const QString& PathUtils::projectRootPath() {
static QString sourceFolder; static QString sourceFolder;
@ -65,23 +81,9 @@ const QString& PathUtils::projectRootPath() {
#endif #endif
const QString& PathUtils::resourcesPath() { const QString& PathUtils::resourcesPath() {
static QString staticResourcePath; static QString staticResourcePath{ ":/" };
static std::once_flag once; static std::once_flag once;
std::call_once(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 !defined(Q_OS_ANDROID) && defined(DEV_BUILD)
if (USE_SOURCE_TREE_RESOURCES()) { if (USE_SOURCE_TREE_RESOURCES()) {
// For dev builds, optionally load content from the Git source tree // For dev builds, optionally load content from the Git source tree
@ -90,21 +92,13 @@ const QString& PathUtils::resourcesPath() {
#endif #endif
qDebug() << "Resource path resolved to " << staticResourcePath; qDebug() << "Resource path resolved to " << staticResourcePath;
}); });
return staticResourcePath; return staticResourcePath;
} }
const QString& PathUtils::resourcesUrl() { const QString& PathUtils::resourcesUrl() {
static QString staticResourcePath; static QString staticResourcePath{ "qrc:///" };
static std::once_flag once; static std::once_flag once;
std::call_once(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 !defined(Q_OS_ANDROID) && defined(DEV_BUILD)
if (USE_SOURCE_TREE_RESOURCES()) { if (USE_SOURCE_TREE_RESOURCES()) {
// For dev builds, optionally load content from the Git source tree // For dev builds, optionally load content from the Git source tree

View file

@ -37,6 +37,7 @@ class PathUtils : public QObject, public Dependency {
Q_PROPERTY(QString resources READ resourcesPath CONSTANT) Q_PROPERTY(QString resources READ resourcesPath CONSTANT)
Q_PROPERTY(QUrl defaultScripts READ defaultScriptsLocation CONSTANT) Q_PROPERTY(QUrl defaultScripts READ defaultScriptsLocation CONSTANT)
public: public:
static const QString& getRccPath();
static const QString& resourcesUrl(); static const QString& resourcesUrl();
static QUrl resourcesUrl(const QString& relative); static QUrl resourcesUrl(const QString& relative);
static const QString& resourcesPath(); static const QString& resourcesPath();

View file

@ -47,7 +47,7 @@ static_assert(
const char* SDL2Manager::NAME = "SDL2"; const char* SDL2Manager::NAME = "SDL2";
const char* SDL2Manager::SDL2_ID_STRING = "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_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) {
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller); SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);

View file

@ -15,19 +15,25 @@
#include "OculusHelpers.h" #include "OculusHelpers.h"
using namespace hifi;
void OculusBaseDisplayPlugin::resetSensors() { void OculusBaseDisplayPlugin::resetSensors() {
ovr_RecenterTrackingOrigin(_session); ovr_RecenterTrackingOrigin(_session);
_currentRenderFrameInfo.renderPose = glm::mat4(); // identity _currentRenderFrameInfo.renderPose = glm::mat4(); // identity
} }
bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) { bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
handleOVREvents(); ovrSessionStatus status{};
if (quitRequested()) { 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"); QMetaObject::invokeMethod(qApp, "quit");
return false; return false;
} }
if (reorientRequested()) { if (ovr::reorientRequested(status)) {
emit resetSensorsRequested(); emit resetSensorsRequested();
} }
@ -35,18 +41,18 @@ bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
_currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds(); _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
_currentRenderFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, frameIndex); _currentRenderFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, frameIndex);
auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrTrue); 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; _currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose;
std::array<glm::mat4, 2> handPoses; std::array<glm::mat4, 2> handPoses;
// Make controller poses available to the presentation thread // 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; static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked | ovrStatus_PositionTracked;
if (REQUIRED_HAND_STATUS != (trackingState.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) { if (REQUIRED_HAND_STATUS != (trackingState.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) {
return; 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); 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); 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 { bool OculusBaseDisplayPlugin::isSupported() const {
return oculusAvailable(); return ovr::available();
} }
glm::mat4 OculusBaseDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& baseProjection) const { 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]; ovrFovPort fovPort = _hmdDesc.DefaultEyeFov[eye];
ovrEyeRenderDesc& erd = ovr_GetRenderDesc(_session, ovrEye, fovPort); ovrEyeRenderDesc& erd = ovr_GetRenderDesc(_session, ovrEye, fovPort);
ovrMatrix4f ovrPerspectiveProjection = ovrMatrix4f_Projection(erd.Fov, baseNearClip, baseFarClip, ovrProjection_ClipRangeOpenGL); ovrMatrix4f ovrPerspectiveProjection = ovrMatrix4f_Projection(erd.Fov, baseNearClip, baseFarClip, ovrProjection_ClipRangeOpenGL);
return toGlm(ovrPerspectiveProjection); return ovr::toGlm(ovrPerspectiveProjection);
} else { } else {
return baseProjection; return baseProjection;
} }
@ -85,7 +91,7 @@ glm::mat4 OculusBaseDisplayPlugin::getCullingProjection(const glm::mat4& basePro
float baseFarClip = baseFrustum.getFarClip(); float baseFarClip = baseFrustum.getFarClip();
auto combinedFov = _eyeFovs[0]; auto combinedFov = _eyeFovs[0];
combinedFov.LeftTan = combinedFov.RightTan = std::max(combinedFov.LeftTan, combinedFov.RightTan); 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 { } else {
return baseProjection; return baseProjection;
} }
@ -102,7 +108,7 @@ void OculusBaseDisplayPlugin::uncustomizeContext() {
} }
bool OculusBaseDisplayPlugin::internalActivate() { bool OculusBaseDisplayPlugin::internalActivate() {
_session = acquireOculusSession(); _session = ovr::acquireRenderSession();
if (!_session) { if (!_session) {
return false; return false;
} }
@ -113,21 +119,21 @@ bool OculusBaseDisplayPlugin::internalActivate() {
_viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; _viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
_ipd = 0; _ipd = 0;
ovr_for_each_eye([&](ovrEyeType eye) { ovr::for_each_eye([&](ovrEyeType eye) {
_eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye]; _eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye];
ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_session, eye, _eyeFovs[eye]); ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_session, eye, _eyeFovs[eye]);
ovrMatrix4f ovrPerspectiveProjection = ovrMatrix4f ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_ClipRangeOpenGL); ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_ClipRangeOpenGL);
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection); _eyeProjections[eye] = ovr::toGlm(ovrPerspectiveProjection);
_eyeOffsets[eye] = glm::translate(mat4(), toGlm(erd.HmdToEyeOffset)); _eyeOffsets[eye] = ovr::toGlm(erd.HmdToEyePose);
eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_session, eye, erd.Fov, 1.0f)); eyeSizes[eye] = ovr::toGlm(ovr_GetFovTextureSize(_session, eye, erd.Fov, 1.0f));
_viewScaleDesc.HmdToEyeOffset[eye] = erd.HmdToEyeOffset; _viewScaleDesc.HmdToEyePose[eye] = erd.HmdToEyePose;
_ipd += glm::abs(glm::length(toGlm(erd.HmdToEyeOffset))); _ipd += glm::abs(glm::length(ovr::toGlm(erd.HmdToEyePose.Position)));
}); });
auto combinedFov = _eyeFovs[0]; auto combinedFov = _eyeFovs[0];
combinedFov.LeftTan = combinedFov.RightTan = std::max(combinedFov.LeftTan, combinedFov.RightTan); 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( _renderTargetSize = uvec2(
eyeSizes[0].x + eyeSizes[1].x, eyeSizes[0].x + eyeSizes[1].x,
@ -136,7 +142,7 @@ bool OculusBaseDisplayPlugin::internalActivate() {
memset(&_sceneLayer, 0, sizeof(ovrLayerEyeFov)); memset(&_sceneLayer, 0, sizeof(ovrLayerEyeFov));
_sceneLayer.Header.Type = ovrLayerType_EyeFov; _sceneLayer.Header.Type = ovrLayerType_EyeFov;
_sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; _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; ovrFovPort & fov = _sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov;
ovrSizei & size = _sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_session, eye, fov, 1.0f); ovrSizei & size = _sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_session, eye, fov, 1.0f);
_sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 }; _sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 };
@ -150,28 +156,14 @@ bool OculusBaseDisplayPlugin::internalActivate() {
void OculusBaseDisplayPlugin::internalDeactivate() { void OculusBaseDisplayPlugin::internalDeactivate() {
Parent::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() { void OculusBaseDisplayPlugin::updatePresentPose() {
ovrTrackingState trackingState;
_currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds(); _currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
_currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, 0); _currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, 0);
auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse); trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse);
_currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose); _currentPresentFrameInfo.presentPose = ovr::toGlm(trackingState.HeadPose.ThePose);
_currentPresentFrameInfo.renderPose = _currentPresentFrameInfo.presentPose; _currentPresentFrameInfo.renderPose = _currentPresentFrameInfo.presentPose;
} }
OculusBaseDisplayPlugin::~OculusBaseDisplayPlugin() {
}

View file

@ -16,37 +16,32 @@
class OculusBaseDisplayPlugin : public HmdDisplayPlugin { class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
using Parent = HmdDisplayPlugin; using Parent = HmdDisplayPlugin;
public: public:
~OculusBaseDisplayPlugin();
bool isSupported() const override; 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 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override;
glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override; glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override;
bool hasAsyncReprojection() const override { return true; }
// Stereo specific methods // Stereo specific methods
void resetSensors() override final; void resetSensors() override final;
bool beginFrameRender(uint32_t frameIndex) override; bool beginFrameRender(uint32_t frameIndex) override;
float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; } float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; }
bool getSupportsAutoSwitch() override final { return true; }
protected: protected:
void customizeContext() override; void customizeContext() override;
void uncustomizeContext() override; void uncustomizeContext() override;
bool internalActivate() override; bool internalActivate() override;
void internalDeactivate() override; void internalDeactivate() override;
bool activateStandBySession() override;
void deactivateSession() override;
void updatePresentPose() override; void updatePresentPose() override;
protected: protected:
ovrSession _session { nullptr }; ovrSession _session{ nullptr };
ovrGraphicsLuid _luid; ovrGraphicsLuid _luid;
ovrEyeRenderDesc _eyeRenderDescs[2]; std::array<ovrEyeRenderDesc, 2> _eyeRenderDescs;
ovrFovPort _eyeFovs[2]; std::array<ovrFovPort, 2> _eyeFovs;
ovrHmdDesc _hmdDesc; ovrHmdDesc _hmdDesc;
ovrLayerEyeFov _sceneLayer; ovrLayerEyeFov _sceneLayer;
ovrViewScaleDesc _viewScaleDesc; ovrViewScaleDesc _viewScaleDesc;
// ovrLayerEyeFovDepth _depthLayer;
}; };

View file

@ -22,34 +22,23 @@
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <StreamUtils.h> #include <StreamUtils.h>
#include <OVR_CAPI.h> #include <ovr_capi.h>
#include "OculusHelpers.h" #include "OculusHelpers.h"
Q_DECLARE_LOGGING_CATEGORY(oculus) using namespace hifi;
static const char* MENU_PARENT = "Avatar";
static const char* MENU_NAME = "Oculus Touch Controllers";
static const char* MENU_PATH = "Avatar" ">" "Oculus Touch Controllers";
const char* OculusControllerManager::NAME = "Oculus"; const char* OculusControllerManager::NAME = "Oculus";
const quint64 LOST_TRACKING_DELAY = 3000000; const quint64 LOST_TRACKING_DELAY = 3000000;
bool OculusControllerManager::isSupported() const { bool OculusControllerManager::isSupported() const {
return oculusAvailable(); return hifi::ovr::available();
} }
bool OculusControllerManager::activate() { bool OculusControllerManager::activate() {
InputPlugin::activate(); InputPlugin::activate();
if (!_session) {
_session = acquireOculusSession();
}
Q_ASSERT(_session);
checkForConnectedDevices(); checkForConnectedDevices();
return true; return true;
} }
@ -58,33 +47,30 @@ void OculusControllerManager::checkForConnectedDevices() {
return; 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 (!_remote && (controllerConnected & ovrControllerType_Remote) == ovrControllerType_Remote) {
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Remote, &_inputState))) { if (OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Remote, &_inputState))) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
_remote = std::make_shared<RemoteDevice>(*this); _remote = std::make_shared<RemoteDevice>(*this);
userInputMapper->registerDevice(_remote); userInputMapper->registerDevice(_remote);
}
} }
}
if (!_touch && (controllerConnected & ovrControllerType_Touch) != 0) { if (!_touch && (controllerConnected & ovrControllerType_Touch) != 0) {
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) { if (OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Touch, &_inputState))) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
_touch = std::make_shared<TouchDevice>(*this); _touch = std::make_shared<TouchDevice>(*this);
userInputMapper->registerDevice(_touch); userInputMapper->registerDevice(_touch);
}
} }
} });
} }
void OculusControllerManager::deactivate() { void OculusControllerManager::deactivate() {
InputPlugin::deactivate(); InputPlugin::deactivate();
if (_session) {
releaseOculusSession();
_session = nullptr;
}
// unregister with UserInputMapper // unregister with UserInputMapper
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
if (_touch) { if (_touch) {
@ -100,20 +86,30 @@ void OculusControllerManager::pluginUpdate(float deltaTime, const controller::In
checkForConnectedDevices(); checkForConnectedDevices();
if (_touch) { bool updateRemote = false, updateTouch = false;
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) {
_touch->update(deltaTime, inputCalibrationData); ovr::withSession([&](ovrSession session) {
} else { if (_touch) {
qCWarning(oculus) << "Unable to read Oculus touch input state"; 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 (_remote && updateRemote) {
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Remote, &_inputState))) { _remote->update(deltaTime, inputCalibrationData);
_remote->update(deltaTime, inputCalibrationData);
} else {
qCWarning(oculus) << "Unable to read Oculus remote input state";
}
} }
} }
@ -210,15 +206,6 @@ void OculusControllerManager::RemoteDevice::focusOutEvent() {
_buttonPressedMap.clear(); _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, void OculusControllerManager::TouchDevice::update(float deltaTime,
const controller::InputCalibrationData& inputCalibrationData) { const controller::InputCalibrationData& inputCalibrationData) {
_buttonPressedMap.clear(); _buttonPressedMap.clear();
@ -226,10 +213,19 @@ void OculusControllerManager::TouchDevice::update(float deltaTime,
int numTrackedControllers = 0; int numTrackedControllers = 0;
quint64 currentTime = usecTimestampNow(); quint64 currentTime = usecTimestampNow();
static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked | ovrStatus_PositionTracked; static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked | ovrStatus_PositionTracked;
auto tracking = ovr_GetTrackingState(_parent._session, 0, false); bool hasInputFocus = ovr::hasInputFocus();
ovr_for_each_hand([&](ovrHandType hand) { auto tracking = ovr::getTrackingState(); // ovr_GetTrackingState(_parent._session, 0, false);
ovr::for_each_hand([&](ovrHandType hand) {
++numTrackedControllers; ++numTrackedControllers;
int controller = (hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND); 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)) { if (REQUIRED_HAND_STATUS == (tracking.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) {
_poseStateMap.erase(controller); _poseStateMap.erase(controller);
handlePose(deltaTime, inputCalibrationData, hand, tracking.HandPoses[hand]); handlePose(deltaTime, inputCalibrationData, hand, tracking.HandPoses[hand]);
@ -253,7 +249,7 @@ void OculusControllerManager::TouchDevice::update(float deltaTime,
handleRotationForUntrackedHand(inputCalibrationData, hand, tracking.HandPoses[hand]); handleRotationForUntrackedHand(inputCalibrationData, hand, tracking.HandPoses[hand]);
}); });
if (_parent.isHeadControllerMounted()) { if (ovr::hmdMounted()) {
handleHeadPose(deltaTime, inputCalibrationData, tracking.HeadPose); handleHeadPose(deltaTime, inputCalibrationData, tracking.HeadPose);
} else { } else {
_poseStateMap[controller::HEAD].valid = false; _poseStateMap[controller::HEAD].valid = false;
@ -311,7 +307,7 @@ void OculusControllerManager::TouchDevice::handlePose(float deltaTime,
ovrHandType hand, const ovrPoseStatef& handPose) { ovrHandType hand, const ovrPoseStatef& handPose) {
auto poseId = hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND; auto poseId = hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND;
auto& pose = _poseStateMap[poseId]; auto& pose = _poseStateMap[poseId];
pose = ovrControllerPoseToHandPose(hand, handPose); pose = ovr::toControllerPose(hand, handPose);
// transform into avatar frame // transform into avatar frame
glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
pose = pose.transform(controllerToAvatar); pose = pose.transform(controllerToAvatar);
@ -320,15 +316,15 @@ void OculusControllerManager::TouchDevice::handlePose(float deltaTime,
void OculusControllerManager::TouchDevice::handleHeadPose(float deltaTime, void OculusControllerManager::TouchDevice::handleHeadPose(float deltaTime,
const controller::InputCalibrationData& inputCalibrationData, const controller::InputCalibrationData& inputCalibrationData,
const ovrPoseStatef& headPose) { const ovrPoseStatef& headPose) {
glm::mat4 mat = createMatFromQuatAndPos(toGlm(headPose.ThePose.Orientation), glm::mat4 mat = createMatFromQuatAndPos(ovr::toGlm(headPose.ThePose.Orientation),
toGlm(headPose.ThePose.Position)); ovr::toGlm(headPose.ThePose.Position));
//perform a 180 flip to make the HMD face the +z instead of -z, beacuse the head faces +z //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; glm::mat4 matYFlip = mat * Matrices::Y_180;
controller::Pose pose(extractTranslation(matYFlip), controller::Pose pose(extractTranslation(matYFlip),
glmExtractRotation(matYFlip), glmExtractRotation(matYFlip),
toGlm(headPose.LinearVelocity), // XXX * matYFlip ? ovr::toGlm(headPose.LinearVelocity), // XXX * matYFlip ?
toGlm(headPose.AngularVelocity)); ovr::toGlm(headPose.AngularVelocity));
glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) * 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 poseId = (hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND);
auto& pose = _poseStateMap[poseId]; auto& pose = _poseStateMap[poseId];
auto lastHandPose = _lastControllerPose[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; glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
pose = pose.transform(controllerToAvatar); pose = pose.transform(controllerToAvatar);
} }
@ -351,36 +347,40 @@ void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const
bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) {
Locker locker(_lock); Locker locker(_lock);
bool toReturn = true; bool toReturn = true;
if (hand == controller::BOTH || hand == controller::LEFT) { ovr::withSession([&](ovrSession session) {
if (strength == 0.0f) { if (hand == controller::BOTH || hand == controller::LEFT) {
_leftHapticStrength = 0.0f; if (strength == 0.0f) {
_leftHapticDuration = 0.0f; _leftHapticStrength = 0.0f;
} else { _leftHapticDuration = 0.0f;
_leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength; } else {
if (ovr_SetControllerVibration(_parent._session, ovrControllerType_LTouch, 1.0f, _leftHapticStrength) != ovrSuccess) { _leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength;
toReturn = false; 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 (hand == controller::BOTH || hand == controller::RIGHT) { if (strength == 0.0f) {
if (strength == 0.0f) { _rightHapticStrength = 0.0f;
_rightHapticStrength = 0.0f; _rightHapticDuration = 0.0f;
_rightHapticDuration = 0.0f; } else {
} else { _rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength;
_rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength; if (ovr_SetControllerVibration(session, ovrControllerType_RTouch, 1.0f, _rightHapticStrength) != ovrSuccess) {
if (ovr_SetControllerVibration(_parent._session, ovrControllerType_RTouch, 1.0f, _rightHapticStrength) != ovrSuccess) { toReturn = false;
toReturn = false; }
_rightHapticDuration = std::max(duration, _rightHapticDuration);
} }
_rightHapticDuration = std::max(duration, _rightHapticDuration);
} }
} });
return toReturn; return toReturn;
} }
void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) { void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) {
auto handType = (leftHand ? ovrControllerType_LTouch : ovrControllerType_RTouch); 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 /**jsdoc

View file

@ -26,10 +26,8 @@ public:
// Plugin functions // Plugin functions
bool isSupported() const override; bool isSupported() const override;
const QString getName() const override { return NAME; } const QString getName() const override { return NAME; }
bool isHandController() const override { return _touch != nullptr; } bool isHandController() const override { return _touch != nullptr; }
bool isHeadController() const override { return true; } bool isHeadController() const override { return true; }
bool isHeadControllerMounted() const;
QStringList getSubdeviceNames() override; QStringList getSubdeviceNames() override;
bool activate() override; bool activate() override;
@ -105,7 +103,6 @@ private:
void checkForConnectedDevices(); void checkForConnectedDevices();
ovrSession _session { nullptr };
ovrInputState _inputState {}; ovrInputState _inputState {};
RemoteDevice::Pointer _remote; RemoteDevice::Pointer _remote;
TouchDevice::Pointer _touch; TouchDevice::Pointer _touch;

View file

@ -19,6 +19,8 @@
#include "OculusHelpers.h" #include "OculusHelpers.h"
using namespace hifi;
const char* OculusDisplayPlugin::NAME { "Oculus Rift" }; const char* OculusDisplayPlugin::NAME { "Oculus Rift" };
static ovrPerfHudMode currentDebugMode = ovrPerfHud_Off; static ovrPerfHudMode currentDebugMode = ovrPerfHud_Off;
@ -63,7 +65,7 @@ void OculusDisplayPlugin::cycleDebugOutput() {
void OculusDisplayPlugin::customizeContext() { void OculusDisplayPlugin::customizeContext() {
Parent::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 = { }; ovrTextureSwapChainDesc desc = { };
desc.Type = ovrTexture_2D; desc.Type = ovrTexture_2D;
desc.ArraySize = 1; desc.ArraySize = 1;
@ -76,14 +78,14 @@ void OculusDisplayPlugin::customizeContext() {
ovrResult result = ovr_CreateTextureSwapChainGL(_session, &desc, &_textureSwapChain); ovrResult result = ovr_CreateTextureSwapChainGL(_session, &desc, &_textureSwapChain);
if (!OVR_SUCCESS(result)) { if (!OVR_SUCCESS(result)) {
logCritical("Failed to create swap textures"); qCritical(oculusLog) << "Failed to create swap textures" << ovr::getError();
return; return;
} }
int length = 0; int length = 0;
result = ovr_GetTextureSwapChainLength(_session, _textureSwapChain, &length); result = ovr_GetTextureSwapChainLength(_session, _textureSwapChain, &length);
if (!OVR_SUCCESS(result) || !length) { if (!OVR_SUCCESS(result) || !length) {
logCritical("Unable to count swap chain textures"); qCritical(oculusLog) << "Unable to count swap chain textures" << ovr::getError();
return; return;
} }
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
@ -164,8 +166,8 @@ void OculusDisplayPlugin::hmdPresent() {
auto result = ovr_CommitTextureSwapChain(_session, _textureSwapChain); auto result = ovr_CommitTextureSwapChain(_session, _textureSwapChain);
Q_ASSERT(OVR_SUCCESS(result)); Q_ASSERT(OVR_SUCCESS(result));
_sceneLayer.SensorSampleTime = _currentPresentFrameInfo.sensorSampleTime; _sceneLayer.SensorSampleTime = _currentPresentFrameInfo.sensorSampleTime;
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose); _sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovr::poseFromGlm(_currentPresentFrameInfo.renderPose);
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovrPoseFromGlm(_currentPresentFrameInfo.renderPose); _sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovr::poseFromGlm(_currentPresentFrameInfo.renderPose);
auto submitStart = usecTimestampNow(); auto submitStart = usecTimestampNow();
uint64_t nonSubmitInterval = 0; uint64_t nonSubmitInterval = 0;
@ -192,7 +194,7 @@ void OculusDisplayPlugin::hmdPresent() {
} }
if (!OVR_SUCCESS(result)) { if (!OVR_SUCCESS(result)) {
logWarning("Failed to present"); qWarning(oculusLog) << "Failed to present" << ovr::getError();
} }
static int compositorDroppedFrames = 0; static int compositorDroppedFrames = 0;
@ -234,9 +236,7 @@ QJsonObject OculusDisplayPlugin::getHardwareStats() const {
} }
bool OculusDisplayPlugin::isHmdMounted() const { bool OculusDisplayPlugin::isHmdMounted() const {
ovrSessionStatus status; return ovr::hmdMounted();
return (OVR_SUCCESS(ovr_GetSessionStatus(_session, &status)) &&
(ovrFalse != status.HmdMounted));
} }
QString OculusDisplayPlugin::getPreferredAudioInDevice() const { QString OculusDisplayPlugin::getPreferredAudioInDevice() const {

View file

@ -24,41 +24,17 @@
#include <NumericalConstants.h> #include <NumericalConstants.h>
Q_LOGGING_CATEGORY(displayplugins, "hifi.plugins.display") Q_LOGGING_CATEGORY(displayplugins, "hifi.plugins.display")
Q_LOGGING_CATEGORY(oculus, "hifi.plugins.display.oculus") Q_LOGGING_CATEGORY(oculusLog, "hifi.plugins.display.oculus")
static std::atomic<uint32_t> 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());
}
using namespace hifi;
static wchar_t* REQUIRED_OCULUS_DLL = L"LibOVRRT64_1.dll"; static wchar_t* REQUIRED_OCULUS_DLL = L"LibOVRRT64_1.dll";
static wchar_t FOUND_PATH[MAX_PATH]; static wchar_t FOUND_PATH[MAX_PATH];
bool oculusAvailable() { bool ovr::available() {
static std::once_flag once; static std::once_flag once;
static bool result { false }; static bool result{ false };
std::call_once(once, [&] { std::call_once(once, [&] {
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR"); static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR");
static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
if (enableDebugOpenVR) { if (enableDebugOpenVR) {
@ -81,113 +57,107 @@ bool oculusAvailable() {
return result; return result;
} }
ovrSession acquireOculusSession() { class ovrImpl {
if (!session && !oculusAvailable()) { using Mutex = std::mutex;
qCDebug(oculus) << "oculus: no runtime or HMD present"; using Lock = std::unique_lock<Mutex>;
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<void(ovrSession)>& f) {
Lock lock(mutex);
if (!session) {
setupSession(false);
}
f(session);
}
ovrSession acquireRenderSession() {
Lock lock(mutex);
if (renderCount++ == 0) {
releaseSession();
setupSession(true);
}
return session; return session;
} }
if (!session) { void releaseRenderSession(ovrSession session) {
ovrInitParams initParams { Lock lock(mutex);
ovrInit_RequestVersion | ovrInit_MixedRendering, OVR_MINOR_VERSION, nullptr, 0, 0 if (--renderCount == 0) {
}; releaseSession();
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;
} }
} }
} _ovr;
++refCount; ovrSession ovr::acquireRenderSession() {
return session; return _ovr.acquireRenderSession();
} }
void releaseOculusSession() { void ovr::releaseRenderSession(ovrSession session) {
Q_ASSERT(refCount > 0 && session); _ovr.releaseRenderSession(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 handleOVREvents() { void ovr::withSession(const std::function<void(ovrSession)>& f) {
if (!session) { _ovr.withSession(f);
return; }
}
ovrSessionStatus status; ovrSessionStatus ovr::getStatus() {
if (!OVR_SUCCESS(ovr_GetSessionStatus(session, &status))) { ovrSessionStatus status{};
return; withSession([&](ovrSession session) {
} if (!OVR_SUCCESS(ovr_GetSessionStatus(session, &status))) {
qCWarning(oculusLog) << "Failed to get session status" << ovr::getError();
_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();
} }
} });
#endif return status;
} }
bool quitRequested() { ovrTrackingState ovr::getTrackingState() {
return _quitRequested; ovrTrackingState result{};
} withSession([&](ovrSession session) { result = ovr_GetTrackingState(session, 0, ovrFalse); });
bool reorientRequested() { return result;
return _reorientRequested;
} }
controller::Pose ovrControllerPoseToHandPose( QString ovr::getError() {
ovrHandType hand, static ovrErrorInfo error;
const ovrPoseStatef& handPose) { 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: // When the sensor-to-world rotation is identity the coordinate axes look like this:
// //
// user // user
@ -247,9 +217,8 @@ controller::Pose ovrControllerPoseToHandPose(
static const glm::quat rightRotationOffset = glm::inverse(rightQuarterZ) * touchToHand; static const glm::quat rightRotationOffset = glm::inverse(rightQuarterZ) * touchToHand;
static const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches static const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches
static const glm::vec3 CONTROLLER_OFFSET = glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f, static const glm::vec3 CONTROLLER_OFFSET =
-CONTROLLER_LENGTH_OFFSET / 2.0f, glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f, -CONTROLLER_LENGTH_OFFSET / 2.0f, CONTROLLER_LENGTH_OFFSET * 1.5f);
CONTROLLER_LENGTH_OFFSET * 1.5f);
static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET; static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET;
static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET; static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET;
@ -268,12 +237,13 @@ controller::Pose ovrControllerPoseToHandPose(
return pose; return pose;
} }
controller::Pose ovrControllerRotationToHandRotation(ovrHandType hand, const ovrPoseStatef& handPose, controller::Pose hifi::ovr::toControllerPose(ovrHandType hand,
const ovrPoseStatef& lastHandPose) { const ovrPoseStatef& handPose,
const ovrPoseStatef& lastHandPose) {
static const glm::quat yFlip = glm::angleAxis(PI, Vectors::UNIT_Y); 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 quarterX = glm::angleAxis(PI_OVER_TWO, Vectors::UNIT_X);
static const glm::quat touchToHand = yFlip * quarterX; static const glm::quat touchToHand = yFlip * quarterX;
static const glm::quat leftQuarterZ = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_Z); 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); 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 glm::quat rightRotationOffset = glm::inverse(rightQuarterZ) * touchToHand;
static const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches static const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches
static const glm::vec3 CONTROLLER_OFFSET = glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f, static const glm::vec3 CONTROLLER_OFFSET =
-CONTROLLER_LENGTH_OFFSET / 2.0f, glm::vec3(CONTROLLER_LENGTH_OFFSET / 2.0f, -CONTROLLER_LENGTH_OFFSET / 2.0f, CONTROLLER_LENGTH_OFFSET * 1.5f);
CONTROLLER_LENGTH_OFFSET * 1.5f);
static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET; static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET;
static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET; static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET;
@ -301,3 +270,52 @@ controller::Pose ovrControllerRotationToHandRotation(ovrHandType hand, const ovr
pose.valid = true; pose.valid = true;
return pose; 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

View file

@ -7,10 +7,9 @@
// //
#pragma once #pragma once
#include <QtCore/QLoggingCategory> #include <QtCore/QLoggingCategory>
#include <OVR_CAPI_GL.h> #include <ovr_capi.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
@ -18,106 +17,107 @@
#include <controllers/Forward.h> #include <controllers/Forward.h>
Q_DECLARE_LOGGING_CATEGORY(displayplugins) Q_DECLARE_LOGGING_CATEGORY(displayplugins)
Q_DECLARE_LOGGING_CATEGORY(oculus) Q_DECLARE_LOGGING_CATEGORY(oculusLog)
void logWarning(const char* what); namespace hifi {
void logCritical(const char* what);
bool oculusAvailable(); struct ovr {
ovrSession acquireOculusSession(); static bool available();
void releaseOculusSession(); static ovrSession acquireRenderSession();
static void releaseRenderSession(ovrSession session);
static void withSession(const std::function<void(ovrSession)>& f);
static ovrSessionStatus getStatus();
static ovrTrackingState getTrackingState();
static QString getError();
void handleOVREvents(); static inline bool quitRequested() { return quitRequested(getStatus()); }
bool quitRequested(); static inline bool reorientRequested() { return reorientRequested(getStatus()); }
bool reorientRequested(); static inline bool hmdMounted() { return hmdMounted(getStatus()); }
static inline bool hasInputFocus() { return hasInputFocus(getStatus()); }
// Convenience method for looping over each eye with a lambda static inline bool quitRequested(const ovrSessionStatus& status) { return status.ShouldQuit != ovrFalse; }
template <typename Function> static inline bool reorientRequested(const ovrSessionStatus& status) { return status.ShouldRecenter != ovrFalse; }
inline void ovr_for_each_eye(Function function) { static inline bool hmdMounted(const ovrSessionStatus& status) { return status.HmdMounted != ovrFalse; }
for (ovrEyeType eye = ovrEyeType::ovrEye_Left; static inline bool hasInputFocus(const ovrSessionStatus& status) { return status.HasInputFocus != ovrFalse; }
eye < ovrEyeType::ovrEye_Count;
eye = static_cast<ovrEyeType>(eye + 1)) { // Convenience method for looping over each eye with a lambda
function(eye); static inline void for_each_eye(const std::function<void(ovrEyeType eye)>& f) {
for (ovrEyeType eye = ovrEye_Left; eye < ovrEye_Count; eye = static_cast<ovrEyeType>(eye + 1)) {
f(eye);
}
} }
}
template <typename Function> static inline void for_each_hand(const std::function<void(ovrHandType eye)>& f) {
inline void ovr_for_each_hand(Function function) { for (ovrHandType hand = ovrHand_Left; hand < ovrHand_Count; hand = static_cast<ovrHandType>(hand + 1)) {
for (ovrHandType hand = ovrHandType::ovrHand_Left; f(hand);
hand <= ovrHandType::ovrHand_Right; }
hand = static_cast<ovrHandType>(hand + 1)) {
function(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) { static inline glm::uvec2 toGlm(const ovrSizei& ov) {
return glm::transpose(glm::make_mat4(&om.M[0][0])); return glm::uvec2(ov.w, ov.h);
} }
inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) { static inline glm::quat toGlm(const ovrQuatf& oq) {
return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true)); return glm::make_quat(&oq.x);
} }
inline glm::vec3 toGlm(const ovrVector3f & ov) { static inline glm::mat4 toGlm(const ovrPosef& op) {
return glm::make_vec3(&ov.x); 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) { static inline ovrMatrix4f fromGlm(const glm::mat4& m) {
return glm::make_vec2(&ov.x); 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) { static inline ovrVector3f fromGlm(const glm::vec3& v) {
return glm::uvec2(ov.w, ov.h); return { v.x, v.y, v.z };
} }
inline glm::quat toGlm(const ovrQuatf & oq) { static inline ovrVector2f fromGlm(const glm::vec2& v) {
return glm::make_quat(&oq.x); return { v.x, v.y };
} }
inline glm::mat4 toGlm(const ovrPosef & op) { static inline ovrSizei fromGlm(const glm::uvec2& v) {
glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation)); return { (int)v.x, (int)v.y };
glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position)); }
return translation * orientation;
}
inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) { static inline ovrQuatf fromGlm(const glm::quat& q) {
ovrMatrix4f result; return { q.x, q.y, q.z, q.w };
glm::mat4 transposed(glm::transpose(m)); }
memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16);
return result;
}
inline ovrVector3f ovrFromGlm(const glm::vec3 & v) { static inline ovrPosef poseFromGlm(const glm::mat4& m) {
return{ v.x, v.y, v.z }; 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) { static controller::Pose toControllerPose(ovrHandType hand, const ovrPoseStatef& handPose);
return{ v.x, v.y }; 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) { } // namespace hifi
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);

View file

@ -57,7 +57,6 @@ if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_I
menuItemName: MENU_ITEM, menuItemName: MENU_ITEM,
isCheckable: true, isCheckable: true,
isChecked: previousSetting, isChecked: previousSetting,
grouping: "Advanced"
}); });
} }

View file

@ -2362,26 +2362,21 @@ var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace");
var propertiesTool = new PropertiesTool(); var propertiesTool = new PropertiesTool();
var particleExplorerTool = new ParticleExplorerTool(); var particleExplorerTool = new ParticleExplorerTool();
var selectedParticleEntity = 0;
var selectedParticleEntityID = null; var selectedParticleEntityID = null;
function selectParticleEntity(entityID) { function selectParticleEntity(entityID) {
var properties = Entities.getEntityProperties(entityID);
selectedParticleEntityID = entityID; selectedParticleEntityID = entityID;
var properties = Entities.getEntityProperties(entityID);
if (properties.emitOrientation) { if (properties.emitOrientation) {
properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation); properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation);
} }
var particleData = {
messageType: "particle_settings",
currentProperties: properties
};
particleExplorerTool.destroyWebView(); particleExplorerTool.destroyWebView();
particleExplorerTool.createWebView(); particleExplorerTool.createWebView();
selectedParticleEntity = entityID;
particleExplorerTool.setActiveParticleEntity(entityID); particleExplorerTool.setActiveParticleEntity(entityID);
particleExplorerTool.setActiveParticleProperties(properties);
particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData));
// Switch to particle explorer // Switch to particle explorer
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
@ -2404,13 +2399,13 @@ entityListTool.webView.webEventReceived.connect(function (data) {
var ids = data.entityIds; var ids = data.entityIds;
if (ids.length === 1) { if (ids.length === 1) {
if (Entities.getEntityProperties(ids[0], "type").type === "ParticleEffect") { 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 // This particle entity is already selected, so return
return; return;
} }
// Destroy the old particles web view first // Destroy the old particles web view first
} else { } else {
selectedParticleEntity = 0; selectedParticleEntityID = 0;
particleExplorerTool.destroyWebView(); particleExplorerTool.destroyWebView();
} }
} }

View file

@ -115,7 +115,8 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) {
}), }),
dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth }, dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth },
parentID: MyAvatar.SELF_ID, parentID: MyAvatar.SELF_ID,
visible: visible visible: visible,
isGroupCulled: true
}; };
// compute position, rotation & parentJointIndex of the tablet // compute position, rotation & parentJointIndex of the tablet

View file

@ -91,7 +91,7 @@ CameraManager = function() {
} }
var CAPTURED_KEYS = []; var CAPTURED_KEYS = [];
for (key in keyToActionMapping) { for (var key in keyToActionMapping) {
CAPTURED_KEYS.push(key); CAPTURED_KEYS.push(key);
} }
@ -99,9 +99,9 @@ CameraManager = function() {
var action = keyToActionMapping[event.text]; var action = keyToActionMapping[event.text];
if (action !== undefined) { if (action !== undefined) {
if (event.isShifted) { if (event.isShifted) {
if (action == "orbitForward") { if (action === "orbitForward") {
action = "orbitUp"; action = "orbitUp";
} else if (action == "orbitBackward") { } else if (action === "orbitBackward") {
action = "orbitDown"; action = "orbitDown";
} }
} }
@ -133,7 +133,7 @@ CameraManager = function() {
}; };
that.enable = function() { that.enable = function() {
if (Camera.mode == "independent" || that.enabled || HMD.active) { if (Camera.mode === "independent" || that.enabled || HMD.active) {
return; return;
} }
@ -235,7 +235,7 @@ CameraManager = function() {
} }
that.setFocalPoint = function(pos) { that.setFocalPoint = function(pos) {
that.targetFocalPoint = pos that.targetFocalPoint = pos;
that.updateCamera(); that.updateCamera();
} }
@ -276,7 +276,7 @@ CameraManager = function() {
} }
that.mouseMoveEvent = function(event) { that.mouseMoveEvent = function(event) {
if (that.enabled && that.mode != MODE_INACTIVE) { if (that.enabled && that.mode !== MODE_INACTIVE) {
var x = Reticle.getPosition().x; var x = Reticle.getPosition().x;
var y = Reticle.getPosition().y; var y = Reticle.getPosition().y;
if (!hasDragged) { if (!hasDragged) {
@ -284,11 +284,11 @@ CameraManager = function() {
that.lastMousePosition.y = y; that.lastMousePosition.y = y;
hasDragged = true; hasDragged = true;
} }
if (that.mode == MODE_ORBIT) { if (that.mode === MODE_ORBIT) {
var diffX = x - that.lastMousePosition.x; var diffX = x - that.lastMousePosition.x;
var diffY = y - that.lastMousePosition.y; var diffY = y - that.lastMousePosition.y;
that.targetYaw -= MOUSE_SENSITIVITY * (diffX / 5.0) that.targetYaw -= MOUSE_SENSITIVITY * (diffX / 5.0);
that.targetPitch += MOUSE_SENSITIVITY * (diffY / 10.0) that.targetPitch += MOUSE_SENSITIVITY * (diffY / 10.0);
while (that.targetYaw > 180.0) that.targetYaw -= 360; while (that.targetYaw > 180.0) that.targetYaw -= 360;
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; if (that.targetPitch < -90) that.targetPitch = -90;
that.updateCamera(); that.updateCamera();
} else if (that.mode == MODE_PAN) { } else if (that.mode === MODE_PAN) {
var diffX = x - that.lastMousePosition.x; var diffX = x - that.lastMousePosition.x;
var diffY = y - that.lastMousePosition.y; var diffY = y - that.lastMousePosition.y;
@ -316,19 +316,19 @@ CameraManager = function() {
var newY = y; var newY = y;
var updatePosition = false; var updatePosition = false;
if (x <= Window.x) { if (x <= 0) {
newX = Window.x + Window.innerWidth; newX = Window.innerWidth;
updatePosition = true; updatePosition = true;
} else if (x >= (Window.x + Window.innerWidth)) { } else if (x >= Window.innerWidth) {
newX = Window.x; newX = 0;
updatePosition = true; updatePosition = true;
} }
if (y <= Window.y) { if (y <= 0) {
newY = Window.y + Window.innerHeight; newY = Window.innerHeight;
updatePosition = true; updatePosition = true;
} else if (y >= (Window.y + Window.innerHeight)) { } else if (y >= Window.innerHeight) {
newY = Window.y; newY = 0;
updatePosition = true; updatePosition = true;
} }
@ -410,7 +410,7 @@ CameraManager = function() {
} }
that.updateCamera = function() { that.updateCamera = function() {
if (!that.enabled || Camera.mode != "independent") { if (!that.enabled || Camera.mode !== "independent") {
cameraTool.update(); cameraTool.update();
return; return;
} }
@ -464,7 +464,7 @@ CameraManager = function() {
// Ease the position and orbit of the camera // Ease the position and orbit of the camera
that.update = function(dt) { that.update = function(dt) {
if (Camera.mode != "independent") { if (Camera.mode !== "independent") {
that.updateCamera(); that.updateCamera();
return; return;
} }

View file

@ -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) { function addHandleTool(overlay, tool) {
handleTools[overlay] = tool; handleTools[overlay] = tool;
return tool; return tool;

View file

@ -16,37 +16,62 @@ var PARTICLE_EXPLORER_HTML_URL = Script.resolvePath('particleExplorer.html');
ParticleExplorerTool = function() { ParticleExplorerTool = function() {
var that = {}; var that = {};
that.activeParticleEntity = 0;
that.activeParticleProperties = {};
that.createWebView = function() { that.createWebView = function() {
that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system"); that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
that.webView.setVisible = function(value) {}; that.webView.setVisible = function(value) {};
that.webView.webEventReceived.connect(that.webEventReceived); that.webView.webEventReceived.connect(that.webEventReceived);
} };
that.destroyWebView = function() { that.destroyWebView = function() {
if (!that.webView) { if (!that.webView) {
return; return;
} }
that.activeParticleEntity = 0; that.activeParticleEntity = 0;
that.activeParticleProperties = {};
var messageData = { var messageData = {
messageType: "particle_close" messageType: "particle_close"
}; };
that.webView.emitScriptEvent(JSON.stringify(messageData)); that.webView.emitScriptEvent(JSON.stringify(messageData));
};
function sendActiveParticleProperties() {
that.webView.emitScriptEvent(JSON.stringify({
messageType: "particle_settings",
currentProperties: that.activeParticleProperties
}));
} }
that.webEventReceived = function(data) { that.webEventReceived = function(message) {
var data = JSON.parse(data); var data = JSON.parse(message);
if (data.messageType === "settings_update") { if (data.messageType === "settings_update") {
if (data.updatedSettings.emitOrientation) { if (data.updatedSettings.emitOrientation) {
data.updatedSettings.emitOrientation = Quat.fromVec3Degrees(data.updatedSettings.emitOrientation); data.updatedSettings.emitOrientation = Quat.fromVec3Degrees(data.updatedSettings.emitOrientation);
} }
Entities.editEntity(that.activeParticleEntity, data.updatedSettings); 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();
} }
} };
that.setActiveParticleEntity = function(id) { that.setActiveParticleEntity = function(id) {
that.activeParticleEntity = id; that.activeParticleEntity = id;
} };
that.setActiveParticleProperties = function(properties) {
that.activeParticleProperties = properties;
sendActiveParticleProperties();
};
return that; return that;
}; };

View file

@ -295,6 +295,7 @@ function printToPolaroid(image_url) {
"description": "Printed from Snaps", "description": "Printed from Snaps",
"modelURL": POLAROID_MODEL_URL, "modelURL": POLAROID_MODEL_URL,
"dimensions": { "x": 0.5667, "y": 0.0212, "z": 0.4176 },
"position": model_pos, "position": model_pos,
"rotation": model_rot, "rotation": model_rot,