mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge remote-tracking branch 'upstream/master' into textAA
This commit is contained in:
commit
a955381726
123 changed files with 3589 additions and 1330 deletions
27
BUILD_WIN.md
27
BUILD_WIN.md
|
@ -27,11 +27,20 @@ Go to `Control Panel > System > Advanced System Settings > Environment Variables
|
|||
* Set "Variable name": `QT_CMAKE_PREFIX_PATH`
|
||||
* Set "Variable value": `C:\Qt\5.9.1\msvc2017_64\lib\cmake`
|
||||
|
||||
### Step 5. Installing OpenSSL
|
||||
### Step 5. Installing [vcpkg](https://github.com/Microsoft/vcpkg)
|
||||
|
||||
Download and install the Win64 OpenSSL v1.0.2 Installer[https://slproweb.com/products/Win32OpenSSL.html].
|
||||
* Clone the VCPKG [repository](https://github.com/Microsoft/vcpkg)
|
||||
* Follow the instructions in the [readme](https://github.com/Microsoft/vcpkg/blob/master/README.md) to bootstrap vcpkg
|
||||
* Note, you may need to do these in a _Developer Command Prompt_
|
||||
* Set an environment variable VCPKG_ROOT to the location of the cloned repository
|
||||
* Close and re-open any command prompts after setting the environment variable so that they will pick up the change
|
||||
|
||||
### Step 6. Running CMake to Generate Build Files
|
||||
### Step 6. Installing OpenSSL via vcpkg
|
||||
|
||||
* In the vcpkg directory, install the 64 bit OpenSSL package with the command `vcpkg install openssl:x64-windows`
|
||||
* Once the build completes you should have a file `ssl.h` in `${VCPKG_ROOT}/installed/x64-windows/include/openssl`
|
||||
|
||||
### Step 7. Running CMake to Generate Build Files
|
||||
|
||||
Run Command Prompt from Start and run the following commands:
|
||||
```
|
||||
|
@ -43,7 +52,7 @@ cmake .. -G "Visual Studio 15 Win64"
|
|||
|
||||
Where `%HIFI_DIR%` is the directory for the highfidelity repository.
|
||||
|
||||
### Step 7. Making a Build
|
||||
### Step 8. Making a Build
|
||||
|
||||
Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio.
|
||||
|
||||
|
@ -51,7 +60,7 @@ Change the Solution Configuration (next to the green play button) from "Debug" t
|
|||
|
||||
Run `Build > Build Solution`.
|
||||
|
||||
### Step 8. Testing Interface
|
||||
### Step 9. Testing Interface
|
||||
|
||||
Create another environment variable (see Step #4)
|
||||
* Set "Variable name": `_NO_DEBUG_HEAP`
|
||||
|
@ -65,16 +74,20 @@ Note: You can also run Interface by launching it from command line or File Explo
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
For any problems after Step #6, first try this:
|
||||
For any problems after Step #7, first try this:
|
||||
* Delete your locally cloned copy of the highfidelity repository
|
||||
* Restart your computer
|
||||
* Redownload the [repository](https://github.com/highfidelity/hifi)
|
||||
* Restart directions from Step #6
|
||||
* Restart directions from Step #7
|
||||
|
||||
#### CMake gives you the same error message repeatedly after the build fails
|
||||
|
||||
Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory.
|
||||
|
||||
#### CMake can't find OpenSSL
|
||||
|
||||
Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory. Verify that your VCPKG_ROOT environment variable is set and pointing to the correct location. Verify that the file `${VCPKG_ROOT}/installed/x64-windows/include/openssl/ssl.h` exists.
|
||||
|
||||
#### Qt is throwing an error
|
||||
|
||||
Make sure you have the correct version (5.9.1) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly.
|
||||
|
|
309
CMakeLists.txt
309
CMakeLists.txt
|
@ -4,254 +4,81 @@ else()
|
|||
cmake_minimum_required(VERSION 3.2)
|
||||
endif()
|
||||
|
||||
if (USE_ANDROID_TOOLCHAIN)
|
||||
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/android/android.toolchain.cmake")
|
||||
set(ANDROID_NATIVE_API_LEVEL 19)
|
||||
set(ANDROID_TOOLCHAIN_NAME arm-linux-androideabi-clang3.5)
|
||||
set(ANDROID_STL c++_shared)
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
cmake_policy(SET CMP0020 NEW)
|
||||
endif (WIN32)
|
||||
|
||||
if (POLICY CMP0043)
|
||||
cmake_policy(SET CMP0043 OLD)
|
||||
endif ()
|
||||
|
||||
if (POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 OLD)
|
||||
endif ()
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMakeTargets")
|
||||
include("cmake/init.cmake")
|
||||
|
||||
project(hifi)
|
||||
add_definitions(-DGLM_FORCE_RADIANS)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||
|
||||
find_package( Threads )
|
||||
include("cmake/compiler.cmake")
|
||||
|
||||
if (WIN32)
|
||||
if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
message( FATAL_ERROR "Only 64 bit builds supported." )
|
||||
endif()
|
||||
|
||||
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
if (NOT WINDOW_SDK_PATH)
|
||||
set(DEBUG_DISCOVERED_SDK_PATH TRUE)
|
||||
endif()
|
||||
|
||||
# sets path for Microsoft SDKs
|
||||
# if you get build error about missing 'glu32' this path is likely wrong
|
||||
if (MSVC_VERSION GREATER_EQUAL 1910) # VS 2017
|
||||
set(WINDOW_SDK_PATH "C:/Program Files (x86)/Windows Kits/10/Lib/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/x64" CACHE PATH "Windows SDK PATH")
|
||||
elseif (MSVC_VERSION GREATER_EQUAL 1800) # VS 2013
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}" CACHE PATH "Windows SDK PATH")
|
||||
else()
|
||||
message( FATAL_ERROR "Visual Studio 2013 or higher required." )
|
||||
endif()
|
||||
|
||||
if (DEBUG_DISCOVERED_SDK_PATH)
|
||||
message(STATUS "The discovered Windows SDK path is ${WINDOW_SDK_PATH}")
|
||||
endif ()
|
||||
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOW_SDK_PATH})
|
||||
# /wd4351 disables warning C4351: new behavior: elements of array will be default initialized
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4351")
|
||||
# /LARGEADDRESSAWARE enables 32-bit apps to use more than 2GB of memory.
|
||||
# Caveats: http://stackoverflow.com/questions/2288728/drawbacks-of-using-largeaddressaware-for-32-bit-windows-executables
|
||||
# TODO: Remove when building 64-bit.
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE")
|
||||
# always produce symbols as PDB files
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG /OPT:REF /OPT:ICF")
|
||||
else ()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -Woverloaded-virtual -Wdouble-promotion")
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "5.1") # gcc 5.1 and on have suggest-override
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override")
|
||||
endif ()
|
||||
endif ()
|
||||
endif(WIN32)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.3")
|
||||
# GLM 0.9.8 on Ubuntu 14 (gcc 4.4) has issues with the simd declarations
|
||||
add_definitions(-DGLM_FORCE_PURE)
|
||||
endif()
|
||||
if (NOT DEFINED SERVER_ONLY)
|
||||
set(SERVER_ONLY 0)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID)
|
||||
if ((NOT MSVC12) AND (NOT MSVC14))
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
|
||||
|
||||
if (COMPILER_SUPPORTS_CXX11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
elseif(COMPILER_SUPPORTS_CXX0X)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
else()
|
||||
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
|
||||
endif()
|
||||
endif ()
|
||||
else ()
|
||||
# assume that the toolchain selected for android has C++11 support
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
|
||||
endif ()
|
||||
|
||||
if (NOT ANDROID_LIB_DIR)
|
||||
set(ANDROID_LIB_DIR $ENV{ANDROID_LIB_DIR})
|
||||
endif ()
|
||||
|
||||
if (ANDROID)
|
||||
if (NOT ANDROID_QT_CMAKE_PREFIX_PATH)
|
||||
set(QT_CMAKE_PREFIX_PATH ${ANDROID_LIB_DIR}/Qt/5.5/android_armv7/lib/cmake)
|
||||
else ()
|
||||
set(QT_CMAKE_PREFIX_PATH ${ANDROID_QT_CMAKE_PREFIX_PATH})
|
||||
endif ()
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
|
||||
|
||||
if (ANDROID_LIB_DIR)
|
||||
list(APPEND CMAKE_FIND_ROOT_PATH ${ANDROID_LIB_DIR})
|
||||
endif ()
|
||||
else ()
|
||||
if (NOT QT_CMAKE_PREFIX_PATH)
|
||||
set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH})
|
||||
endif ()
|
||||
if (NOT QT_CMAKE_PREFIX_PATH)
|
||||
get_filename_component(QT_CMAKE_PREFIX_PATH "${Qt5_DIR}/.." REALPATH)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set(QT_DIR $ENV{QT_DIR})
|
||||
|
||||
if (WIN32)
|
||||
if (NOT EXISTS ${QT_CMAKE_PREFIX_PATH})
|
||||
message(FATAL_ERROR "Could not determine QT_CMAKE_PREFIX_PATH.")
|
||||
endif ()
|
||||
if (ANDROID OR UWP)
|
||||
set(MOBILE 1)
|
||||
else()
|
||||
set(MOBILE 0)
|
||||
endif()
|
||||
|
||||
# figure out where the qt dir is
|
||||
get_filename_component(QT_DIR "${QT_CMAKE_PREFIX_PATH}/../../" ABSOLUTE)
|
||||
if (ANDROID OR UWP)
|
||||
option(BUILD_SERVER "Build server components" OFF)
|
||||
option(BUILD_TOOLS "Build tools" OFF)
|
||||
else()
|
||||
option(BUILD_SERVER "Build server components" ON)
|
||||
option(BUILD_TOOLS "Build tools" ON)
|
||||
endif()
|
||||
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_CMAKE_PREFIX_PATH})
|
||||
if (SERVER_ONLY)
|
||||
option(BUILD_CLIENT "Build client components" OFF)
|
||||
option(BUILD_TESTS "Build tests" OFF)
|
||||
else()
|
||||
option(BUILD_CLIENT "Build client components" ON)
|
||||
option(BUILD_TESTS "Build tests" ON)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
option(BUILD_INSTALLER "Build installer" ON)
|
||||
|
||||
exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE OSX_VERSION)
|
||||
string(REGEX MATCH "^[0-9]+\\.[0-9]+" OSX_VERSION ${OSX_VERSION})
|
||||
message(STATUS "Detected OS X version = ${OSX_VERSION}")
|
||||
MESSAGE(STATUS "Build server: " ${BUILD_SERVER})
|
||||
MESSAGE(STATUS "Build client: " ${BUILD_CLIENT})
|
||||
MESSAGE(STATUS "Build tests: " ${BUILD_TESTS})
|
||||
MESSAGE(STATUS "Build tools: " ${BUILD_TOOLS})
|
||||
MESSAGE(STATUS "Build installer: " ${BUILD_INSTALLER})
|
||||
|
||||
set(OSX_SDK "${OSX_VERSION}" CACHE String "OS X SDK version to look for inside Xcode bundle or at OSX_SDK_PATH")
|
||||
if (UNIX AND DEFINED ENV{HIFI_MEMORY_DEBUGGING})
|
||||
MESSAGE(STATUS "Memory debugging is enabled")
|
||||
endif()
|
||||
|
||||
# set our OS X deployment target
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8)
|
||||
|
||||
# find the SDK path for the desired SDK
|
||||
find_path(
|
||||
_OSX_DESIRED_SDK_PATH
|
||||
NAME MacOSX${OSX_SDK}.sdk
|
||||
HINTS ${OSX_SDK_PATH}
|
||||
PATHS /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
|
||||
/Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
|
||||
)
|
||||
|
||||
if (NOT _OSX_DESIRED_SDK_PATH)
|
||||
message(STATUS "Could not find OS X ${OSX_SDK} SDK. Will fall back to default. If you want a specific SDK, please pass OSX_SDK and optionally OSX_SDK_PATH to CMake.")
|
||||
else ()
|
||||
message(STATUS "Found OS X ${OSX_SDK} SDK at ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk")
|
||||
|
||||
# set that as the SDK to use
|
||||
set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk)
|
||||
endif ()
|
||||
|
||||
endif ()
|
||||
|
||||
# Hide automoc folders (for IDEs)
|
||||
set(AUTOGEN_TARGETS_FOLDER "hidden/generated")
|
||||
|
||||
# Find includes in corresponding build directories
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
# Instruct CMake to run moc automatically when needed.
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
# Instruct CMake to run rcc automatically when needed
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(HIFI_LIBRARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries")
|
||||
|
||||
# setup for find modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/")
|
||||
|
||||
if (CMAKE_BUILD_TYPE)
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} UPPER_CMAKE_BUILD_TYPE)
|
||||
else ()
|
||||
set(UPPER_CMAKE_BUILD_TYPE DEBUG)
|
||||
endif ()
|
||||
|
||||
set(HF_CMAKE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
set(MACRO_DIR "${HF_CMAKE_DIR}/macros")
|
||||
set(EXTERNAL_PROJECT_DIR "${HF_CMAKE_DIR}/externals")
|
||||
|
||||
file(GLOB HIFI_CUSTOM_MACROS "cmake/macros/*.cmake")
|
||||
foreach(CUSTOM_MACRO ${HIFI_CUSTOM_MACROS})
|
||||
include(${CUSTOM_MACRO})
|
||||
endforeach()
|
||||
#
|
||||
# Helper projects
|
||||
#
|
||||
file(GLOB_RECURSE CMAKE_SRC cmake/*.cmake cmake/CMakeLists.txt)
|
||||
add_custom_target(cmake SOURCES ${CMAKE_SRC})
|
||||
GroupSources("cmake")
|
||||
|
||||
file(GLOB_RECURSE JS_SRC scripts/*.js unpublishedScripts/*.js)
|
||||
add_custom_target(js SOURCES ${JS_SRC})
|
||||
GroupSources("scripts")
|
||||
GroupSources("unpublishedScripts")
|
||||
|
||||
if (UNIX)
|
||||
install(
|
||||
DIRECTORY "${CMAKE_SOURCE_DIR}/scripts"
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/interface
|
||||
COMPONENT ${CLIENT_COMPONENT}
|
||||
)
|
||||
endif()
|
||||
# Locate the required Qt build on the filesystem
|
||||
setup_qt()
|
||||
list(APPEND CMAKE_PREFIX_PATH "${QT_CMAKE_PREFIX_PATH}")
|
||||
|
||||
if (ANDROID)
|
||||
file(GLOB ANDROID_CUSTOM_MACROS "cmake/android/*.cmake")
|
||||
foreach(CUSTOM_MACRO ${ANDROID_CUSTOM_MACROS})
|
||||
include(${CUSTOM_MACRO})
|
||||
endforeach()
|
||||
endif ()
|
||||
find_package( Threads )
|
||||
|
||||
add_definitions(-DGLM_FORCE_RADIANS)
|
||||
set(HIFI_LIBRARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries")
|
||||
|
||||
set(EXTERNAL_PROJECT_PREFIX "project")
|
||||
set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX})
|
||||
setup_externals_binary_dir()
|
||||
|
||||
option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
|
||||
option(GET_QUAZIP "Get QuaZip library automatically as external project" 1)
|
||||
|
||||
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs("${QT_DIR}/bin")
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED SERVER_ONLY)
|
||||
set(SERVER_ONLY 0)
|
||||
endif()
|
||||
|
||||
set_packaging_parameters()
|
||||
|
||||
option(BUILD_TESTS "Build tests" ON)
|
||||
MESSAGE(STATUS "Build tests: " ${BUILD_TESTS})
|
||||
|
||||
# add subdirectories for all targets
|
||||
if (NOT ANDROID)
|
||||
if (BUILD_SERVER)
|
||||
add_subdirectory(assignment-client)
|
||||
set_target_properties(assignment-client PROPERTIES FOLDER "Apps")
|
||||
add_subdirectory(domain-server)
|
||||
|
@ -259,30 +86,36 @@ if (NOT ANDROID)
|
|||
add_subdirectory(ice-server)
|
||||
set_target_properties(ice-server PROPERTIES FOLDER "Apps")
|
||||
add_subdirectory(server-console)
|
||||
if (NOT SERVER_ONLY)
|
||||
endif()
|
||||
|
||||
if (BUILD_CLIENT)
|
||||
add_subdirectory(interface)
|
||||
set_target_properties(interface PROPERTIES FOLDER "Apps")
|
||||
if (BUILD_TESTS)
|
||||
add_subdirectory(tests)
|
||||
if (ANDROID)
|
||||
add_subdirectory(gvr-interface)
|
||||
set_target_properties(gvr-interface PROPERTIES FOLDER "Apps")
|
||||
endif()
|
||||
endif()
|
||||
add_subdirectory(plugins)
|
||||
endif()
|
||||
|
||||
if (BUILD_CLIENT OR BUILD_SERVER)
|
||||
add_subdirectory(plugins)
|
||||
endif()
|
||||
|
||||
if (BUILD_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
endif()
|
||||
|
||||
if (ANDROID OR DESKTOP_GVR)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(gvr-interface)
|
||||
add_subdirectory(plugins)
|
||||
endif ()
|
||||
if (BUILD_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if (DEFINED ENV{HIFI_MEMORY_DEBUGGING})
|
||||
SET( HIFI_MEMORY_DEBUGGING true )
|
||||
endif ()
|
||||
if (HIFI_MEMORY_DEBUGGING)
|
||||
if (UNIX)
|
||||
MESSAGE("-- Memory debugging is enabled")
|
||||
endif (UNIX)
|
||||
endif ()
|
||||
|
||||
generate_installers()
|
||||
if (BUILD_INSTALLER)
|
||||
if (UNIX)
|
||||
install(
|
||||
DIRECTORY "${CMAKE_SOURCE_DIR}/scripts"
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/interface
|
||||
COMPONENT ${CLIENT_COMPONENT}
|
||||
)
|
||||
endif()
|
||||
generate_installers()
|
||||
endif()
|
||||
|
|
BIN
Test Plan 2.docx
BIN
Test Plan 2.docx
Binary file not shown.
|
@ -558,7 +558,7 @@ float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const Positio
|
|||
|
||||
// produce an oriented angle about the y-axis
|
||||
glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
|
||||
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
|
||||
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
|
||||
return (direction.x < 0.0f) ? -angle : angle;
|
||||
|
||||
} else {
|
||||
|
|
|
@ -35,7 +35,7 @@ void ScriptableAvatar::startAnimation(const QString& url, float fps, float prior
|
|||
return;
|
||||
}
|
||||
_animation = DependencyManager::get<AnimationCache>()->getAnimation(url);
|
||||
_animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame);
|
||||
_animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame, false);
|
||||
_maskedJoints = maskedJoints;
|
||||
}
|
||||
|
||||
|
|
|
@ -458,84 +458,80 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
|||
return _truePacketsSent;
|
||||
}
|
||||
|
||||
bool OctreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params) {
|
||||
bool somethingToSend = false;
|
||||
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(params.nodeData);
|
||||
if (!nodeData->elementBag.isEmpty()) {
|
||||
quint64 encodeStart = usecTimestampNow();
|
||||
quint64 lockWaitStart = encodeStart;
|
||||
|
||||
_myServer->getOctree()->withReadLock([&]{
|
||||
OctreeServer::trackTreeWaitTime((float)(usecTimestampNow() - lockWaitStart));
|
||||
|
||||
OctreeElementPointer subTree = nodeData->elementBag.extract();
|
||||
if (subTree) {
|
||||
// NOTE: this is where the tree "contents" are actually packed
|
||||
nodeData->stats.encodeStarted();
|
||||
_myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->elementBag, params);
|
||||
nodeData->stats.encodeStopped();
|
||||
|
||||
somethingToSend = true;
|
||||
}
|
||||
});
|
||||
|
||||
OctreeServer::trackEncodeTime((float)(usecTimestampNow() - encodeStart));
|
||||
} else {
|
||||
OctreeServer::trackTreeWaitTime(OctreeServer::SKIP_TIME);
|
||||
OctreeServer::trackEncodeTime(OctreeServer::SKIP_TIME);
|
||||
}
|
||||
return somethingToSend;
|
||||
}
|
||||
|
||||
void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) {
|
||||
// calculate max number of packets that can be sent during this interval
|
||||
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
|
||||
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
|
||||
|
||||
int extraPackingAttempts = 0;
|
||||
bool completedScene = false;
|
||||
|
||||
// init params once outside the while loop
|
||||
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
|
||||
int boundaryLevelAdjust = boundaryLevelAdjustClient +
|
||||
(viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
float octreeSizeScale = nodeData->getOctreeSizeScale();
|
||||
EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
|
||||
viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale,
|
||||
isFullScene, _myServer->getJurisdiction(), nodeData);
|
||||
// Our trackSend() function is implemented by the server subclass, and will be called back
|
||||
// during the encodeTreeBitstream() as new entities/data elements are sent
|
||||
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
|
||||
_myServer->trackSend(dataID, dataEdited, _nodeUuid);
|
||||
};
|
||||
nodeData->copyCurrentViewFrustum(params.viewFrustum);
|
||||
if (viewFrustumChanged) {
|
||||
nodeData->copyLastKnownViewFrustum(params.lastViewFrustum);
|
||||
}
|
||||
|
||||
bool somethingToSend = true; // assume we have something
|
||||
bool bagHadSomething = !nodeData->elementBag.isEmpty();
|
||||
while (somethingToSend && _packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) {
|
||||
float lockWaitElapsedUsec = OctreeServer::SKIP_TIME;
|
||||
float encodeElapsedUsec = OctreeServer::SKIP_TIME;
|
||||
float compressAndWriteElapsedUsec = OctreeServer::SKIP_TIME;
|
||||
float packetSendingElapsedUsec = OctreeServer::SKIP_TIME;
|
||||
|
||||
quint64 startInside = usecTimestampNow();
|
||||
|
||||
bool lastNodeDidntFit = false; // assume each node fits
|
||||
if (!nodeData->elementBag.isEmpty()) {
|
||||
params.stopReason = EncodeBitstreamParams::UNKNOWN; // reset params.stopReason before traversal
|
||||
|
||||
quint64 lockWaitStart = usecTimestampNow();
|
||||
_myServer->getOctree()->withReadLock([&]{
|
||||
quint64 lockWaitEnd = usecTimestampNow();
|
||||
lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart);
|
||||
quint64 encodeStart = usecTimestampNow();
|
||||
somethingToSend = traverseTreeAndBuildNextPacketPayload(params);
|
||||
|
||||
OctreeElementPointer subTree = nodeData->elementBag.extract();
|
||||
if (!subTree) {
|
||||
return;
|
||||
}
|
||||
|
||||
float octreeSizeScale = nodeData->getOctreeSizeScale();
|
||||
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
|
||||
|
||||
int boundaryLevelAdjust = boundaryLevelAdjustClient +
|
||||
(viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
|
||||
EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
|
||||
viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale,
|
||||
isFullScene, _myServer->getJurisdiction(), nodeData);
|
||||
nodeData->copyCurrentViewFrustum(params.viewFrustum);
|
||||
if (viewFrustumChanged) {
|
||||
nodeData->copyLastKnownViewFrustum(params.lastViewFrustum);
|
||||
}
|
||||
|
||||
// Our trackSend() function is implemented by the server subclass, and will be called back
|
||||
// during the encodeTreeBitstream() as new entities/data elements are sent
|
||||
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
|
||||
_myServer->trackSend(dataID, dataEdited, _nodeUuid);
|
||||
};
|
||||
|
||||
// TODO: should this include the lock time or not? This stat is sent down to the client,
|
||||
// it seems like it may be a good idea to include the lock time as part of the encode time
|
||||
// are reported to client. Since you can encode without the lock
|
||||
nodeData->stats.encodeStarted();
|
||||
|
||||
// NOTE: this is where the tree "contents" are actaully packed
|
||||
_myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->elementBag, params);
|
||||
|
||||
quint64 encodeEnd = usecTimestampNow();
|
||||
encodeElapsedUsec = (float)(encodeEnd - encodeStart);
|
||||
|
||||
// If after calling encodeTreeBitstream() there are no nodes left to send, then we know we've
|
||||
// sent the entire scene. We want to know this below so we'll actually write this content into
|
||||
// the packet and send it
|
||||
completedScene = nodeData->elementBag.isEmpty();
|
||||
|
||||
if (params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
|
||||
lastNodeDidntFit = true;
|
||||
extraPackingAttempts++;
|
||||
}
|
||||
|
||||
nodeData->stats.encodeStopped();
|
||||
});
|
||||
} else {
|
||||
somethingToSend = false; // this will cause us to drop out of the loop...
|
||||
if (params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
|
||||
lastNodeDidntFit = true;
|
||||
extraPackingAttempts++;
|
||||
}
|
||||
|
||||
// If the bag had contents but is now empty then we know we've sent the entire scene.
|
||||
bool completedScene = bagHadSomething && nodeData->elementBag.isEmpty();
|
||||
if (completedScene || lastNodeDidntFit) {
|
||||
// we probably want to flush what has accumulated in nodeData but:
|
||||
// do we have more data to send? and is there room?
|
||||
|
@ -562,8 +558,7 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
|
|||
if (sendNow) {
|
||||
quint64 packetSendingStart = usecTimestampNow();
|
||||
_packetsSentThisInterval += handlePacketSend(node, nodeData);
|
||||
quint64 packetSendingEnd = usecTimestampNow();
|
||||
packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart);
|
||||
packetSendingElapsedUsec = (float)(usecTimestampNow() - packetSendingStart);
|
||||
|
||||
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
|
||||
extraPackingAttempts = 0;
|
||||
|
@ -576,14 +571,9 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
|
|||
}
|
||||
_packetData.changeSettings(true, targetSize); // will do reset - NOTE: Always compressed
|
||||
}
|
||||
OctreeServer::trackTreeWaitTime(lockWaitElapsedUsec);
|
||||
OctreeServer::trackEncodeTime(encodeElapsedUsec);
|
||||
OctreeServer::trackCompressAndWriteTime(compressAndWriteElapsedUsec);
|
||||
OctreeServer::trackPacketSendingTime(packetSendingElapsedUsec);
|
||||
|
||||
quint64 endInside = usecTimestampNow();
|
||||
quint64 elapsedInsideUsecs = endInside - startInside;
|
||||
OctreeServer::trackInsideTime((float)elapsedInsideUsecs);
|
||||
OctreeServer::trackInsideTime((float)(usecTimestampNow() - startInside));
|
||||
}
|
||||
|
||||
if (somethingToSend && _myServer->wantsVerboseDebug()) {
|
||||
|
|
|
@ -53,7 +53,9 @@ protected:
|
|||
|
||||
/// Called before a packetDistributor pass to allow for pre-distribution processing
|
||||
virtual void preDistributionProcessing() {};
|
||||
virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene);
|
||||
virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool viewFrustumChanged, bool isFullScene);
|
||||
virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params);
|
||||
|
||||
OctreeServer* _myServer { nullptr };
|
||||
QWeakPointer<Node> _node;
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include <QtCore/QDir>
|
||||
|
||||
int OctreeServer::_clientCount = 0;
|
||||
const int MOVING_AVERAGE_SAMPLE_COUNTS = 1000000;
|
||||
const int MOVING_AVERAGE_SAMPLE_COUNTS = 1000;
|
||||
|
||||
float OctreeServer::SKIP_TIME = -1.0f; // use this for trackXXXTime() calls for non-times
|
||||
|
||||
|
@ -136,18 +136,19 @@ void OctreeServer::trackEncodeTime(float time) {
|
|||
|
||||
if (time == SKIP_TIME) {
|
||||
_noEncode++;
|
||||
time = 0.0f;
|
||||
} else if (time <= MAX_SHORT_TIME) {
|
||||
_shortEncode++;
|
||||
_averageShortEncodeTime.updateAverage(time);
|
||||
} else if (time <= MAX_LONG_TIME) {
|
||||
_longEncode++;
|
||||
_averageLongEncodeTime.updateAverage(time);
|
||||
} else {
|
||||
_extraLongEncode++;
|
||||
_averageExtraLongEncodeTime.updateAverage(time);
|
||||
if (time <= MAX_SHORT_TIME) {
|
||||
_shortEncode++;
|
||||
_averageShortEncodeTime.updateAverage(time);
|
||||
} else if (time <= MAX_LONG_TIME) {
|
||||
_longEncode++;
|
||||
_averageLongEncodeTime.updateAverage(time);
|
||||
} else {
|
||||
_extraLongEncode++;
|
||||
_averageExtraLongEncodeTime.updateAverage(time);
|
||||
}
|
||||
_averageEncodeTime.updateAverage(time);
|
||||
}
|
||||
_averageEncodeTime.updateAverage(time);
|
||||
}
|
||||
|
||||
void OctreeServer::trackTreeWaitTime(float time) {
|
||||
|
@ -155,18 +156,19 @@ void OctreeServer::trackTreeWaitTime(float time) {
|
|||
const float MAX_LONG_TIME = 100.0f;
|
||||
if (time == SKIP_TIME) {
|
||||
_noTreeWait++;
|
||||
time = 0.0f;
|
||||
} else if (time <= MAX_SHORT_TIME) {
|
||||
_shortTreeWait++;
|
||||
_averageTreeShortWaitTime.updateAverage(time);
|
||||
} else if (time <= MAX_LONG_TIME) {
|
||||
_longTreeWait++;
|
||||
_averageTreeLongWaitTime.updateAverage(time);
|
||||
} else {
|
||||
_extraLongTreeWait++;
|
||||
_averageTreeExtraLongWaitTime.updateAverage(time);
|
||||
if (time <= MAX_SHORT_TIME) {
|
||||
_shortTreeWait++;
|
||||
_averageTreeShortWaitTime.updateAverage(time);
|
||||
} else if (time <= MAX_LONG_TIME) {
|
||||
_longTreeWait++;
|
||||
_averageTreeLongWaitTime.updateAverage(time);
|
||||
} else {
|
||||
_extraLongTreeWait++;
|
||||
_averageTreeExtraLongWaitTime.updateAverage(time);
|
||||
}
|
||||
_averageTreeWaitTime.updateAverage(time);
|
||||
}
|
||||
_averageTreeWaitTime.updateAverage(time);
|
||||
}
|
||||
|
||||
void OctreeServer::trackCompressAndWriteTime(float time) {
|
||||
|
@ -174,26 +176,27 @@ void OctreeServer::trackCompressAndWriteTime(float time) {
|
|||
const float MAX_LONG_TIME = 100.0f;
|
||||
if (time == SKIP_TIME) {
|
||||
_noCompress++;
|
||||
time = 0.0f;
|
||||
} else if (time <= MAX_SHORT_TIME) {
|
||||
_shortCompress++;
|
||||
_averageShortCompressTime.updateAverage(time);
|
||||
} else if (time <= MAX_LONG_TIME) {
|
||||
_longCompress++;
|
||||
_averageLongCompressTime.updateAverage(time);
|
||||
} else {
|
||||
_extraLongCompress++;
|
||||
_averageExtraLongCompressTime.updateAverage(time);
|
||||
if (time <= MAX_SHORT_TIME) {
|
||||
_shortCompress++;
|
||||
_averageShortCompressTime.updateAverage(time);
|
||||
} else if (time <= MAX_LONG_TIME) {
|
||||
_longCompress++;
|
||||
_averageLongCompressTime.updateAverage(time);
|
||||
} else {
|
||||
_extraLongCompress++;
|
||||
_averageExtraLongCompressTime.updateAverage(time);
|
||||
}
|
||||
_averageCompressAndWriteTime.updateAverage(time);
|
||||
}
|
||||
_averageCompressAndWriteTime.updateAverage(time);
|
||||
}
|
||||
|
||||
void OctreeServer::trackPacketSendingTime(float time) {
|
||||
if (time == SKIP_TIME) {
|
||||
_noSend++;
|
||||
time = 0.0f;
|
||||
} else {
|
||||
_averagePacketSendingTime.updateAverage(time);
|
||||
}
|
||||
_averagePacketSendingTime.updateAverage(time);
|
||||
}
|
||||
|
||||
|
||||
|
@ -202,18 +205,19 @@ void OctreeServer::trackProcessWaitTime(float time) {
|
|||
const float MAX_LONG_TIME = 100.0f;
|
||||
if (time == SKIP_TIME) {
|
||||
_noProcessWait++;
|
||||
time = 0.0f;
|
||||
} else if (time <= MAX_SHORT_TIME) {
|
||||
_shortProcessWait++;
|
||||
_averageProcessShortWaitTime.updateAverage(time);
|
||||
} else if (time <= MAX_LONG_TIME) {
|
||||
_longProcessWait++;
|
||||
_averageProcessLongWaitTime.updateAverage(time);
|
||||
} else {
|
||||
_extraLongProcessWait++;
|
||||
_averageProcessExtraLongWaitTime.updateAverage(time);
|
||||
if (time <= MAX_SHORT_TIME) {
|
||||
_shortProcessWait++;
|
||||
_averageProcessShortWaitTime.updateAverage(time);
|
||||
} else if (time <= MAX_LONG_TIME) {
|
||||
_longProcessWait++;
|
||||
_averageProcessLongWaitTime.updateAverage(time);
|
||||
} else {
|
||||
_extraLongProcessWait++;
|
||||
_averageProcessExtraLongWaitTime.updateAverage(time);
|
||||
}
|
||||
_averageProcessWaitTime.updateAverage(time);
|
||||
}
|
||||
_averageProcessWaitTime.updateAverage(time);
|
||||
}
|
||||
|
||||
OctreeServer::OctreeServer(ReceivedMessage& message) :
|
||||
|
|
106
cmake/compiler.cmake
Normal file
106
cmake/compiler.cmake
Normal file
|
@ -0,0 +1,106 @@
|
|||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||
|
||||
if (WIN32)
|
||||
if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
message( FATAL_ERROR "Only 64 bit builds supported." )
|
||||
endif()
|
||||
|
||||
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
if (NOT WINDOW_SDK_PATH)
|
||||
set(DEBUG_DISCOVERED_SDK_PATH TRUE)
|
||||
endif()
|
||||
|
||||
# sets path for Microsoft SDKs
|
||||
# if you get build error about missing 'glu32' this path is likely wrong
|
||||
if (MSVC_VERSION GREATER_EQUAL 1910) # VS 2017
|
||||
set(WINDOW_SDK_PATH "C:/Program Files (x86)/Windows Kits/10/Lib/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/x64" CACHE PATH "Windows SDK PATH")
|
||||
elseif (MSVC_VERSION GREATER_EQUAL 1800) # VS 2013
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}" CACHE PATH "Windows SDK PATH")
|
||||
else()
|
||||
message( FATAL_ERROR "Visual Studio 2013 or higher required." )
|
||||
endif()
|
||||
|
||||
if (DEBUG_DISCOVERED_SDK_PATH)
|
||||
message(STATUS "The discovered Windows SDK path is ${WINDOW_SDK_PATH}")
|
||||
endif ()
|
||||
|
||||
list(APPEND CMAKE_PREFIX_PATH "${WINDOW_SDK_PATH}")
|
||||
|
||||
# /wd4351 disables warning C4351: new behavior: elements of array will be default initialized
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4351")
|
||||
# /LARGEADDRESSAWARE enables 32-bit apps to use more than 2GB of memory.
|
||||
# Caveats: http://stackoverflow.com/questions/2288728/drawbacks-of-using-largeaddressaware-for-32-bit-windows-executables
|
||||
# TODO: Remove when building 64-bit.
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE")
|
||||
# always produce symbols as PDB files
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG /OPT:REF /OPT:ICF")
|
||||
else ()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -Woverloaded-virtual -Wdouble-promotion")
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "5.1") # gcc 5.1 and on have suggest-override
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override")
|
||||
endif ()
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.3")
|
||||
# GLM 0.9.8 on Ubuntu 14 (gcc 4.4) has issues with the simd declarations
|
||||
add_definitions(-DGLM_FORCE_PURE)
|
||||
endif()
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
# assume that the toolchain selected for android has C++11 support
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
elseif ((NOT MSVC12) AND (NOT MSVC14))
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
|
||||
if (COMPILER_SUPPORTS_CXX11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
elseif(COMPILER_SUPPORTS_CXX0X)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
else()
|
||||
message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
|
||||
endif ()
|
||||
|
||||
if (NOT ANDROID_LIB_DIR)
|
||||
set(ANDROID_LIB_DIR $ENV{ANDROID_LIB_DIR})
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE OSX_VERSION)
|
||||
string(REGEX MATCH "^[0-9]+\\.[0-9]+" OSX_VERSION ${OSX_VERSION})
|
||||
message(STATUS "Detected OS X version = ${OSX_VERSION}")
|
||||
|
||||
set(OSX_SDK "${OSX_VERSION}" CACHE String "OS X SDK version to look for inside Xcode bundle or at OSX_SDK_PATH")
|
||||
|
||||
# set our OS X deployment target
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8)
|
||||
|
||||
# find the SDK path for the desired SDK
|
||||
find_path(
|
||||
_OSX_DESIRED_SDK_PATH
|
||||
NAME MacOSX${OSX_SDK}.sdk
|
||||
HINTS ${OSX_SDK_PATH}
|
||||
PATHS /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
|
||||
/Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
|
||||
)
|
||||
|
||||
if (NOT _OSX_DESIRED_SDK_PATH)
|
||||
message(STATUS "Could not find OS X ${OSX_SDK} SDK. Will fall back to default. If you want a specific SDK, please pass OSX_SDK and optionally OSX_SDK_PATH to CMake.")
|
||||
else ()
|
||||
message(STATUS "Found OS X ${OSX_SDK} SDK at ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk")
|
||||
|
||||
# set that as the SDK to use
|
||||
set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk)
|
||||
endif ()
|
||||
endif ()
|
1
cmake/externals/LibOVR/CMakeLists.txt
vendored
1
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -33,7 +33,6 @@ if (WIN32)
|
|||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(LIBOVR)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE TYPE INTERNAL)
|
||||
message("Libs ${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES}")
|
||||
|
||||
elseif(APPLE)
|
||||
|
||||
|
|
14
cmake/externals/quazip/CMakeLists.txt
vendored
14
cmake/externals/quazip/CMakeLists.txt
vendored
|
@ -4,14 +4,6 @@ cmake_policy(SET CMP0046 OLD)
|
|||
|
||||
include(ExternalProject)
|
||||
|
||||
if (WIN32)
|
||||
# windows shell does not like backslashes expanded on the command line,
|
||||
# so convert all backslashes in the QT path to forward slashes
|
||||
string(REPLACE \\ / QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH})
|
||||
elseif ($ENV{QT_CMAKE_PREFIX_PATH})
|
||||
set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH})
|
||||
endif ()
|
||||
|
||||
set(QUAZIP_CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=<INSTALL_DIR>/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON)
|
||||
|
||||
if (APPLE)
|
||||
|
@ -34,9 +26,9 @@ add_dependencies(quazip zlib)
|
|||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES
|
||||
FOLDER "hidden/externals"
|
||||
INSTALL_NAME_DIR ${INSTALL_DIR}/lib
|
||||
BUILD_WITH_INSTALL_RPATH True)
|
||||
FOLDER "hidden/externals"
|
||||
INSTALL_NAME_DIR ${INSTALL_DIR}/lib
|
||||
BUILD_WITH_INSTALL_RPATH True)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include CACHE PATH "List of QuaZip include directories")
|
||||
|
|
43
cmake/init.cmake
Normal file
43
cmake/init.cmake
Normal file
|
@ -0,0 +1,43 @@
|
|||
if (WIN32)
|
||||
cmake_policy(SET CMP0020 NEW)
|
||||
endif (WIN32)
|
||||
|
||||
if (POLICY CMP0043)
|
||||
cmake_policy(SET CMP0043 OLD)
|
||||
endif ()
|
||||
|
||||
if (POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 OLD)
|
||||
endif ()
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMakeTargets")
|
||||
# Hide automoc folders (for IDEs)
|
||||
set(AUTOGEN_TARGETS_FOLDER "hidden/generated")
|
||||
# Find includes in corresponding build directories
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
if (CMAKE_BUILD_TYPE)
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} UPPER_CMAKE_BUILD_TYPE)
|
||||
else ()
|
||||
set(UPPER_CMAKE_BUILD_TYPE DEBUG)
|
||||
endif ()
|
||||
|
||||
# CMAKE_CURRENT_SOURCE_DIR is the parent folder here
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/")
|
||||
|
||||
set(HF_CMAKE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
set(MACRO_DIR "${HF_CMAKE_DIR}/macros")
|
||||
set(EXTERNAL_PROJECT_DIR "${HF_CMAKE_DIR}/externals")
|
||||
|
||||
file(GLOB HIFI_CUSTOM_MACROS "cmake/macros/*.cmake")
|
||||
foreach(CUSTOM_MACRO ${HIFI_CUSTOM_MACROS})
|
||||
include(${CUSTOM_MACRO})
|
||||
endforeach()
|
||||
|
||||
if (ANDROID)
|
||||
file(GLOB ANDROID_CUSTOM_MACROS "cmake/android/*.cmake")
|
||||
foreach(CUSTOM_MACRO ${ANDROID_CUSTOM_MACROS})
|
||||
include(${CUSTOM_MACRO})
|
||||
endforeach()
|
||||
endif ()
|
|
@ -133,7 +133,7 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
else()
|
||||
message( FATAL_ERROR "Visual Studio 2013 or higher required." )
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT SIGNTOOL_EXECUTABLE)
|
||||
message(FATAL_ERROR "Code signing of executables was requested but signtool.exe could not be found.")
|
||||
endif ()
|
||||
|
@ -147,6 +147,7 @@ macro(SET_PACKAGING_PARAMETERS)
|
|||
set(CONSOLE_STARTUP_REG_KEY "ConsoleStartupShortcut")
|
||||
set(CLIENT_LAUNCH_NOW_REG_KEY "ClientLaunchAfterInstall")
|
||||
set(SERVER_LAUNCH_NOW_REG_KEY "ServerLaunchAfterInstall")
|
||||
set(CUSTOM_INSTALL_REG_KEY "CustomInstall")
|
||||
endif ()
|
||||
|
||||
# setup component categories for installer
|
||||
|
|
|
@ -9,11 +9,13 @@ macro(SETUP_HIFI_CLIENT_SERVER_PLUGIN)
|
|||
set(${TARGET_NAME}_SHARED 1)
|
||||
setup_hifi_library(${ARGV})
|
||||
|
||||
if (NOT DEFINED SERVER_ONLY)
|
||||
if (BUILD_CLIENT)
|
||||
add_dependencies(interface ${TARGET_NAME})
|
||||
endif()
|
||||
|
||||
add_dependencies(assignment-client ${TARGET_NAME})
|
||||
if (BUILD_SERVER)
|
||||
add_dependencies(assignment-client ${TARGET_NAME})
|
||||
endif()
|
||||
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Plugins")
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
macro(SETUP_HIFI_PLUGIN)
|
||||
set(${TARGET_NAME}_SHARED 1)
|
||||
setup_hifi_library(${ARGV})
|
||||
add_dependencies(interface ${TARGET_NAME})
|
||||
if (BUILD_CLIENT)
|
||||
add_dependencies(interface ${TARGET_NAME})
|
||||
endif()
|
||||
target_link_libraries(${TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT})
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Plugins")
|
||||
|
||||
|
|
84
cmake/macros/SetupQt.cmake
Normal file
84
cmake/macros/SetupQt.cmake
Normal file
|
@ -0,0 +1,84 @@
|
|||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Bradley Austin Davis on 2015/10/10
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
function(set_from_env _RESULT_NAME _ENV_VAR_NAME _DEFAULT_VALUE)
|
||||
if ("$ENV{${_ENV_VAR_NAME}}" STREQUAL "")
|
||||
set (${_RESULT_NAME} ${_DEFAULT_VALUE} PARENT_SCOPE)
|
||||
else()
|
||||
set (${_RESULT_NAME} $ENV{${_ENV_VAR_NAME}} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Construct a default QT location from a root path, a version and an architecture
|
||||
function(calculate_default_qt_dir _RESULT_NAME)
|
||||
if (ANDROID)
|
||||
set(QT_DEFAULT_ARCH "android_armv7")
|
||||
elseif(UWP)
|
||||
set(QT_DEFAULT_ARCH "winrt_x64_msvc2017")
|
||||
elseif(APPLE)
|
||||
set(QT_DEFAULT_ARCH "clang_64")
|
||||
elseif(WIN32)
|
||||
set(QT_DEFAULT_ARCH "msvc2017_64")
|
||||
else()
|
||||
set(QT_DEFAULT_ARCH "gcc_64")
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
set(QT_DEFAULT_ROOT "c:/Qt")
|
||||
else()
|
||||
set(QT_DEFAULT_ROOT "$ENV{HOME}/Qt")
|
||||
endif()
|
||||
|
||||
set_from_env(QT_ROOT QT_ROOT ${QT_DEFAULT_ROOT})
|
||||
set_from_env(QT_VERSION QT_VERSION "5.9.1")
|
||||
set_from_env(QT_ARCH QT_ARCH ${QT_DEFAULT_ARCH})
|
||||
|
||||
set(${_RESULT_NAME} "${QT_ROOT}/${QT_VERSION}/${QT_ARCH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Sets the QT_CMAKE_PREFIX_PATH and QT_DIR variables
|
||||
# Also enables CMAKE_AUTOMOC and CMAKE_AUTORCC
|
||||
macro(setup_qt)
|
||||
set(QT_CMAKE_PREFIX_PATH "$ENV{QT_CMAKE_PREFIX_PATH}")
|
||||
if (("QT_CMAKE_PREFIX_PATH" STREQUAL "") OR (NOT EXISTS "${QT_CMAKE_PREFIX_PATH}"))
|
||||
calculate_default_qt_dir(QT_DIR)
|
||||
set(QT_CMAKE_PREFIX_PATH "${QT_DIR}/lib/cmake")
|
||||
else()
|
||||
# figure out where the qt dir is
|
||||
get_filename_component(QT_DIR "${QT_CMAKE_PREFIX_PATH}/../../" ABSOLUTE)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
# windows shell does not like backslashes expanded on the command line,
|
||||
# so convert all backslashes in the QT path to forward slashes
|
||||
string(REPLACE \\ / QT_CMAKE_PREFIX_PATH ${QT_CMAKE_PREFIX_PATH})
|
||||
string(REPLACE \\ / QT_DIR ${QT_DIR})
|
||||
endif()
|
||||
|
||||
# This check doesn't work on Mac
|
||||
#if (NOT EXISTS "${QT_DIR}/include/QtCore/QtGlobal")
|
||||
# message(FATAL_ERROR "Unable to locate Qt includes in ${QT_DIR}")
|
||||
#endif()
|
||||
|
||||
if (NOT EXISTS "${QT_CMAKE_PREFIX_PATH}/Qt5Core/Qt5CoreConfig.cmake")
|
||||
message(FATAL_ERROR "Unable to locate Qt cmake config in ${QT_CMAKE_PREFIX_PATH}")
|
||||
endif()
|
||||
|
||||
message(STATUS "The Qt build in use is: \"${QT_DIR}\"")
|
||||
|
||||
# Instruct CMake to run moc automatically when needed.
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
# Instruct CMake to run rcc automatically when needed
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs("${QT_DIR}/bin")
|
||||
endif ()
|
||||
|
||||
endmacro()
|
|
@ -34,26 +34,11 @@ if (UNIX)
|
|||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
|
||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
||||
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
# http://www.slproweb.com/products/Win32OpenSSL.html
|
||||
set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]"
|
||||
)
|
||||
set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win64" "C:/OpenSSL/" "C:/OpenSSL-Win64/")
|
||||
if (("${CMAKE_SIZEOF_VOID_P}" EQUAL "8"))
|
||||
set(_OPENSSL_ROOT_HINTS_AND_PATHS $ENV{VCPKG_ROOT}/installed/x64-windows)
|
||||
else()
|
||||
# http://www.slproweb.com/products/Win32OpenSSL.html
|
||||
set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]"
|
||||
)
|
||||
set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win32" "C:/OpenSSL/" "C:/OpenSSL-Win32/")
|
||||
set(_OPENSSL_ROOT_HINTS_AND_PATHS $ENV{VCPKG_ROOT}/installed/x86-windows)
|
||||
endif()
|
||||
|
||||
unset(_programfiles)
|
||||
set(_OPENSSL_ROOT_HINTS_AND_PATHS HINTS ${_OPENSSL_ROOT_HINTS} PATHS ${_OPENSSL_ROOT_PATHS})
|
||||
|
||||
else ()
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("openssl")
|
||||
|
@ -67,47 +52,14 @@ find_path(OPENSSL_INCLUDE_DIR NAMES openssl/ssl.h HINTS ${_OPENSSL_ROOT_HINTS_AN
|
|||
|
||||
if (WIN32 AND NOT CYGWIN)
|
||||
if (MSVC)
|
||||
|
||||
# In Visual C++ naming convention each of these four kinds of Windows libraries has it's standard suffix:
|
||||
# * MD for dynamic-release
|
||||
# * MDd for dynamic-debug
|
||||
# * MT for static-release
|
||||
# * MTd for static-debug
|
||||
|
||||
# Implementation details:
|
||||
# We are using the libraries located in the VC subdir instead of the parent directory eventhough :
|
||||
# libeay32MD.lib is identical to ../libeay32.lib, and
|
||||
# ssleay32MD.lib is identical to ../ssleay32.lib
|
||||
|
||||
# The Kitware FindOpenSSL module has been modified here by High Fidelity to look specifically for static libraries
|
||||
|
||||
find_library(LIB_EAY_DEBUG NAMES libeay32MTd
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib/VC/static"
|
||||
)
|
||||
|
||||
find_library(LIB_EAY_RELEASE NAMES libeay32MT
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib/VC/static"
|
||||
)
|
||||
|
||||
find_library(SSL_EAY_DEBUG NAMES ssleay32MTd
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib/VC/static"
|
||||
)
|
||||
|
||||
find_library(SSL_EAY_RELEASE NAMES ssleay32MT
|
||||
${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib/VC/static"
|
||||
)
|
||||
|
||||
set(LIB_EAY_LIBRARY_DEBUG "${LIB_EAY_DEBUG}")
|
||||
set(LIB_EAY_LIBRARY_RELEASE "${LIB_EAY_RELEASE}")
|
||||
set(SSL_EAY_LIBRARY_DEBUG "${SSL_EAY_DEBUG}")
|
||||
set(SSL_EAY_LIBRARY_RELEASE "${SSL_EAY_RELEASE}")
|
||||
# Using vcpkg builds of openssl
|
||||
find_library(LIB_EAY_LIBRARY_RELEASE NAMES libeay32 HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib")
|
||||
find_library(SSL_EAY_LIBRARY_RELEASE NAMES ssleay32 HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib")
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(LIB_EAY)
|
||||
select_library_configurations(SSL_EAY)
|
||||
|
||||
set(OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY})
|
||||
|
||||
find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" ${_OPENSSL_ROOT_HINTS_AND_PATHS})
|
||||
endif()
|
||||
else()
|
||||
|
|
|
@ -40,6 +40,7 @@ set(CONSOLE_DESKTOP_SHORTCUT_REG_KEY "@CONSOLE_DESKTOP_SHORTCUT_REG_KEY@")
|
|||
set(CONSOLE_STARTUP_REG_KEY "@CONSOLE_STARTUP_REG_KEY@")
|
||||
set(SERVER_LAUNCH_NOW_REG_KEY "@SERVER_LAUNCH_NOW_REG_KEY@")
|
||||
set(CLIENT_LAUNCH_NOW_REG_KEY "@CLIENT_LAUNCH_NOW_REG_KEY@")
|
||||
set(CUSTOM_INSTALL_REG_KEY "@CUSTOM_INSTALL_REG_KEY@")
|
||||
set(INSTALLER_HEADER_IMAGE "@INSTALLER_HEADER_IMAGE@")
|
||||
set(UNINSTALLER_HEADER_IMAGE "@UNINSTALLER_HEADER_IMAGE@")
|
||||
set(ADD_REMOVE_ICON_PATH "@ADD_REMOVE_ICON_PATH@")
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
Var STR_CONTAINS_VAR_3
|
||||
Var STR_CONTAINS_VAR_4
|
||||
Var STR_RETURN_VAR
|
||||
|
||||
|
||||
Function StrContains
|
||||
Exch $STR_NEEDLE
|
||||
Exch 1
|
||||
|
@ -343,29 +343,29 @@ SectionEnd
|
|||
;--------------------------------
|
||||
;Pages
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
|
||||
|
||||
!insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@"
|
||||
|
||||
|
||||
Page custom InstallTypesPage ReadInstallTypes
|
||||
|
||||
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
|
||||
|
||||
;Start Menu Folder Page Configuration
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
|
||||
|
||||
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction
|
||||
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
|
||||
|
||||
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction
|
||||
@CPACK_NSIS_PAGE_COMPONENTS@
|
||||
|
||||
|
||||
Page custom PostInstallOptionsPage ReadPostInstallOptions
|
||||
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
|
@ -453,9 +453,10 @@ Var ExpressInstallRadioButton
|
|||
Var CustomInstallRadioButton
|
||||
Var InstallTypeDialog
|
||||
Var Express
|
||||
Var CustomInstallTemporaryState
|
||||
|
||||
!macro SetPostInstallOption Checkbox OptionName Default
|
||||
; reads the value for the given post install option to the registry
|
||||
!macro SetInstallOption Checkbox OptionName Default
|
||||
; reads the value for the given install option to the registry
|
||||
ReadRegStr $0 HKLM "@REGISTRY_HKLM_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\@POST_INSTALL_OPTIONS_REG_GROUP@" "${OptionName}"
|
||||
|
||||
${If} $0 == "NO"
|
||||
|
@ -472,31 +473,39 @@ Var Express
|
|||
|
||||
Function InstallTypesPage
|
||||
!insertmacro MUI_HEADER_TEXT "Choose Installation Type" "Express or Custom Install"
|
||||
|
||||
|
||||
nsDialogs::Create 1018
|
||||
Pop $InstallTypeDialog
|
||||
|
||||
|
||||
${If} $InstallTypeDialog == error
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
|
||||
StrCpy $CurrentOffset 0
|
||||
StrCpy $OffsetUnits u
|
||||
StrCpy $Express "0"
|
||||
|
||||
StrCpy $Express "0"
|
||||
|
||||
${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls High Fidelity Interface and High Fidelity Sandbox"
|
||||
pop $ExpressInstallRadioButton
|
||||
${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
|
||||
|
||||
${NSD_CreateRadiobutton} 30% $CurrentOffset$OffsetUnits 100% 10u "Custom Install (Advanced)"
|
||||
pop $CustomInstallRadioButton
|
||||
${NSD_OnClick} $CustomInstallRadioButton ChangeCustomLabel
|
||||
|
||||
; Express Install selected by default
|
||||
${NSD_Check} $ExpressInstallRadioButton
|
||||
${NSD_OnClick} $CustomInstallRadioButton ChangeCustomLabel
|
||||
|
||||
; check install type from the registry, express install by default
|
||||
!insertmacro SetInstallOption $CustomInstallRadioButton @CUSTOM_INSTALL_REG_KEY@ ${BST_UNCHECKED}
|
||||
|
||||
; set the express install value based on the custom install value from registry
|
||||
${NSD_GetState} $CustomInstallRadioButton $CustomInstallTemporaryState
|
||||
|
||||
${If} $CustomInstallTemporaryState == ${BST_UNCHECKED}
|
||||
${NSD_Check} $ExpressInstallRadioButton
|
||||
${EndIf}
|
||||
|
||||
Call ChangeExpressLabel
|
||||
|
||||
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
|
@ -519,18 +528,18 @@ Function AbortFunction
|
|||
StrCmp $Express "1" 0 end
|
||||
Abort
|
||||
end:
|
||||
FunctionEnd
|
||||
FunctionEnd
|
||||
|
||||
Function PostInstallOptionsPage
|
||||
!insertmacro MUI_HEADER_TEXT "Setup Options" ""
|
||||
|
||||
nsDialogs::Create 1018
|
||||
Pop $PostInstallDialog
|
||||
|
||||
|
||||
${If} $PostInstallDialog == error
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
|
||||
; Check if Express is set, if so, abort the post install options page
|
||||
StrCmp $Express "1" 0 end
|
||||
Abort
|
||||
|
@ -543,18 +552,18 @@ Function PostInstallOptionsPage
|
|||
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @INTERFACE_HF_SHORTCUT_NAME@"
|
||||
Pop $DesktopClientCheckbox
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
|
||||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetPostInstallOption $DesktopClientCheckbox @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ ${BST_CHECKED}
|
||||
!insertmacro SetInstallOption $DesktopClientCheckbox @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ ${BST_CHECKED}
|
||||
${EndIf}
|
||||
|
||||
|
||||
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
||||
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @CONSOLE_HF_SHORTCUT_NAME@"
|
||||
Pop $DesktopServerCheckbox
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
|
||||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetPostInstallOption $DesktopServerCheckbox @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ ${BST_UNCHECKED}
|
||||
!insertmacro SetInstallOption $DesktopServerCheckbox @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
|
||||
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
||||
|
@ -562,7 +571,7 @@ Function PostInstallOptionsPage
|
|||
Pop $LaunchServerNowCheckbox
|
||||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetPostInstallOption $LaunchServerNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||
!insertmacro SetInstallOption $LaunchServerNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||
${StrContains} $substringResult "/forceNoLaunchServer" $CMDLINE
|
||||
${IfNot} $substringResult == ""
|
||||
${NSD_SetState} $LaunchServerNowCheckbox ${BST_UNCHECKED}
|
||||
|
@ -570,29 +579,29 @@ Function PostInstallOptionsPage
|
|||
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
${EndIf}
|
||||
|
||||
|
||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @INTERFACE_HF_SHORTCUT_NAME@ after install"
|
||||
Pop $LaunchClientNowCheckbox
|
||||
IntOp $CurrentOffset $CurrentOffset + 30
|
||||
|
||||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetPostInstallOption $LaunchClientNowCheckbox @CLIENT_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||
!insertmacro SetInstallOption $LaunchClientNowCheckbox @CLIENT_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||
${StrContains} $substringResult "/forceNoLaunchClient" $CMDLINE
|
||||
${IfNot} $substringResult == ""
|
||||
${NSD_SetState} $LaunchClientNowCheckbox ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
|
||||
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
||||
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ on startup"
|
||||
Pop $ServerStartupCheckbox
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
|
||||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetPostInstallOption $ServerStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED}
|
||||
!insertmacro SetInstallOption $ServerStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED}
|
||||
${EndIf}
|
||||
|
||||
|
||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Perform a clean install (Delete older settings and content)"
|
||||
Pop $CleanInstallCheckbox
|
||||
|
@ -618,12 +627,12 @@ Function PostInstallOptionsPage
|
|||
|
||||
${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
|
||||
|
||||
nsDialogs::Show
|
||||
FunctionEnd
|
||||
|
||||
!macro WritePostInstallOption OptionName Option
|
||||
; writes the value for the given post install option to the registry
|
||||
!macro WriteInstallOption OptionName Option
|
||||
; writes the value for the given install option to the registry
|
||||
WriteRegStr HKLM "@REGISTRY_HKLM_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\@POST_INSTALL_OPTIONS_REG_GROUP@" "${OptionName}" ${Option}
|
||||
!macroend
|
||||
|
||||
|
@ -641,12 +650,12 @@ Function ReadInstallTypes
|
|||
; check if the user asked for express/custom install
|
||||
${NSD_GetState} $ExpressInstallRadioButton $ExpressInstallState
|
||||
${NSD_GetState} $CustomInstallRadioButton $CustomInstallState
|
||||
|
||||
|
||||
${If} $ExpressInstallState == ${BST_CHECKED}
|
||||
StrCpy $Express "1"
|
||||
|
||||
StrCpy $DesktopClientState ${BST_CHECKED}
|
||||
StrCpy $ServerStartupState ${BST_CHECKED}
|
||||
|
||||
StrCpy $DesktopClientState ${BST_CHECKED}
|
||||
StrCpy $ServerStartupState ${BST_CHECKED}
|
||||
StrCpy $LaunchServerNowState ${BST_CHECKED}
|
||||
StrCpy $LaunchClientNowState ${BST_CHECKED}
|
||||
StrCpy $CleanInstallState ${BST_UNCHECKED}
|
||||
|
@ -655,9 +664,12 @@ Function ReadInstallTypes
|
|||
${If} @PR_BUILD@ == 1
|
||||
StrCpy $CopyFromProductionState ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
|
||||
|
||||
!insertmacro WriteInstallOption "@CUSTOM_INSTALL_REG_KEY@" NO
|
||||
${Else}
|
||||
!insertmacro WriteInstallOption "@CUSTOM_INSTALL_REG_KEY@" YES
|
||||
${EndIf}
|
||||
|
||||
|
||||
FunctionEnd
|
||||
|
||||
Function ReadPostInstallOptions
|
||||
|
@ -683,12 +695,12 @@ Function ReadPostInstallOptions
|
|||
; check if we need to launch the server post-install
|
||||
${NSD_GetState} $LaunchServerNowCheckbox $LaunchServerNowState
|
||||
${EndIf}
|
||||
|
||||
|
||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||
; check if we need to launch the client post-install
|
||||
${NSD_GetState} $LaunchClientNowCheckbox $LaunchClientNowState
|
||||
${EndIf}
|
||||
|
||||
|
||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||
; check if the user asked for a clean install
|
||||
${NSD_GetState} $CleanInstallCheckbox $CleanInstallState
|
||||
|
@ -700,9 +712,9 @@ Function HandlePostInstallOptions
|
|||
; check if the user asked for a desktop shortcut to High Fidelity
|
||||
${If} $DesktopClientState == ${BST_CHECKED}
|
||||
CreateShortCut "$DESKTOP\@INTERFACE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@"
|
||||
!insertmacro WritePostInstallOption "@CLIENT_DESKTOP_SHORTCUT_REG_KEY@" YES
|
||||
!insertmacro WriteInstallOption "@CLIENT_DESKTOP_SHORTCUT_REG_KEY@" YES
|
||||
${Else}
|
||||
!insertmacro WritePostInstallOption @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ NO
|
||||
!insertmacro WriteInstallOption @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ NO
|
||||
${EndIf}
|
||||
|
||||
${EndIf}
|
||||
|
@ -711,12 +723,12 @@ Function HandlePostInstallOptions
|
|||
; check if the user asked for a desktop shortcut to Sandbox
|
||||
${If} $DesktopServerState == ${BST_CHECKED}
|
||||
CreateShortCut "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
|
||||
!insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ YES
|
||||
!insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ YES
|
||||
${Else}
|
||||
!insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO
|
||||
!insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO
|
||||
${EndIf}
|
||||
|
||||
|
||||
|
||||
; check if the user asked to have Sandbox launched every startup
|
||||
${If} $ServerStartupState == ${BST_CHECKED}
|
||||
; in case we added a shortcut in the global context, pull that now
|
||||
|
@ -730,12 +742,12 @@ Function HandlePostInstallOptions
|
|||
; reset the shell var context back
|
||||
SetShellVarContext all
|
||||
|
||||
!insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ YES
|
||||
!insertmacro WriteInstallOption @CONSOLE_STARTUP_REG_KEY@ YES
|
||||
${Else}
|
||||
!insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ NO
|
||||
!insertmacro WriteInstallOption @CONSOLE_STARTUP_REG_KEY@ NO
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
|
||||
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
|
||||
; check if the user asked for a clean install
|
||||
${If} $CleanInstallState == ${BST_CHECKED}
|
||||
|
@ -774,28 +786,28 @@ Function HandlePostInstallOptions
|
|||
${EndIf}
|
||||
|
||||
${If} $LaunchServerNowState == ${BST_CHECKED}
|
||||
!insertmacro WritePostInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ YES
|
||||
!insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ YES
|
||||
|
||||
; both launches use the explorer trick in case the user has elevated permissions for the installer
|
||||
${If} $LaunchClientNowState == ${BST_CHECKED}
|
||||
!insertmacro WritePostInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ YES
|
||||
!insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ YES
|
||||
; create shortcut with ARGUMENTS
|
||||
CreateShortCut "$TEMP\SandboxShortcut.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@" "-- --launchInterface"
|
||||
Exec '"$WINDIR\explorer.exe" "$TEMP\SandboxShortcut.lnk"'
|
||||
${Else}
|
||||
!insertmacro WritePostInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ NO
|
||||
!insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ NO
|
||||
Exec '"$WINDIR\explorer.exe" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"'
|
||||
${EndIf}
|
||||
|
||||
${Else}
|
||||
!insertmacro WritePostInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ NO
|
||||
!insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ NO
|
||||
|
||||
; launch uses the explorer trick in case the user has elevated permissions for the installer
|
||||
${If} $LaunchClientNowState == ${BST_CHECKED}
|
||||
!insertmacro WritePostInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ YES
|
||||
!insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ YES
|
||||
Exec '"$WINDIR\explorer.exe" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@"'
|
||||
${Else}
|
||||
!insertmacro WritePostInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ NO
|
||||
!insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ NO
|
||||
${EndIf}
|
||||
|
||||
${EndIf}
|
||||
|
@ -843,7 +855,7 @@ Section "-Core installation"
|
|||
Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml"
|
||||
|
||||
ExecWait "$INSTDIR\vcredist_x64.exe /install /q /norestart"
|
||||
|
||||
|
||||
; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console)
|
||||
RMDir /r "$INSTDIR\Interface"
|
||||
Delete "$INSTDIR\vcredist_x64.exe"
|
||||
|
@ -943,9 +955,9 @@ Section "-Core installation"
|
|||
Call ConditionalAddToRegisty
|
||||
|
||||
!insertmacro MUI_STARTMENU_WRITE_END
|
||||
|
||||
|
||||
@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@
|
||||
|
||||
|
||||
; Handle whichever post install options were set
|
||||
Call HandlePostInstallOptions
|
||||
|
||||
|
@ -963,9 +975,18 @@ SectionEnd
|
|||
${If} $R0 == 0
|
||||
|
||||
; the process is running, ask the user to close it
|
||||
|
||||
${If} "${displayName}" == "@CONSOLE_DISPLAY_NAME@"
|
||||
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION \
|
||||
"${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it and click Retry to continue." \
|
||||
"${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it in the system tray and click Retry to continue." \
|
||||
/SD IDCANCEL IDRETRY Prompt_${UniqueID} IDCANCEL 0
|
||||
${EndIf}
|
||||
|
||||
${If} "${displayName}" == "@INTERFACE_DISPLAY_NAME@"
|
||||
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION \
|
||||
"${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it in the task bar and click Retry to continue." \
|
||||
/SD IDCANCEL IDRETRY Prompt_${UniqueID} IDCANCEL 0
|
||||
${EndIf}
|
||||
|
||||
; If the user decided to cancel, stop the current installer/uninstaller
|
||||
Abort
|
||||
|
|
|
@ -752,8 +752,28 @@ void DomainServer::setupICEHeartbeatForFullNetworking() {
|
|||
}
|
||||
|
||||
void DomainServer::updateICEServerAddresses() {
|
||||
if (_iceAddressLookupID == -1) {
|
||||
if (_iceAddressLookupID == INVALID_ICE_LOOKUP_ID) {
|
||||
_iceAddressLookupID = QHostInfo::lookupHost(_iceServerAddr, this, SLOT(handleICEHostInfo(QHostInfo)));
|
||||
|
||||
// there seems to be a 5.9 bug where lookupHost never calls our slot
|
||||
// so we add a single shot manual "timeout" to fire it off again if it hasn't called back yet
|
||||
static const int ICE_ADDRESS_LOOKUP_TIMEOUT_MS = 5000;
|
||||
QTimer::singleShot(ICE_ADDRESS_LOOKUP_TIMEOUT_MS, this, &DomainServer::timeoutICEAddressLookup);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::timeoutICEAddressLookup() {
|
||||
if (_iceAddressLookupID != INVALID_ICE_LOOKUP_ID) {
|
||||
// we waited 5s and didn't hear back for our ICE DNS lookup
|
||||
// so time that one out and kick off another
|
||||
|
||||
qDebug() << "IP address lookup timed out for" << _iceServerAddr << "- retrying";
|
||||
|
||||
QHostInfo::abortHostLookup(_iceAddressLookupID);
|
||||
|
||||
_iceAddressLookupID = INVALID_ICE_LOOKUP_ID;
|
||||
|
||||
updateICEServerAddresses();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ typedef QMultiHash<QUuid, WalletTransaction*> TransactionHash;
|
|||
using Subnet = QPair<QHostAddress, int>;
|
||||
using SubnetList = std::vector<Subnet>;
|
||||
|
||||
const int INVALID_ICE_LOOKUP_ID = -1;
|
||||
|
||||
enum ReplicationServerDirection {
|
||||
Upstream,
|
||||
Downstream
|
||||
|
@ -114,6 +116,8 @@ private slots:
|
|||
void tokenGrantFinished();
|
||||
void profileRequestFinished();
|
||||
|
||||
void timeoutICEAddressLookup();
|
||||
|
||||
signals:
|
||||
void iceServerChanged();
|
||||
void userConnected();
|
||||
|
@ -223,7 +227,7 @@ private:
|
|||
|
||||
QList<QHostAddress> _iceServerAddresses;
|
||||
QSet<QHostAddress> _failedIceServerAddresses;
|
||||
int _iceAddressLookupID { -1 };
|
||||
int _iceAddressLookupID { INVALID_ICE_LOOKUP_ID };
|
||||
int _noReplyICEHeartbeats { 0 };
|
||||
int _numHeartbeatDenials { 0 };
|
||||
bool _connectedToICEServer { false };
|
||||
|
|
|
@ -6,7 +6,14 @@
|
|||
<title>Welcome to Interface</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
@font-face {
|
||||
font-family: 'Raleway Light';
|
||||
src: url('../fonts/Raleway-Light.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
background: black;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
|
@ -15,6 +22,14 @@
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
a:link {color:inherit}
|
||||
a:active {color:inherit}
|
||||
a:visited {color:inherit}
|
||||
a:hover {
|
||||
color:inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#left_button {
|
||||
position: absolute;
|
||||
left: 70;
|
||||
|
@ -38,6 +53,15 @@
|
|||
position: absolute;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
}
|
||||
|
||||
#report_problem {
|
||||
position: fixed;
|
||||
top: 10;
|
||||
right: 10;
|
||||
font-family: "Raleway Light", sans-serif;
|
||||
text-decoration: none;
|
||||
color: #ddd;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var handControllerImageURL = null;
|
||||
|
@ -152,6 +176,7 @@
|
|||
<a href="#" id="left_button" onmousedown="cycleLeft()"></a>
|
||||
<a href="#" id="right_button" onmousedown="cycleRight()"></a>
|
||||
</div>
|
||||
<a href="mailto:support@highfidelity.com" id="report_problem">Report Problem</a>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtWebChannel 1.0
|
||||
import QtWebEngine 1.2
|
||||
import QtWebEngine 1.5
|
||||
|
||||
import "controls"
|
||||
import "controls-uit" as HifiControls
|
||||
|
@ -13,11 +13,11 @@ Item {
|
|||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
HifiStyles.HifiConstants { id: hifistyles }
|
||||
//width: parent.width
|
||||
|
||||
height: 600
|
||||
property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
|
||||
property alias url: webview.url
|
||||
property WebEngineView webView: webview
|
||||
|
||||
property bool canGoBack: webview.canGoBack
|
||||
property bool canGoForward: webview.canGoForward
|
||||
|
||||
|
@ -123,5 +123,4 @@ Item {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtWebEngine 1.2
|
||||
import QtWebEngine 1.5
|
||||
|
||||
WebEngineView {
|
||||
id: root
|
||||
|
|
|
@ -9,10 +9,13 @@
|
|||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtWebEngine 1.5
|
||||
|
||||
AnimatedImage {
|
||||
property WebEngineView webview: parent
|
||||
source: "../../icons/loader-snake-64-w.gif"
|
||||
visible: parent.loading && /^(http.*|)$/i.test(parent.url.toString())
|
||||
visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString())
|
||||
playing: visible
|
||||
z: 10000
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
|
|
132
interface/resources/qml/controls/FlickableWebViewCore.qml
Normal file
132
interface/resources/qml/controls/FlickableWebViewCore.qml
Normal file
|
@ -0,0 +1,132 @@
|
|||
import QtQuick 2.7
|
||||
import QtWebEngine 1.5
|
||||
import QtWebChannel 1.0
|
||||
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
import "../styles-uit" as StylesUIt
|
||||
|
||||
Item {
|
||||
id: flick
|
||||
|
||||
property alias url: webViewCore.url
|
||||
property alias canGoBack: webViewCore.canGoBack
|
||||
property alias webViewCore: webViewCore
|
||||
property alias webViewCoreProfile: webViewCore.profile
|
||||
property string webViewCoreUserAgent
|
||||
|
||||
property string userScriptUrl: ""
|
||||
property string urlTag: "noDownload=false";
|
||||
|
||||
signal newViewRequestedCallback(var request)
|
||||
signal loadingChangedCallback(var loadRequest)
|
||||
|
||||
property bool interactive: false
|
||||
|
||||
StylesUIt.HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
function onLoadingChanged(loadRequest) {
|
||||
if (WebEngineView.LoadStartedStatus === loadRequest.status) {
|
||||
|
||||
// Required to support clicking on "hifi://" links
|
||||
var url = loadRequest.url.toString();
|
||||
url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag;
|
||||
if (urlHandler.canHandleUrl(url)) {
|
||||
if (urlHandler.handleUrl(url)) {
|
||||
webViewCore.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (WebEngineView.LoadFailedStatus === loadRequest.status) {
|
||||
console.log(" Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
|
||||
}
|
||||
|
||||
if (WebEngineView.LoadSucceededStatus === loadRequest.status) {
|
||||
//disable Chromium's scroll bars
|
||||
}
|
||||
}
|
||||
|
||||
WebEngineView {
|
||||
id: webViewCore
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
profile: HFWebEngineProfile;
|
||||
settings.pluginsEnabled: true
|
||||
settings.touchIconsEnabled: true
|
||||
settings.allowRunningInsecureContent: true
|
||||
|
||||
// creates a global EventBridge object.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.DocumentCreation
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// detects when to raise and lower virtual keyboard
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// User script.
|
||||
WebEngineScript {
|
||||
id: userScript
|
||||
sourceUrl: flick.userScriptUrl
|
||||
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
|
||||
|
||||
property string newUrl: ""
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
if (webViewCoreUserAgent !== undefined) {
|
||||
webViewCore.profile.httpUserAgent = webViewCoreUserAgent
|
||||
} else {
|
||||
webViewCore.profile.httpUserAgent += " (HighFidelityInterface)";
|
||||
}
|
||||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
}
|
||||
|
||||
//disable popup
|
||||
onContextMenuRequested: {
|
||||
request.accepted = true;
|
||||
}
|
||||
|
||||
onNewViewRequested: {
|
||||
newViewRequestedCallback(request)
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
flick.onLoadingChanged(loadRequest)
|
||||
loadingChangedCallback(loadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedImage {
|
||||
//anchoring doesnt works here when changing content size
|
||||
x: flick.width/2 - width/2
|
||||
y: flick.height/2 - height/2
|
||||
source: "../../icons/loader-snake-64-w.gif"
|
||||
visible: webViewCore.loading && /^(http.*|)$/i.test(webViewCore.url.toString())
|
||||
playing: visible
|
||||
z: 10000
|
||||
}
|
||||
}
|
|
@ -1,14 +1,13 @@
|
|||
import QtQuick 2.5
|
||||
import QtWebEngine 1.1
|
||||
import QtWebChannel 1.0
|
||||
import QtQuick 2.7
|
||||
import "../controls-uit" as HiFiControls
|
||||
|
||||
Item {
|
||||
property alias url: root.url
|
||||
property alias scriptURL: root.userScriptUrl
|
||||
property alias canGoBack: root.canGoBack;
|
||||
property var goBack: root.goBack;
|
||||
property alias urlTag: root.urlTag
|
||||
id: root
|
||||
property alias url: webroot.url
|
||||
property alias scriptURL: webroot.userScriptUrl
|
||||
property alias canGoBack: webroot.canGoBack;
|
||||
property var goBack: webroot.webViewCore.goBack;
|
||||
property alias urlTag: webroot.urlTag
|
||||
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
@ -21,84 +20,20 @@ Item {
|
|||
}
|
||||
*/
|
||||
|
||||
property alias viewProfile: root.profile
|
||||
property alias viewProfile: webroot.webViewCoreProfile
|
||||
|
||||
WebEngineView {
|
||||
id: root
|
||||
objectName: "webEngineView"
|
||||
x: 0
|
||||
y: 0
|
||||
FlickableWebViewCore {
|
||||
id: webroot
|
||||
width: parent.width
|
||||
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
|
||||
|
||||
profile: HFWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
// creates a global EventBridge object.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.DocumentCreation
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// detects when to raise and lower virtual keyboard
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// User script.
|
||||
WebEngineScript {
|
||||
id: userScript
|
||||
sourceUrl: root.userScriptUrl
|
||||
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
property string urlTag: "noDownload=false";
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
|
||||
|
||||
property string newUrl: ""
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
|
||||
root.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
|
||||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
onLoadingChangedCallback: {
|
||||
keyboardRaised = false;
|
||||
punctuationMode = false;
|
||||
keyboard.resetShiftMode(false);
|
||||
|
||||
// Required to support clicking on "hifi://" links
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
var url = loadRequest.url.toString();
|
||||
url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag;
|
||||
if (urlHandler.canHandleUrl(url)) {
|
||||
if (urlHandler.handleUrl(url)) {
|
||||
root.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onNewViewRequested:{
|
||||
onNewViewRequestedCallback: {
|
||||
// desktop is not defined for web-entities or tablet
|
||||
if (typeof desktop !== "undefined") {
|
||||
desktop.openBrowserWindow(request, profile);
|
||||
|
@ -107,7 +42,6 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
HiFiControls.WebSpinner { }
|
||||
}
|
||||
|
||||
HiFiControls.Keyboard {
|
||||
|
@ -120,5 +54,4 @@ Item {
|
|||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtWebEngine 1.2
|
||||
import QtWebChannel 1.0
|
||||
import QtQuick 2.7
|
||||
import QtWebEngine 1.5
|
||||
import "../controls-uit" as HiFiControls
|
||||
import "../styles" as HifiStyles
|
||||
import "../styles-uit"
|
||||
import "../"
|
||||
import "."
|
||||
|
||||
Item {
|
||||
id: web
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
width: parent !== null ? parent.width : undefined
|
||||
height: parent !== null ? parent.height : undefined
|
||||
property var parentStackItem: null
|
||||
property int headerHeight: 70
|
||||
property string url
|
||||
|
@ -21,8 +17,8 @@ Item {
|
|||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
property bool isDesktop: false
|
||||
property alias webView: webview
|
||||
property alias profile: webview.profile
|
||||
property alias webView: web.webViewCore
|
||||
property alias profile: web.webViewCoreProfile
|
||||
property bool remove: false
|
||||
property bool closeButtonVisible: true
|
||||
|
||||
|
@ -79,7 +75,7 @@ Item {
|
|||
color: hifi.colors.baseGray
|
||||
font.pixelSize: 12
|
||||
verticalAlignment: Text.AlignLeft
|
||||
text: webview.url
|
||||
text: root.url
|
||||
anchors {
|
||||
top: nav.bottom
|
||||
horizontalCenter: parent.horizontalCenter;
|
||||
|
@ -104,13 +100,13 @@ Item {
|
|||
|
||||
function closeWebEngine() {
|
||||
if (remove) {
|
||||
web.destroy();
|
||||
root.destroy();
|
||||
return;
|
||||
}
|
||||
if (parentStackItem) {
|
||||
parentStackItem.pop();
|
||||
} else {
|
||||
web.visible = false;
|
||||
root.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,67 +124,19 @@ Item {
|
|||
}
|
||||
|
||||
function loadUrl(url) {
|
||||
webview.url = url
|
||||
web.url = webview.url;
|
||||
web.webViewCore.url = url
|
||||
root.url = web.webViewCore.url;
|
||||
}
|
||||
|
||||
onUrlChanged: {
|
||||
loadUrl(url);
|
||||
}
|
||||
|
||||
WebEngineView {
|
||||
id: webview
|
||||
objectName: "webEngineView"
|
||||
x: 0
|
||||
y: 0
|
||||
FlickableWebViewCore {
|
||||
id: web
|
||||
width: parent.width
|
||||
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - web.headerHeight : parent.height - web.headerHeight
|
||||
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - root.headerHeight : parent.height - root.headerHeight
|
||||
anchors.top: buttons.bottom
|
||||
profile: HFWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
// creates a global EventBridge object.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.DocumentCreation
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// detects when to raise and lower virtual keyboard
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// User script.
|
||||
WebEngineScript {
|
||||
id: userScript
|
||||
sourceUrl: webview.userScriptUrl
|
||||
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
property string urlTag: "noDownload=false";
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
|
||||
|
||||
property string newUrl: ""
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
}
|
||||
|
||||
onUrlChanged: {
|
||||
// Record history, skipping null and duplicate items.
|
||||
|
@ -201,34 +149,16 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
onLoadingChangedCallback: {
|
||||
keyboardRaised = false;
|
||||
punctuationMode = false;
|
||||
keyboard.resetShiftMode(false);
|
||||
// Required to support clicking on "hifi://" links
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
var url = loadRequest.url.toString();
|
||||
if (urlHandler.canHandleUrl(url)) {
|
||||
if (urlHandler.handleUrl(url)) {
|
||||
root.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (WebEngineView.LoadFailedStatus == loadRequest.status) {
|
||||
console.log(" Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
|
||||
}
|
||||
|
||||
if (WebEngineView.LoadSucceededStatus == loadRequest.status) {
|
||||
webview.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
onNewViewRequested: {
|
||||
request.openIn(webview);
|
||||
webViewCore.forceActiveFocus();
|
||||
}
|
||||
|
||||
HiFiControls.WebSpinner { }
|
||||
onNewViewRequestedCallback: {
|
||||
request.openIn(webViewCore);
|
||||
}
|
||||
}
|
||||
|
||||
HiFiControls.Keyboard {
|
||||
|
@ -244,7 +174,7 @@ Item {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
web.isDesktop = (typeof desktop !== "undefined");
|
||||
root.isDesktop = (typeof desktop !== "undefined");
|
||||
keyboardEnabled = HMD.active;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
import QtQuick 2.5
|
||||
import QtWebEngine 1.1
|
||||
import QtWebChannel 1.0
|
||||
import QtQuick 2.7
|
||||
import "../controls-uit" as HiFiControls
|
||||
|
||||
Item {
|
||||
property alias url: root.url
|
||||
property alias scriptURL: root.userScriptUrl
|
||||
property alias canGoBack: root.canGoBack;
|
||||
property var goBack: root.goBack;
|
||||
property alias urlTag: root.urlTag
|
||||
width: parent !== null ? parent.width : undefined
|
||||
height: parent !== null ? parent.height : undefined
|
||||
|
||||
property alias url: webroot.url
|
||||
property alias scriptURL: webroot.userScriptUrl
|
||||
property alias canGoBack: webroot.canGoBack;
|
||||
property var goBack: webroot.webViewCore.goBack;
|
||||
property alias urlTag: webroot.urlTag
|
||||
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
property alias flickable: webroot.interactive
|
||||
|
||||
// FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface
|
||||
// or provide HMDinfo object to QML in RenderableWebEntityItem and do the following.
|
||||
|
@ -21,82 +23,20 @@ Item {
|
|||
}
|
||||
*/
|
||||
|
||||
property alias viewProfile: root.profile
|
||||
property alias viewProfile: webroot.webViewCoreProfile
|
||||
|
||||
WebEngineView {
|
||||
id: root
|
||||
objectName: "webEngineView"
|
||||
x: 0
|
||||
y: 0
|
||||
FlickableWebViewCore {
|
||||
id: webroot
|
||||
width: parent.width
|
||||
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
|
||||
|
||||
profile: HFWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
// creates a global EventBridge object.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.DocumentCreation
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// detects when to raise and lower virtual keyboard
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// User script.
|
||||
WebEngineScript {
|
||||
id: userScript
|
||||
sourceUrl: root.userScriptUrl
|
||||
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
property string urlTag: "noDownload=false";
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
|
||||
|
||||
property string newUrl: ""
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
onLoadingChangedCallback: {
|
||||
keyboardRaised = false;
|
||||
punctuationMode = false;
|
||||
keyboard.resetShiftMode(false);
|
||||
|
||||
// Required to support clicking on "hifi://" links
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
var url = loadRequest.url.toString();
|
||||
url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag;
|
||||
if (urlHandler.canHandleUrl(url)) {
|
||||
if (urlHandler.handleUrl(url)) {
|
||||
root.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onNewViewRequested:{
|
||||
onNewViewRequestedCallback: {
|
||||
// desktop is not defined for web-entities or tablet
|
||||
if (typeof desktop !== "undefined") {
|
||||
desktop.openBrowserWindow(request, profile);
|
||||
|
@ -104,8 +44,6 @@ Item {
|
|||
tabletRoot.openBrowserWindow(request, profile);
|
||||
}
|
||||
}
|
||||
|
||||
HiFiControls.WebSpinner { }
|
||||
}
|
||||
|
||||
HiFiControls.Keyboard {
|
||||
|
@ -118,5 +56,4 @@ Item {
|
|||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,6 +36,24 @@ Rectangle {
|
|||
return (root.parent !== null) && root.parent.objectName == "loader";
|
||||
}
|
||||
|
||||
property bool showPeaks: true;
|
||||
function enablePeakValues() {
|
||||
Audio.devices.input.peakValuesEnabled = true;
|
||||
Audio.devices.input.peakValuesEnabledChanged.connect(function(enabled) {
|
||||
if (!enabled && root.showPeaks) {
|
||||
Audio.devices.input.peakValuesEnabled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
function disablePeakValues() {
|
||||
root.showPeaks = false;
|
||||
Audio.devices.input.peakValuesEnabled = false;
|
||||
}
|
||||
|
||||
Component.onCompleted: enablePeakValues();
|
||||
Component.onDestruction: disablePeakValues();
|
||||
onVisibleChanged: visible ? enablePeakValues() : disablePeakValues();
|
||||
|
||||
Column {
|
||||
y: 16; // padding does not work
|
||||
spacing: 16;
|
||||
|
@ -133,12 +151,13 @@ Rectangle {
|
|||
onClicked: Audio.setInputDevice(info);
|
||||
}
|
||||
|
||||
InputLevel {
|
||||
id: level;
|
||||
InputPeak {
|
||||
id: inputPeak;
|
||||
visible: Audio.devices.input.peakValuesAvailable;
|
||||
peak: model.peak;
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 30
|
||||
visible: selected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// InputLevel.qml
|
||||
// InputPeak.qml
|
||||
// qml/hifi/audio
|
||||
//
|
||||
// Created by Zach Pomerantz on 6/20/2017
|
||||
|
@ -15,7 +15,7 @@ import QtQuick.Layouts 1.3
|
|||
import QtGraphicalEffects 1.0
|
||||
|
||||
Rectangle {
|
||||
readonly property var level: Audio.inputLevel;
|
||||
property var peak;
|
||||
|
||||
width: 70;
|
||||
height: 8;
|
||||
|
@ -65,7 +65,7 @@ Rectangle {
|
|||
|
||||
Rectangle { // mask
|
||||
id: mask;
|
||||
width: parent.width * level;
|
||||
width: parent.width * peak;
|
||||
radius: 5;
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
|
@ -29,7 +29,6 @@ Rectangle {
|
|||
property bool purchasesReceived: false;
|
||||
property bool balanceReceived: false;
|
||||
property bool securityImageResultReceived: false;
|
||||
property bool keyFilePathIfExistsResultReceived: false;
|
||||
property string itemId: "";
|
||||
property string itemHref: "";
|
||||
property double balanceAfterPurchase: 0;
|
||||
|
@ -41,15 +40,28 @@ Rectangle {
|
|||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onAccountResult: {
|
||||
if (result.status === "success") {
|
||||
commerce.getKeyFilePathIfExists();
|
||||
} else {
|
||||
// unsure how to handle a failure here. We definitely cannot proceed.
|
||||
}
|
||||
}
|
||||
|
||||
onLoginStatusResult: {
|
||||
if (!isLoggedIn && root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
} else if (isLoggedIn) {
|
||||
root.activeView = "initialize";
|
||||
commerce.account();
|
||||
}
|
||||
}
|
||||
|
||||
onKeyFilePathIfExistsResult: {
|
||||
if (path === "" && root.activeView !== "notSetUp") {
|
||||
root.activeView = "notSetUp";
|
||||
} else if (path !== "" && root.activeView === "initialize") {
|
||||
commerce.getSecurityImage();
|
||||
commerce.getKeyFilePathIfExists();
|
||||
commerce.balance();
|
||||
commerce.inventory();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,8 +69,8 @@ Rectangle {
|
|||
securityImageResultReceived = true;
|
||||
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
|
||||
root.activeView = "notSetUp";
|
||||
} else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") {
|
||||
root.activeView = "checkoutMain";
|
||||
} else if (exists && root.activeView === "initialize") {
|
||||
commerce.getWalletAuthenticatedStatus();
|
||||
} else if (exists) {
|
||||
// just set the source again (to be sure the change was noticed)
|
||||
securityImage.source = "";
|
||||
|
@ -66,12 +78,17 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
onKeyFilePathIfExistsResult: {
|
||||
keyFilePathIfExistsResultReceived = true;
|
||||
if (path === "" && root.activeView !== "notSetUp") {
|
||||
root.activeView = "notSetUp";
|
||||
} else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") {
|
||||
onWalletAuthenticatedStatusResult: {
|
||||
if (!isAuthenticated && !passphraseModal.visible) {
|
||||
passphraseModal.visible = true;
|
||||
} else if (isAuthenticated) {
|
||||
root.activeView = "checkoutMain";
|
||||
if (!balanceReceived) {
|
||||
commerce.balance();
|
||||
}
|
||||
if (!purchasesReceived) {
|
||||
commerce.inventory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,10 +127,6 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
HifiWallet.SecurityImageModel {
|
||||
id: securityImageModel;
|
||||
}
|
||||
|
||||
//
|
||||
// TITLE BAR START
|
||||
//
|
||||
|
@ -195,7 +208,6 @@ Rectangle {
|
|||
securityImageResultReceived = false;
|
||||
purchasesReceived = false;
|
||||
balanceReceived = false;
|
||||
keyFilePathIfExistsResultReceived = false;
|
||||
commerce.getLoginStatus();
|
||||
}
|
||||
}
|
||||
|
@ -221,7 +233,20 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
HifiWallet.PassphraseModal {
|
||||
id: passphraseModal;
|
||||
visible: false;
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
Connections {
|
||||
onSendSignalToParent: {
|
||||
sendToScript(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// "WALLET NOT SET UP" START
|
||||
|
|
|
@ -28,7 +28,6 @@ Rectangle {
|
|||
property string activeView: "initialize";
|
||||
property string referrerURL: "";
|
||||
property bool securityImageResultReceived: false;
|
||||
property bool keyFilePathIfExistsResultReceived: false;
|
||||
property bool purchasesReceived: false;
|
||||
property bool punctuationMode: false;
|
||||
// Style
|
||||
|
@ -36,14 +35,28 @@ Rectangle {
|
|||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onAccountResult: {
|
||||
if (result.status === "success") {
|
||||
commerce.getKeyFilePathIfExists();
|
||||
} else {
|
||||
// unsure how to handle a failure here. We definitely cannot proceed.
|
||||
}
|
||||
}
|
||||
|
||||
onLoginStatusResult: {
|
||||
if (!isLoggedIn && root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
} else if (isLoggedIn) {
|
||||
root.activeView = "initialize";
|
||||
commerce.account();
|
||||
}
|
||||
}
|
||||
|
||||
onKeyFilePathIfExistsResult: {
|
||||
if (path === "" && root.activeView !== "notSetUp") {
|
||||
root.activeView = "notSetUp";
|
||||
} else if (path !== "" && root.activeView === "initialize") {
|
||||
commerce.getSecurityImage();
|
||||
commerce.getKeyFilePathIfExists();
|
||||
commerce.inventory();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,8 +64,8 @@ Rectangle {
|
|||
securityImageResultReceived = true;
|
||||
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
|
||||
root.activeView = "notSetUp";
|
||||
} else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") {
|
||||
root.activeView = "purchasesMain";
|
||||
} else if (exists && root.activeView === "initialize") {
|
||||
commerce.getWalletAuthenticatedStatus();
|
||||
} else if (exists) {
|
||||
// just set the source again (to be sure the change was noticed)
|
||||
securityImage.source = "";
|
||||
|
@ -60,12 +73,12 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
onKeyFilePathIfExistsResult: {
|
||||
keyFilePathIfExistsResultReceived = true;
|
||||
if (path === "" && root.activeView !== "notSetUp") {
|
||||
root.activeView = "notSetUp";
|
||||
} else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") {
|
||||
onWalletAuthenticatedStatusResult: {
|
||||
if (!isAuthenticated && !passphraseModal.visible) {
|
||||
passphraseModal.visible = true;
|
||||
} else if (isAuthenticated) {
|
||||
root.activeView = "purchasesMain";
|
||||
commerce.inventory();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,11 +179,10 @@ Rectangle {
|
|||
Component.onCompleted: {
|
||||
securityImageResultReceived = false;
|
||||
purchasesReceived = false;
|
||||
keyFilePathIfExistsResultReceived = false;
|
||||
commerce.getLoginStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HifiWallet.NeedsLogIn {
|
||||
id: needsLogIn;
|
||||
visible: root.activeView === "needsLogIn";
|
||||
|
@ -191,7 +203,22 @@ Rectangle {
|
|||
commerce.getLoginStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HifiWallet.PassphraseModal {
|
||||
id: passphraseModal;
|
||||
visible: false;
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
Connections {
|
||||
onSendSignalToParent: {
|
||||
sendToScript(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// "WALLET NOT SET UP" START
|
||||
//
|
||||
|
@ -202,7 +229,7 @@ Rectangle {
|
|||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
|
||||
RalewayRegular {
|
||||
id: notSetUpText;
|
||||
text: "<b>Your wallet isn't set up.</b><br><br>Set up your Wallet (no credit card necessary) to claim your <b>free HFC</b> " +
|
||||
|
@ -233,7 +260,7 @@ Rectangle {
|
|||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 24;
|
||||
|
||||
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
id: cancelButton;
|
||||
|
@ -290,7 +317,7 @@ Rectangle {
|
|||
anchors.topMargin: 8;
|
||||
anchors.bottom: actionButtonsContainer.top;
|
||||
anchors.bottomMargin: 8;
|
||||
|
||||
|
||||
//
|
||||
// FILTER BAR START
|
||||
//
|
||||
|
@ -364,7 +391,7 @@ Rectangle {
|
|||
itemHref: root_file_url;
|
||||
anchors.topMargin: 12;
|
||||
anchors.bottomMargin: 12;
|
||||
|
||||
|
||||
Connections {
|
||||
onSendToPurchases: {
|
||||
if (msg.method === 'purchases_itemInfoClicked') {
|
||||
|
@ -383,7 +410,7 @@ Rectangle {
|
|||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: parent.width;
|
||||
|
||||
|
||||
// Explanitory text
|
||||
RalewayRegular {
|
||||
id: haventPurchasedYet;
|
||||
|
|
|
@ -47,11 +47,27 @@ Item {
|
|||
HifiControlsUit.Button {
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.bottom: helpText.bottom;
|
||||
anchors.bottom: resetButton.top;
|
||||
anchors.bottomMargin: 15;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
height: 50;
|
||||
width: 250;
|
||||
text: "Testing: Reset Wallet!";
|
||||
text: "DEBUG: Clear Cached Passphrase";
|
||||
onClicked: {
|
||||
commerce.setPassphrase("");
|
||||
sendSignalToWallet({method: 'passphraseReset'});
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: resetButton;
|
||||
color: hifi.buttons.red;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.bottom: helpText.bottom;
|
||||
anchors.bottomMargin: 15;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
height: 50;
|
||||
width: 250;
|
||||
text: "DEBUG: Reset Wallet!";
|
||||
onClicked: {
|
||||
commerce.reset();
|
||||
sendSignalToWallet({method: 'walletReset'});
|
||||
|
|
304
interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml
Normal file
304
interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml
Normal file
|
@ -0,0 +1,304 @@
|
|||
//
|
||||
// PassphraseModal.qml
|
||||
// qml/hifi/commerce/wallet
|
||||
//
|
||||
// PassphraseModal
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-31
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../../styles-uit"
|
||||
import "../../../controls-uit" as HifiControlsUit
|
||||
import "../../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
z: 998;
|
||||
property bool keyboardRaised: false;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onSecurityImageResult: {
|
||||
passphraseModalSecurityImage.source = "";
|
||||
passphraseModalSecurityImage.source = "image://security/securityImage";
|
||||
}
|
||||
|
||||
onWalletAuthenticatedStatusResult: {
|
||||
submitPassphraseInputButton.enabled = true;
|
||||
if (!isAuthenticated) {
|
||||
errorText.text = "Authentication failed - please try again.";
|
||||
} else {
|
||||
root.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This object is always used in a popup.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup.
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
propagateComposedEvents: false;
|
||||
}
|
||||
|
||||
// This will cause a bug -- if you bring up passphrase selection in HUD mode while
|
||||
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
|
||||
// HMD preview will stay off.
|
||||
// TODO: Fix this unlikely bug
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
passphraseField.focus = true;
|
||||
sendSignalToParent({method: 'disableHmdPreview'});
|
||||
} else {
|
||||
sendSignalToParent({method: 'maybeEnableHmdPreview'});
|
||||
}
|
||||
}
|
||||
|
||||
// Background rectangle
|
||||
Rectangle {
|
||||
anchors.fill: parent;
|
||||
color: "black";
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
height: 250;
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: instructionsText;
|
||||
text: "Enter Wallet Passphrase";
|
||||
size: 16;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 30;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
width: passphraseField.width;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignLeft;
|
||||
}
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: passphraseField;
|
||||
anchors.top: instructionsText.bottom;
|
||||
anchors.topMargin: 4;
|
||||
anchors.left: instructionsText.left;
|
||||
width: 280;
|
||||
height: 50;
|
||||
echoMode: TextInput.Password;
|
||||
placeholderText: "passphrase";
|
||||
|
||||
onFocusChanged: {
|
||||
root.keyboardRaised = focus;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
|
||||
onClicked: {
|
||||
parent.focus = true;
|
||||
root.keyboardRaised = true;
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
submitPassphraseInputButton.enabled = false;
|
||||
commerce.setPassphrase(passphraseField.text);
|
||||
}
|
||||
}
|
||||
|
||||
// Show passphrase text
|
||||
HifiControlsUit.CheckBox {
|
||||
id: showPassphrase;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.left: passphraseField.left;
|
||||
anchors.top: passphraseField.bottom;
|
||||
anchors.topMargin: 8;
|
||||
height: 30;
|
||||
text: "Show passphrase as plain text";
|
||||
boxSize: 24;
|
||||
onClicked: {
|
||||
passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
|
||||
}
|
||||
}
|
||||
|
||||
// Security Image
|
||||
Item {
|
||||
id: securityImageContainer;
|
||||
// Anchors
|
||||
anchors.top: instructionsText.top;
|
||||
anchors.left: passphraseField.right;
|
||||
anchors.leftMargin: 12;
|
||||
anchors.right: parent.right;
|
||||
Image {
|
||||
id: passphraseModalSecurityImage;
|
||||
anchors.top: parent.top;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
height: 75;
|
||||
width: height;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
source: "image://security/securityImage";
|
||||
cache: false;
|
||||
onVisibleChanged: {
|
||||
commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
Image {
|
||||
id: passphraseModalSecurityImageOverlay;
|
||||
source: "images/lockOverlay.png";
|
||||
width: passphraseModalSecurityImage.width * 0.45;
|
||||
height: passphraseModalSecurityImage.height * 0.45;
|
||||
anchors.bottom: passphraseModalSecurityImage.bottom;
|
||||
anchors.right: passphraseModalSecurityImage.right;
|
||||
mipmap: true;
|
||||
opacity: 0.9;
|
||||
}
|
||||
// "Security image" text below pic
|
||||
RalewayRegular {
|
||||
text: "security image";
|
||||
// Text size
|
||||
size: 12;
|
||||
// Anchors
|
||||
anchors.top: passphraseModalSecurityImage.bottom;
|
||||
anchors.topMargin: 4;
|
||||
anchors.left: securityImageContainer.left;
|
||||
anchors.right: securityImageContainer.right;
|
||||
height: paintedHeight;
|
||||
// Style
|
||||
color: hifi.colors.faintGray;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// Error text above buttons
|
||||
RalewaySemiBold {
|
||||
id: errorText;
|
||||
text: "";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.bottom: passphrasePopupActionButtonsContainer.top;
|
||||
anchors.bottomMargin: 4;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 30;
|
||||
// Style
|
||||
color: hifi.colors.redHighlight;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
//
|
||||
// ACTION BUTTONS START
|
||||
//
|
||||
Item {
|
||||
id: passphrasePopupActionButtonsContainer;
|
||||
// Size
|
||||
width: root.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 10;
|
||||
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
id: cancelPassphraseInputButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
height: 40;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: parent.width/2 - anchors.leftMargin*2;
|
||||
text: "Cancel"
|
||||
onClicked: {
|
||||
sendSignalToParent({method: 'passphrasePopup_cancelClicked'});
|
||||
}
|
||||
}
|
||||
|
||||
// "Submit" button
|
||||
HifiControlsUit.Button {
|
||||
id: submitPassphraseInputButton;
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
height: 40;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: parent.width/2 - anchors.rightMargin*2;
|
||||
text: "Submit"
|
||||
onClicked: {
|
||||
submitPassphraseInputButton.enabled = false;
|
||||
commerce.setPassphrase(passphraseField.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: keyboardContainer;
|
||||
z: 999;
|
||||
visible: keyboard.raised;
|
||||
property bool punctuationMode: false;
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
}
|
||||
|
||||
Image {
|
||||
id: lowerKeyboardButton;
|
||||
source: "images/lowerKeyboard.png";
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.bottom: keyboard.top;
|
||||
height: 30;
|
||||
width: 120;
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
|
||||
onClicked: {
|
||||
root.keyboardRaised = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.Keyboard {
|
||||
id: keyboard;
|
||||
raised: HMD.mounted && root.keyboardRaised;
|
||||
numeric: parent.punctuationMode;
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal sendSignalToParent(var msg);
|
||||
}
|
|
@ -40,8 +40,8 @@ Item {
|
|||
passphrasePageSecurityImage.source = "image://security/securityImage";
|
||||
}
|
||||
|
||||
onPassphraseSetupStatusResult: {
|
||||
sendMessageToLightbox({method: 'statusResult', status: passphraseIsSetup});
|
||||
onWalletAuthenticatedStatusResult: {
|
||||
sendMessageToLightbox({method: 'statusResult', status: isAuthenticated});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,10 +58,6 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
SecurityImageModel {
|
||||
id: gridModel;
|
||||
}
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: passphraseField;
|
||||
anchors.top: parent.top;
|
||||
|
@ -199,7 +195,7 @@ Item {
|
|||
// Text below TextFields
|
||||
RalewaySemiBold {
|
||||
id: passwordReqs;
|
||||
text: "Passphrase must be at least 4 characters";
|
||||
text: "Passphrase must be at least 3 characters";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
|
@ -256,7 +252,7 @@ Item {
|
|||
}
|
||||
|
||||
function validateAndSubmitPassphrase() {
|
||||
if (passphraseField.text.length < 4) {
|
||||
if (passphraseField.text.length < 3) {
|
||||
setErrorText("Passphrase too short.");
|
||||
return false;
|
||||
} else if (passphraseField.text !== passphraseFieldAgain.text) {
|
||||
|
|
|
@ -26,8 +26,6 @@ Rectangle {
|
|||
id: root;
|
||||
|
||||
property string activeView: "initialize";
|
||||
property bool securityImageResultReceived: false;
|
||||
property bool keyFilePathIfExistsResultReceived: false;
|
||||
property bool keyboardRaised: false;
|
||||
|
||||
// Style
|
||||
|
@ -35,30 +33,42 @@ Rectangle {
|
|||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
|
||||
onAccountResult: {
|
||||
if (result.status === "success") {
|
||||
commerce.getKeyFilePathIfExists();
|
||||
} else {
|
||||
// unsure how to handle a failure here. We definitely cannot proceed.
|
||||
}
|
||||
}
|
||||
onLoginStatusResult: {
|
||||
if (!isLoggedIn && root.activeView !== "needsLogIn") {
|
||||
root.activeView = "needsLogIn";
|
||||
} else if (isLoggedIn) {
|
||||
root.activeView = "initialize";
|
||||
commerce.getSecurityImage();
|
||||
commerce.getKeyFilePathIfExists();
|
||||
}
|
||||
}
|
||||
|
||||
onSecurityImageResult: {
|
||||
securityImageResultReceived = true;
|
||||
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
|
||||
root.activeView = "notSetUp";
|
||||
} else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") {
|
||||
root.activeView = "walletHome";
|
||||
commerce.account();
|
||||
}
|
||||
}
|
||||
|
||||
onKeyFilePathIfExistsResult: {
|
||||
keyFilePathIfExistsResultReceived = true;
|
||||
if (path === "" && root.activeView !== "notSetUp") {
|
||||
root.activeView = "notSetUp";
|
||||
} else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") {
|
||||
} else if (path !== "" && root.activeView === "initialize") {
|
||||
commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
|
||||
onSecurityImageResult: {
|
||||
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
|
||||
root.activeView = "notSetUp";
|
||||
} else if (exists && root.activeView === "initialize") {
|
||||
commerce.getWalletAuthenticatedStatus();
|
||||
}
|
||||
}
|
||||
|
||||
onWalletAuthenticatedStatusResult: {
|
||||
if (!isAuthenticated && passphraseModal && !passphraseModal.visible) {
|
||||
passphraseModal.visible = true;
|
||||
} else if (isAuthenticated) {
|
||||
root.activeView = "walletHome";
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +99,8 @@ Rectangle {
|
|||
if (msg.method === 'walletSetup_cancelClicked') {
|
||||
walletSetupLightbox.visible = false;
|
||||
} else if (msg.method === 'walletSetup_finished') {
|
||||
root.activeView = "walletHome";
|
||||
root.activeView = "initialize";
|
||||
commerce.getLoginStatus();
|
||||
} else if (msg.method === 'walletSetup_raiseKeyboard') {
|
||||
root.keyboardRaised = true;
|
||||
} else if (msg.method === 'walletSetup_lowerKeyboard') {
|
||||
|
@ -218,6 +229,21 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
PassphraseModal {
|
||||
id: passphraseModal;
|
||||
visible: false;
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
Connections {
|
||||
onSendSignalToParent: {
|
||||
sendToScript(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotSetUp {
|
||||
id: notSetUp;
|
||||
visible: root.activeView === "notSetUp";
|
||||
|
@ -299,7 +325,7 @@ Rectangle {
|
|||
|
||||
Connections {
|
||||
onSendSignalToWallet: {
|
||||
if (msg.method === 'walletReset') {
|
||||
if (msg.method === 'walletReset' || msg.method === 'passphraseReset') {
|
||||
sendToScript(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,13 +88,14 @@ Item {
|
|||
height: 60;
|
||||
Rectangle {
|
||||
id: hfcBalanceField;
|
||||
color: hifi.colors.darkGray;
|
||||
anchors.right: parent.right;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
height: parent.height - 15;
|
||||
|
||||
// "HFC" balance label
|
||||
RalewayRegular {
|
||||
FiraSansRegular {
|
||||
id: balanceLabel;
|
||||
text: "HFC";
|
||||
// Text size
|
||||
|
@ -106,7 +107,7 @@ Item {
|
|||
anchors.rightMargin: 4;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignRight;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
|
@ -121,7 +122,7 @@ Item {
|
|||
}
|
||||
|
||||
// Balance Text
|
||||
FiraSansRegular {
|
||||
FiraSansSemiBold {
|
||||
id: balanceText;
|
||||
text: "--";
|
||||
// Text size
|
||||
|
@ -133,7 +134,7 @@ Item {
|
|||
anchors.right: balanceLabel.left;
|
||||
anchors.rightMargin: 4;
|
||||
// Style
|
||||
color: hifi.colors.darkGray;
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignRight;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
|
@ -258,7 +259,7 @@ Item {
|
|||
delegate: Item {
|
||||
width: parent.width;
|
||||
height: transactionText.height + 30;
|
||||
RalewayRegular {
|
||||
FiraSansRegular {
|
||||
id: transactionText;
|
||||
text: model.text;
|
||||
// Style
|
||||
|
@ -288,7 +289,7 @@ Item {
|
|||
}
|
||||
|
||||
// This should never be visible (since you immediately get 100 HFC)
|
||||
RalewayRegular {
|
||||
FiraSansRegular {
|
||||
id: emptyTransationHistory;
|
||||
size: 24;
|
||||
visible: !transactionHistory.visible && root.historyReceived;
|
||||
|
|
|
@ -39,9 +39,9 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
onPassphraseSetupStatusResult: {
|
||||
onWalletAuthenticatedStatusResult: {
|
||||
securityImageContainer.visible = false;
|
||||
if (passphraseIsSetup) {
|
||||
if (isAuthenticated) {
|
||||
privateKeysReadyContainer.visible = true;
|
||||
} else {
|
||||
choosePassphraseContainer.visible = true;
|
||||
|
@ -117,7 +117,7 @@ Rectangle {
|
|||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
height: 280;
|
||||
|
||||
|
||||
Connections {
|
||||
onSendSignalToWallet: {
|
||||
sendSignalToWallet(msg);
|
||||
|
@ -210,7 +210,7 @@ Rectangle {
|
|||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
commerce.getPassphraseSetupStatus();
|
||||
commerce.getWalletAuthenticatedStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,7 +325,7 @@ Rectangle {
|
|||
onClicked: {
|
||||
if (passphraseSelection.validateAndSubmitPassphrase()) {
|
||||
root.lastPage = "choosePassphrase";
|
||||
commerce.balance(); // Do this here so that keys are generated. Order might change as backend changes?
|
||||
commerce.generateKeyPair();
|
||||
choosePassphraseContainer.visible = false;
|
||||
privateKeysReadyContainer.visible = true;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import "../../styles-uit"
|
||||
import "../audio" as HifiAudio
|
||||
|
@ -109,15 +110,45 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: usernameText
|
||||
text: tabletRoot.username
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Item {
|
||||
width: 150
|
||||
height: 50
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 20
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: 20
|
||||
color: "#afafaf"
|
||||
anchors.rightMargin: 30
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
RalewaySemiBold {
|
||||
text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in")
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.right: parent.right
|
||||
font.pixelSize: 20
|
||||
color: "#afafaf"
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
visible: Account.loggedIn
|
||||
height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0
|
||||
text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : ""
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.right: parent.right
|
||||
font.pixelSize: 20
|
||||
color: "#afafaf"
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (!Account.loggedIn) {
|
||||
DialogsManager.showLoginDialog()
|
||||
} else {
|
||||
Account.logOut()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ import QtGraphicalEffects 1.0
|
|||
|
||||
Item {
|
||||
id: tabletButton
|
||||
|
||||
property string captionColorOverride: ""
|
||||
property var uuid;
|
||||
property string icon: "icons/tablet-icons/edit-i.svg"
|
||||
property string hoverIcon: tabletButton.icon
|
||||
|
@ -102,7 +104,7 @@ Item {
|
|||
|
||||
Text {
|
||||
id: text
|
||||
color: "#ffffff"
|
||||
color: captionColorOverride !== "" ? captionColorOverride: "#ffffff"
|
||||
text: tabletButton.text
|
||||
font.bold: true
|
||||
font.pixelSize: 18
|
||||
|
|
|
@ -8,6 +8,7 @@ Item {
|
|||
id: tabletRoot
|
||||
objectName: "tabletRoot"
|
||||
property string username: "Unknown user"
|
||||
property string usernameShort: "Unknown user"
|
||||
property var rootMenu;
|
||||
property var openModal: null;
|
||||
property var openMessage: null;
|
||||
|
@ -157,6 +158,11 @@ Item {
|
|||
|
||||
function setUsername(newUsername) {
|
||||
username = newUsername;
|
||||
usernameShort = newUsername.substring(0, 8);
|
||||
|
||||
if (newUsername.length > 8) {
|
||||
usernameShort = usernameShort + "..."
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
|
|
|
@ -604,6 +604,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>();
|
||||
|
||||
// Set dependencies
|
||||
DependencyManager::set<Cursor::Manager>();
|
||||
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
|
||||
DependencyManager::set<StatTracker>();
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
|
||||
|
@ -2802,35 +2803,18 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
|
||||
// Get controller availability
|
||||
bool hasHandControllers = false;
|
||||
HandControllerType handControllerType = Vive;
|
||||
if (PluginUtils::isViveControllerAvailable()) {
|
||||
if (PluginUtils::isViveControllerAvailable() || PluginUtils::isOculusTouchControllerAvailable()) {
|
||||
hasHandControllers = true;
|
||||
handControllerType = Vive;
|
||||
} else if (PluginUtils::isOculusTouchControllerAvailable()) {
|
||||
hasHandControllers = true;
|
||||
handControllerType = Oculus;
|
||||
}
|
||||
|
||||
// Check tutorial content versioning
|
||||
bool hasTutorialContent = contentVersion >= MIN_CONTENT_VERSION.at(handControllerType);
|
||||
|
||||
// Check HMD use (may be technically available without being in use)
|
||||
bool hasHMD = PluginUtils::isHMDAvailable();
|
||||
bool isUsingHMD = _displayPlugin->isHmd();
|
||||
bool isUsingHMDAndHandControllers = hasHMD && hasHandControllers && isUsingHMD;
|
||||
|
||||
Setting::Handle<bool> tutorialComplete{ "tutorialComplete", false };
|
||||
Setting::Handle<bool> firstRun{ Settings::firstRun, true };
|
||||
|
||||
const QString HIFI_SKIP_TUTORIAL_COMMAND_LINE_KEY = "--skipTutorial";
|
||||
// Skips tutorial/help behavior, and does NOT clear firstRun setting.
|
||||
bool skipTutorial = arguments().contains(HIFI_SKIP_TUTORIAL_COMMAND_LINE_KEY);
|
||||
bool isTutorialComplete = tutorialComplete.get();
|
||||
bool shouldGoToTutorial = isUsingHMDAndHandControllers && hasTutorialContent && !isTutorialComplete && !skipTutorial;
|
||||
|
||||
qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers;
|
||||
qCDebug(interfaceapp) << "Tutorial version:" << contentVersion << ", sufficient:" << hasTutorialContent <<
|
||||
", complete:" << isTutorialComplete << ", should go:" << shouldGoToTutorial;
|
||||
|
||||
// when --url in command line, teleport to location
|
||||
const QString HIFI_URL_COMMAND_LINE_KEY = "--url";
|
||||
|
@ -2840,58 +2824,31 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
addressLookupString = arguments().value(urlIndex + 1);
|
||||
}
|
||||
|
||||
const QString TUTORIAL_PATH = "/tutorial_begin";
|
||||
|
||||
static const QString SENT_TO_TUTORIAL = "tutorial";
|
||||
static const QString SENT_TO_PREVIOUS_LOCATION = "previous_location";
|
||||
static const QString SENT_TO_ENTRY = "entry";
|
||||
static const QString SENT_TO_SANDBOX = "sandbox";
|
||||
|
||||
QString sentTo;
|
||||
|
||||
if (shouldGoToTutorial) {
|
||||
if (sandboxIsRunning) {
|
||||
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
|
||||
DependencyManager::get<AddressManager>()->goToLocalSandbox(TUTORIAL_PATH);
|
||||
sentTo = SENT_TO_TUTORIAL;
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
|
||||
if (firstRun.get()) {
|
||||
showHelp();
|
||||
}
|
||||
if (addressLookupString.isEmpty()) {
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
sentTo = SENT_TO_ENTRY;
|
||||
} else {
|
||||
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
|
||||
sentTo = SENT_TO_PREVIOUS_LOCATION;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// If this is a first run we short-circuit the address passed in
|
||||
if (firstRun.get() && !skipTutorial) {
|
||||
if (firstRun.get()) {
|
||||
showHelp();
|
||||
if (isUsingHMDAndHandControllers) {
|
||||
if (sandboxIsRunning) {
|
||||
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
|
||||
DependencyManager::get<AddressManager>()->goToLocalSandbox();
|
||||
sentTo = SENT_TO_SANDBOX;
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
sentTo = SENT_TO_ENTRY;
|
||||
}
|
||||
if (sandboxIsRunning) {
|
||||
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
|
||||
DependencyManager::get<AddressManager>()->goToLocalSandbox();
|
||||
sentTo = SENT_TO_SANDBOX;
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
sentTo = SENT_TO_ENTRY;
|
||||
}
|
||||
firstRun.set(false);
|
||||
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
|
||||
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
|
||||
sentTo = SENT_TO_PREVIOUS_LOCATION;
|
||||
}
|
||||
}
|
||||
|
||||
UserActivityLogger::getInstance().logAction("startup_sent_to", {
|
||||
{ "sent_to", sentTo },
|
||||
|
@ -2900,18 +2857,10 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
{ "has_hand_controllers", hasHandControllers },
|
||||
{ "is_using_hmd", isUsingHMD },
|
||||
{ "is_using_hmd_and_hand_controllers", isUsingHMDAndHandControllers },
|
||||
{ "content_version", contentVersion },
|
||||
{ "is_tutorial_complete", isTutorialComplete },
|
||||
{ "has_tutorial_content", hasTutorialContent },
|
||||
{ "should_go_to_tutorial", shouldGoToTutorial }
|
||||
{ "content_version", contentVersion }
|
||||
});
|
||||
|
||||
_connectionMonitor.init();
|
||||
|
||||
// After all of the constructor is completed, then set firstRun to false.
|
||||
if (!skipTutorial) {
|
||||
firstRun.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::importJSONFromURL(const QString& urlString) {
|
||||
|
@ -6320,11 +6269,11 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
|
|||
|
||||
bool Application::askToReplaceDomainContent(const QString& url) {
|
||||
QString methodDetails;
|
||||
const int MAX_CHARACTERS_PER_LINE = 90;
|
||||
if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) {
|
||||
QUrl originURL { url };
|
||||
if (originURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) {
|
||||
// Create a confirmation dialog when this call is made
|
||||
const int MAX_CHARACTERS_PER_LINE = 90;
|
||||
static const QString infoText = simpleWordWrap("Your domain's content will be replaced with a new content set. "
|
||||
"If you want to save what you have now, create a backup before proceeding. For more information about backing up "
|
||||
"and restoring content, visit the documentation page at: ", MAX_CHARACTERS_PER_LINE) +
|
||||
|
@ -6360,7 +6309,9 @@ bool Application::askToReplaceDomainContent(const QString& url) {
|
|||
}
|
||||
} else {
|
||||
methodDetails = "UserDoesNotHavePermissionToReplaceContent";
|
||||
OffscreenUi::warning("Unable to replace content", "You do not have permissions to replace domain content",
|
||||
static const QString warningMessage = simpleWordWrap("The domain owner must enable 'Replace Content' "
|
||||
"permissions for you in this domain's server settings before you can continue.", MAX_CHARACTERS_PER_LINE);
|
||||
OffscreenUi::warning("You do not have permissions to replace domain content", warningMessage,
|
||||
QMessageBox::Ok, QMessageBox::Ok);
|
||||
}
|
||||
QJsonObject messageProperties = {
|
||||
|
@ -7438,7 +7389,10 @@ void Application::updateDisplayMode() {
|
|||
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
|
||||
_displayPlugin = newDisplayPlugin;
|
||||
connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent);
|
||||
offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked);
|
||||
auto desktop = offscreenUi->getDesktop();
|
||||
if (desktop) {
|
||||
desktop->setProperty("repositionLocked", wasRepositionLocked);
|
||||
}
|
||||
}
|
||||
|
||||
bool isHmd = _displayPlugin->isHmd();
|
||||
|
|
|
@ -1912,6 +1912,17 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
|
|||
const bool shouldDrawHead = shouldRenderHead(renderArgs);
|
||||
if (shouldDrawHead != _prevShouldDrawHead) {
|
||||
_skeletonModel->setEnableCauterization(!shouldDrawHead);
|
||||
|
||||
for (int i = 0; i < _attachmentData.size(); i++) {
|
||||
if (_attachmentData[i].jointName.compare("Head", Qt::CaseInsensitive) == 0 ||
|
||||
_attachmentData[i].jointName.compare("Neck", Qt::CaseInsensitive) == 0 ||
|
||||
_attachmentData[i].jointName.compare("LeftEye", Qt::CaseInsensitive) == 0 ||
|
||||
_attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 ||
|
||||
_attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 ||
|
||||
_attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) {
|
||||
_attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene());
|
||||
}
|
||||
}
|
||||
}
|
||||
_prevShouldDrawHead = shouldDrawHead;
|
||||
}
|
||||
|
|
|
@ -176,3 +176,28 @@ void Ledger::resetFailure(QNetworkReply& reply) { failResponse("reset", reply);
|
|||
void Ledger::reset() {
|
||||
send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, QJsonObject());
|
||||
}
|
||||
|
||||
void Ledger::accountSuccess(QNetworkReply& reply) {
|
||||
// lets set the appropriate stuff in the wallet now
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
QByteArray response = reply.readAll();
|
||||
QJsonObject data = QJsonDocument::fromJson(response).object()["data"].toObject();
|
||||
|
||||
auto salt = QByteArray::fromBase64(data["salt"].toString().toUtf8());
|
||||
auto iv = QByteArray::fromBase64(data["iv"].toString().toUtf8());
|
||||
auto ckey = QByteArray::fromBase64(data["ckey"].toString().toUtf8());
|
||||
|
||||
wallet->setSalt(salt);
|
||||
wallet->setIv(iv);
|
||||
wallet->setCKey(ckey);
|
||||
|
||||
// none of the hfc account info should be emitted
|
||||
emit accountResult(QJsonObject{ {"status", "success"} });
|
||||
}
|
||||
|
||||
void Ledger::accountFailure(QNetworkReply& reply) {
|
||||
failResponse("account", reply);
|
||||
}
|
||||
void Ledger::account() {
|
||||
send("hfc_account", "accountSuccess", "accountFailure", QNetworkAccessManager::PutOperation, QJsonObject());
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ public:
|
|||
void balance(const QStringList& keys);
|
||||
void inventory(const QStringList& keys);
|
||||
void history(const QStringList& keys);
|
||||
void account();
|
||||
void reset();
|
||||
|
||||
signals:
|
||||
|
@ -37,6 +38,7 @@ signals:
|
|||
void balanceResult(QJsonObject result);
|
||||
void inventoryResult(QJsonObject result);
|
||||
void historyResult(QJsonObject result);
|
||||
void accountResult(QJsonObject result);
|
||||
|
||||
public slots:
|
||||
void buySuccess(QNetworkReply& reply);
|
||||
|
@ -51,6 +53,8 @@ public slots:
|
|||
void historyFailure(QNetworkReply& reply);
|
||||
void resetSuccess(QNetworkReply& reply);
|
||||
void resetFailure(QNetworkReply& reply);
|
||||
void accountSuccess(QNetworkReply& reply);
|
||||
void accountFailure(QNetworkReply& reply);
|
||||
|
||||
private:
|
||||
QJsonObject apiResponse(const QString& label, QNetworkReply& reply);
|
||||
|
|
|
@ -27,6 +27,31 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
|
|||
connect(wallet.data(), &Wallet::securityImageResult, this, &QmlCommerce::securityImageResult);
|
||||
connect(ledger.data(), &Ledger::historyResult, this, &QmlCommerce::historyResult);
|
||||
connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult);
|
||||
connect(ledger.data(), &Ledger::accountResult, this, &QmlCommerce::accountResult);
|
||||
}
|
||||
|
||||
void QmlCommerce::getLoginStatus() {
|
||||
emit loginStatusResult(DependencyManager::get<AccountManager>()->isLoggedIn());
|
||||
}
|
||||
|
||||
void QmlCommerce::getKeyFilePathIfExists() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->sendKeyFilePathIfExists();
|
||||
}
|
||||
|
||||
void QmlCommerce::getWalletAuthenticatedStatus() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
emit walletAuthenticatedStatusResult(wallet->walletIsAuthenticatedWithPassphrase());
|
||||
}
|
||||
|
||||
void QmlCommerce::getSecurityImage() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->getSecurityImage();
|
||||
}
|
||||
|
||||
void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->chooseSecurityImage(imageFile);
|
||||
}
|
||||
|
||||
void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) {
|
||||
|
@ -60,30 +85,20 @@ void QmlCommerce::history() {
|
|||
ledger->history(wallet->listPublicKeys());
|
||||
}
|
||||
|
||||
void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->chooseSecurityImage(imageFile);
|
||||
}
|
||||
|
||||
void QmlCommerce::getSecurityImage() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->getSecurityImage();
|
||||
}
|
||||
|
||||
void QmlCommerce::getLoginStatus() {
|
||||
emit loginStatusResult(DependencyManager::get<AccountManager>()->isLoggedIn());
|
||||
}
|
||||
|
||||
void QmlCommerce::setPassphrase(const QString& passphrase) {
|
||||
emit passphraseSetupStatusResult(true);
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
if(wallet->getPassphrase() && !wallet->getPassphrase()->isEmpty() && !passphrase.isEmpty()) {
|
||||
wallet->changePassphrase(passphrase);
|
||||
} else {
|
||||
wallet->setPassphrase(passphrase);
|
||||
}
|
||||
getWalletAuthenticatedStatus();
|
||||
}
|
||||
|
||||
void QmlCommerce::getPassphraseSetupStatus() {
|
||||
emit passphraseSetupStatusResult(false);
|
||||
}
|
||||
void QmlCommerce::getKeyFilePathIfExists() {
|
||||
void QmlCommerce::generateKeyPair() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->sendKeyFilePathIfExists();
|
||||
wallet->generateKeyPair();
|
||||
getWalletAuthenticatedStatus();
|
||||
}
|
||||
|
||||
void QmlCommerce::reset() {
|
||||
|
@ -91,4 +106,9 @@ void QmlCommerce::reset() {
|
|||
auto wallet = DependencyManager::get<Wallet>();
|
||||
ledger->reset();
|
||||
wallet->reset();
|
||||
}
|
||||
}
|
||||
|
||||
void QmlCommerce::account() {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
ledger->account();
|
||||
}
|
||||
|
|
|
@ -28,29 +28,35 @@ public:
|
|||
QmlCommerce(QQuickItem* parent = nullptr);
|
||||
|
||||
signals:
|
||||
void loginStatusResult(bool isLoggedIn);
|
||||
void keyFilePathIfExistsResult(const QString& path);
|
||||
void securityImageResult(bool exists);
|
||||
void walletAuthenticatedStatusResult(bool isAuthenticated);
|
||||
|
||||
void buyResult(QJsonObject result);
|
||||
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
|
||||
// because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain).
|
||||
void balanceResult(QJsonObject result);
|
||||
void inventoryResult(QJsonObject result);
|
||||
void securityImageResult(bool exists);
|
||||
void loginStatusResult(bool isLoggedIn);
|
||||
void passphraseSetupStatusResult(bool passphraseIsSetup);
|
||||
void historyResult(QJsonObject result);
|
||||
void keyFilePathIfExistsResult(const QString& path);
|
||||
void accountResult(QJsonObject result);
|
||||
|
||||
protected:
|
||||
Q_INVOKABLE void getLoginStatus();
|
||||
Q_INVOKABLE void getKeyFilePathIfExists();
|
||||
Q_INVOKABLE void getSecurityImage();
|
||||
Q_INVOKABLE void getWalletAuthenticatedStatus();
|
||||
|
||||
Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
|
||||
Q_INVOKABLE void setPassphrase(const QString& passphrase);
|
||||
|
||||
Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = "");
|
||||
Q_INVOKABLE void balance();
|
||||
Q_INVOKABLE void inventory();
|
||||
Q_INVOKABLE void history();
|
||||
Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
|
||||
Q_INVOKABLE void getSecurityImage();
|
||||
Q_INVOKABLE void getLoginStatus();
|
||||
Q_INVOKABLE void setPassphrase(const QString& passphrase);
|
||||
Q_INVOKABLE void getPassphraseSetupStatus();
|
||||
Q_INVOKABLE void getKeyFilePathIfExists();
|
||||
Q_INVOKABLE void generateKeyPair();
|
||||
Q_INVOKABLE void reset();
|
||||
Q_INVOKABLE void account();
|
||||
};
|
||||
|
||||
#endif // hifi_QmlCommerce_h
|
||||
|
|
|
@ -31,6 +31,13 @@
|
|||
#include <openssl/evp.h>
|
||||
#include <openssl/aes.h>
|
||||
|
||||
// I know, right? But per https://www.openssl.org/docs/faq.html
|
||||
// this avoids OPENSSL_Uplink(00007FF847238000,08): no OPENSSL_Applink
|
||||
// at runtime.
|
||||
#ifdef Q_OS_WIN
|
||||
#include <openssl/applink.c>
|
||||
#endif
|
||||
|
||||
static const char* KEY_FILE = "hifikey";
|
||||
static const char* IMAGE_FILE = "hifi_image"; // eventually this will live in keyfile
|
||||
|
||||
|
@ -55,17 +62,76 @@ QString imageFilePath() {
|
|||
// use the cached _passphrase if it exists, otherwise we need to prompt
|
||||
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
|
||||
// just return a hardcoded pwd for now
|
||||
auto passphrase = DependencyManager::get<Wallet>()->getPassphrase();
|
||||
if (passphrase) {
|
||||
strcpy(password, passphrase->toLocal8Bit().constData());
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
auto passphrase = wallet->getPassphrase();
|
||||
if (passphrase && !passphrase->isEmpty()) {
|
||||
QString saltedPassphrase(*passphrase);
|
||||
saltedPassphrase.append(wallet->getSalt());
|
||||
strcpy(password, saltedPassphrase.toUtf8().constData());
|
||||
return static_cast<int>(passphrase->size());
|
||||
} else {
|
||||
// ok gotta bring up modal dialog... But right now lets just
|
||||
// just keep it empty
|
||||
// this shouldn't happen - so lets log it to tell us we have
|
||||
// a problem with the flow...
|
||||
qCCritical(commerce) << "no cached passphrase while decrypting!";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
RSA* readKeys(const char* filename) {
|
||||
FILE* fp;
|
||||
RSA* key = NULL;
|
||||
if ((fp = fopen(filename, "rt"))) {
|
||||
// file opened successfully
|
||||
qCDebug(commerce) << "opened key file" << filename;
|
||||
if ((key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL))) {
|
||||
// now read private key
|
||||
|
||||
qCDebug(commerce) << "read public key";
|
||||
|
||||
if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) {
|
||||
qCDebug(commerce) << "read private key";
|
||||
fclose(fp);
|
||||
return key;
|
||||
}
|
||||
qCDebug(commerce) << "failed to read private key";
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to read public key";
|
||||
}
|
||||
fclose(fp);
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to open key file" << filename;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
bool writeKeys(const char* filename, RSA* keys) {
|
||||
FILE* fp;
|
||||
bool retval = false;
|
||||
if ((fp = fopen(filename, "wt"))) {
|
||||
if (!PEM_write_RSAPublicKey(fp, keys)) {
|
||||
fclose(fp);
|
||||
qCDebug(commerce) << "failed to write public key";
|
||||
QFile(QString(filename)).remove();
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!PEM_write_RSAPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
|
||||
fclose(fp);
|
||||
qCDebug(commerce) << "failed to write private key";
|
||||
QFile(QString(filename)).remove();
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = true;
|
||||
qCDebug(commerce) << "wrote keys successfully";
|
||||
fclose(fp);
|
||||
} else {
|
||||
qCDebug(commerce) << "failed to open key file" << filename;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
// BEGIN copied code - this will be removed/changed at some point soon
|
||||
// copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp.
|
||||
// We will have a different implementation in practice, but this gives us a start for now
|
||||
|
@ -124,25 +190,9 @@ QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
|
|||
}
|
||||
|
||||
|
||||
|
||||
// now lets persist them to files
|
||||
// FIXME: for now I'm appending to the file if it exists. As long as we always put
|
||||
// the keys in the same order, this works fine. TODO: verify this will skip over
|
||||
// anything else (like an embedded image)
|
||||
FILE* fp;
|
||||
if ((fp = fopen(keyFilePath().toStdString().c_str(), "at"))) {
|
||||
if (!PEM_write_RSAPublicKey(fp, keyPair)) {
|
||||
fclose(fp);
|
||||
qCDebug(commerce) << "failed to write public key";
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!PEM_write_RSAPrivateKey(fp, keyPair, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
|
||||
fclose(fp);
|
||||
qCDebug(commerce) << "failed to write private key";
|
||||
return retval;
|
||||
}
|
||||
fclose(fp);
|
||||
if (!writeKeys(keyFilePath().toStdString().c_str(), keyPair)) {
|
||||
qCDebug(commerce) << "couldn't save keys!";
|
||||
return retval;
|
||||
}
|
||||
|
||||
RSA_free(keyPair);
|
||||
|
@ -201,13 +251,12 @@ RSA* readPrivateKey(const char* filename) {
|
|||
// file opened successfully
|
||||
qCDebug(commerce) << "opened key file" << filename;
|
||||
if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) {
|
||||
// cleanup
|
||||
fclose(fp);
|
||||
|
||||
qCDebug(commerce) << "parsed private key file successfully";
|
||||
|
||||
} else {
|
||||
qCDebug(commerce) << "couldn't parse" << filename;
|
||||
// if the passphrase is wrong, then let's not cache it
|
||||
DependencyManager::get<Wallet>()->setPassphrase("");
|
||||
}
|
||||
fclose(fp);
|
||||
} else {
|
||||
|
@ -216,13 +265,11 @@ RSA* readPrivateKey(const char* filename) {
|
|||
return key;
|
||||
}
|
||||
|
||||
static const unsigned char IVEC[16] = "IAmAnIVecYay123";
|
||||
|
||||
void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) {
|
||||
// first ivec
|
||||
memcpy(ivec, IVEC, 16);
|
||||
auto hash = QCryptographicHash::hash(salt, QCryptographicHash::Sha256);
|
||||
memcpy(ckey, hash.data(), 32);
|
||||
// use the ones in the wallet
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
memcpy(ivec, wallet->getIv(), 16);
|
||||
memcpy(ckey, wallet->getCKey(), 32);
|
||||
}
|
||||
|
||||
Wallet::~Wallet() {
|
||||
|
@ -236,6 +283,8 @@ void Wallet::setPassphrase(const QString& passphrase) {
|
|||
delete _passphrase;
|
||||
}
|
||||
_passphrase = new QString(passphrase);
|
||||
|
||||
_publicKeys.clear();
|
||||
}
|
||||
|
||||
// encrypt some stuff
|
||||
|
@ -341,28 +390,42 @@ bool Wallet::decryptFile(const QString& inputFilePath, unsigned char** outputBuf
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Wallet::createIfNeeded() {
|
||||
if (_publicKeys.count() > 0) return false;
|
||||
bool Wallet::walletIsAuthenticatedWithPassphrase() {
|
||||
// try to read existing keys if they exist...
|
||||
|
||||
// FIXME: initialize OpenSSL elsewhere soon
|
||||
initialize();
|
||||
|
||||
// try to read existing keys if they exist...
|
||||
// this should always be false if we don't have a passphrase
|
||||
// cached yet
|
||||
if (!_passphrase || _passphrase->isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (_publicKeys.count() > 0) {
|
||||
// we _must_ be authenticated if the publicKeys are there
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise, we have a passphrase but no keys, so we have to check
|
||||
auto publicKey = readPublicKey(keyFilePath().toStdString().c_str());
|
||||
|
||||
if (publicKey.size() > 0) {
|
||||
if (auto key = readPrivateKey(keyFilePath().toStdString().c_str()) ) {
|
||||
qCDebug(commerce) << "read private key";
|
||||
if (auto key = readPrivateKey(keyFilePath().toStdString().c_str())) {
|
||||
RSA_free(key);
|
||||
// K -- add the public key since we have a legit private key associated with it
|
||||
|
||||
// be sure to add the public key so we don't do this over and over
|
||||
_publicKeys.push_back(publicKey.toBase64());
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
qCInfo(commerce) << "Creating wallet.";
|
||||
return generateKeyPair();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Wallet::generateKeyPair() {
|
||||
// FIXME: initialize OpenSSL elsewhere soon
|
||||
initialize();
|
||||
|
||||
qCInfo(commerce) << "Generating keypair.";
|
||||
auto keyPair = generateRSAKeypair();
|
||||
sendKeyFilePathIfExists();
|
||||
|
@ -381,7 +444,6 @@ bool Wallet::generateKeyPair() {
|
|||
|
||||
QStringList Wallet::listPublicKeys() {
|
||||
qCInfo(commerce) << "Enumerating public keys.";
|
||||
createIfNeeded();
|
||||
return _publicKeys;
|
||||
}
|
||||
|
||||
|
@ -500,15 +562,39 @@ void Wallet::reset() {
|
|||
|
||||
// tell the provider we got nothing
|
||||
updateImageProvider();
|
||||
delete _passphrase;
|
||||
_passphrase->clear();
|
||||
|
||||
// for now we need to maintain the hard-coded passphrase.
|
||||
// FIXME: remove this line as part of wiring up the passphrase
|
||||
// and probably set it to nullptr
|
||||
_passphrase = new QString("pwd");
|
||||
|
||||
QFile keyFile(keyFilePath());
|
||||
QFile imageFile(imageFilePath());
|
||||
keyFile.remove();
|
||||
imageFile.remove();
|
||||
}
|
||||
|
||||
bool Wallet::changePassphrase(const QString& newPassphrase) {
|
||||
qCDebug(commerce) << "changing passphrase";
|
||||
RSA* keys = readKeys(keyFilePath().toStdString().c_str());
|
||||
if (keys) {
|
||||
// we read successfully, so now write to a new temp file
|
||||
// save old passphrase just in case
|
||||
// TODO: force re-enter?
|
||||
QString oldPassphrase = *_passphrase;
|
||||
setPassphrase(newPassphrase);
|
||||
QString tempFileName = QString("%1.%2").arg(keyFilePath(), QString("temp"));
|
||||
if (writeKeys(tempFileName.toStdString().c_str(), keys)) {
|
||||
// ok, now move the temp file to the correct spot
|
||||
QFile(QString(keyFilePath())).remove();
|
||||
QFile(tempFileName).rename(QString(keyFilePath()));
|
||||
qCDebug(commerce) << "passphrase changed successfully";
|
||||
return true;
|
||||
} else {
|
||||
qCDebug(commerce) << "couldn't write keys";
|
||||
QFile(tempFileName).remove();
|
||||
setPassphrase(oldPassphrase);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
qCDebug(commerce) << "couldn't decrypt keys with current passphrase, clearing";
|
||||
setPassphrase(QString(""));
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ public:
|
|||
|
||||
~Wallet();
|
||||
// These are currently blocking calls, although they might take a moment.
|
||||
bool createIfNeeded();
|
||||
bool generateKeyPair();
|
||||
QStringList listPublicKeys();
|
||||
QString signWithKey(const QByteArray& text, const QString& key);
|
||||
|
@ -36,21 +35,30 @@ public:
|
|||
|
||||
void setSalt(const QByteArray& salt) { _salt = salt; }
|
||||
QByteArray getSalt() { return _salt; }
|
||||
void setIv(const QByteArray& iv) { _iv = iv; }
|
||||
QByteArray getIv() { return _iv; }
|
||||
void setCKey(const QByteArray& ckey) { _ckey = ckey; }
|
||||
QByteArray getCKey() { return _ckey; }
|
||||
|
||||
void setPassphrase(const QString& passphrase);
|
||||
QString* getPassphrase() { return _passphrase; }
|
||||
bool getPassphraseIsCached() { return !(_passphrase->isEmpty()); }
|
||||
bool walletIsAuthenticatedWithPassphrase();
|
||||
bool changePassphrase(const QString& newPassphrase);
|
||||
|
||||
void reset();
|
||||
|
||||
signals:
|
||||
void securityImageResult(bool exists) ;
|
||||
void securityImageResult(bool exists);
|
||||
void keyFilePathIfExistsResult(const QString& path);
|
||||
|
||||
private:
|
||||
QStringList _publicKeys{};
|
||||
QPixmap* _securityImage { nullptr };
|
||||
QByteArray _salt {"iamsalt!"};
|
||||
QString* _passphrase { new QString("pwd") };
|
||||
QByteArray _iv;
|
||||
QByteArray _ckey;
|
||||
QString* _passphrase { new QString("") };
|
||||
|
||||
void updateImageProvider();
|
||||
bool encryptFile(const QString& inputFilePath, const QString& outputFilePath);
|
||||
|
|
|
@ -144,25 +144,33 @@ int main(int argc, const char* argv[]) {
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
// FIXME this method of checking the OpenGL version screws up the `QOpenGLContext::globalShareContext()` value, which in turn
|
||||
// leads to crashes when creating the real OpenGL instance. Disabling for now until we come up with a better way of checking
|
||||
// the GL version on the system without resorting to creating a full Qt application
|
||||
#if 0
|
||||
// Check OpenGL version.
|
||||
// This is done separately from the main Application so that start-up and shut-down logic within the main Application is
|
||||
// not made more complicated than it already is.
|
||||
bool override = false;
|
||||
bool overrideGLCheck = false;
|
||||
|
||||
QJsonObject glData;
|
||||
{
|
||||
OpenGLVersionChecker openGLVersionChecker(argc, const_cast<char**>(argv));
|
||||
bool valid = true;
|
||||
glData = openGLVersionChecker.checkVersion(valid, override);
|
||||
glData = openGLVersionChecker.checkVersion(valid, overrideGLCheck);
|
||||
if (!valid) {
|
||||
if (override) {
|
||||
if (overrideGLCheck) {
|
||||
auto glVersion = glData["version"].toString();
|
||||
qCDebug(interfaceapp, "Running on insufficient OpenGL version: %s.", glVersion.toStdString().c_str());
|
||||
qCWarning(interfaceapp, "Running on insufficient OpenGL version: %s.", glVersion.toStdString().c_str());
|
||||
} else {
|
||||
qCDebug(interfaceapp, "Early exit due to OpenGL version.");
|
||||
qCWarning(interfaceapp, "Early exit due to OpenGL version.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Debug option to demonstrate that the client's local time does not
|
||||
// need to be in sync with any other network node. This forces clock
|
||||
|
@ -223,8 +231,9 @@ int main(int argc, const char* argv[]) {
|
|||
|
||||
Application app(argcExtended, const_cast<char**>(argvExtended.data()), startupTime, runningMarkerExisted);
|
||||
|
||||
#if 0
|
||||
// If we failed the OpenGLVersion check, log it.
|
||||
if (override) {
|
||||
if (overrideGLcheck) {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
if (accountManager->isLoggedIn()) {
|
||||
UserActivityLogger::getInstance().insufficientGLVersion(glData);
|
||||
|
@ -238,6 +247,8 @@ int main(int argc, const char* argv[]) {
|
|||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Setup local server
|
||||
QLocalServer server { &app };
|
||||
|
|
|
@ -18,6 +18,8 @@ AccountScriptingInterface* AccountScriptingInterface::getInstance() {
|
|||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
QObject::connect(accountManager.data(), &AccountManager::profileChanged,
|
||||
&sharedInstance, &AccountScriptingInterface::usernameChanged);
|
||||
QObject::connect(accountManager.data(), &AccountManager::usernameChanged,
|
||||
&sharedInstance, &AccountScriptingInterface::onUsernameChanged);
|
||||
return &sharedInstance;
|
||||
}
|
||||
|
||||
|
@ -31,6 +33,21 @@ bool AccountScriptingInterface::checkAndSignalForAccessToken() {
|
|||
return accountManager->checkAndSignalForAccessToken();
|
||||
}
|
||||
|
||||
void AccountScriptingInterface::logOut() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return accountManager->logout();
|
||||
}
|
||||
|
||||
AccountScriptingInterface::AccountScriptingInterface(QObject *parent): QObject(parent) {
|
||||
m_loggedIn = isLoggedIn();
|
||||
emit loggedInChanged(m_loggedIn);
|
||||
}
|
||||
|
||||
void AccountScriptingInterface::onUsernameChanged(QString username) {
|
||||
m_loggedIn = (username != QString());
|
||||
emit loggedInChanged(m_loggedIn);
|
||||
}
|
||||
|
||||
QString AccountScriptingInterface::getUsername() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
if (accountManager->isLoggedIn()) {
|
||||
|
|
|
@ -18,6 +18,7 @@ class AccountScriptingInterface : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged)
|
||||
Q_PROPERTY(bool loggedIn READ loggedIn NOTIFY loggedInChanged)
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Account
|
||||
|
@ -32,6 +33,7 @@ signals:
|
|||
* @return {Signal}
|
||||
*/
|
||||
void usernameChanged();
|
||||
void loggedInChanged(bool loggedIn);
|
||||
|
||||
public slots:
|
||||
static AccountScriptingInterface* getInstance();
|
||||
|
@ -50,6 +52,20 @@ public slots:
|
|||
*/
|
||||
bool isLoggedIn();
|
||||
bool checkAndSignalForAccessToken();
|
||||
void logOut();
|
||||
|
||||
public:
|
||||
AccountScriptingInterface(QObject* parent = nullptr);
|
||||
bool loggedIn() const {
|
||||
return m_loggedIn;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void onUsernameChanged(QString username);
|
||||
|
||||
private:
|
||||
bool m_loggedIn { false };
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_AccountScriptingInterface_h
|
||||
|
|
|
@ -37,6 +37,20 @@ Setting::Handle<QString>& getSetting(bool contextIsHMD, QAudio::Mode mode) {
|
|||
}
|
||||
}
|
||||
|
||||
enum AudioDeviceRole {
|
||||
DisplayRole = Qt::DisplayRole,
|
||||
CheckStateRole = Qt::CheckStateRole,
|
||||
PeakRole = Qt::UserRole,
|
||||
InfoRole = Qt::UserRole + 1
|
||||
};
|
||||
|
||||
QHash<int, QByteArray> AudioDeviceList::_roles {
|
||||
{ DisplayRole, "display" },
|
||||
{ CheckStateRole, "selected" },
|
||||
{ PeakRole, "peak" },
|
||||
{ InfoRole, "info" }
|
||||
};
|
||||
|
||||
static QString getTargetDevice(bool hmd, QAudio::Mode mode) {
|
||||
QString deviceName;
|
||||
auto& setting = getSetting(hmd, mode);
|
||||
|
@ -52,29 +66,36 @@ static QString getTargetDevice(bool hmd, QAudio::Mode mode) {
|
|||
return deviceName;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> AudioDeviceList::_roles {
|
||||
{ Qt::DisplayRole, "display" },
|
||||
{ Qt::CheckStateRole, "selected" },
|
||||
{ Qt::UserRole, "info" }
|
||||
};
|
||||
Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled };
|
||||
|
||||
QVariant AudioDeviceList::data(const QModelIndex& index, int role) const {
|
||||
if (!index.isValid() || index.row() >= _devices.size()) {
|
||||
if (!index.isValid() || index.row() >= rowCount()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
return _devices.at(index.row()).display;
|
||||
} else if (role == Qt::CheckStateRole) {
|
||||
return _devices.at(index.row()).selected;
|
||||
} else if (role == Qt::UserRole) {
|
||||
return QVariant::fromValue<QAudioDeviceInfo>(_devices.at(index.row()).info);
|
||||
if (role == DisplayRole) {
|
||||
return _devices.at(index.row())->display;
|
||||
} else if (role == CheckStateRole) {
|
||||
return _devices.at(index.row())->selected;
|
||||
} else if (role == InfoRole) {
|
||||
return QVariant::fromValue<QAudioDeviceInfo>(_devices.at(index.row())->info);
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant AudioInputDeviceList::data(const QModelIndex& index, int role) const {
|
||||
if (!index.isValid() || index.row() >= rowCount()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (role == PeakRole) {
|
||||
return std::static_pointer_cast<AudioInputDevice>(_devices.at(index.row()))->peak;
|
||||
} else {
|
||||
return AudioDeviceList::data(index, role);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDeviceList::resetDevice(bool contextIsHMD) {
|
||||
auto client = DependencyManager::get<AudioClient>().data();
|
||||
QString deviceName = getTargetDevice(contextIsHMD, _mode);
|
||||
|
@ -113,8 +134,9 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) {
|
|||
auto oldDevice = _selectedDevice;
|
||||
_selectedDevice = device;
|
||||
|
||||
for (auto i = 0; i < _devices.size(); ++i) {
|
||||
AudioDevice& device = _devices[i];
|
||||
for (auto i = 0; i < rowCount(); ++i) {
|
||||
AudioDevice& device = *_devices[i];
|
||||
|
||||
if (device.selected && device.info != _selectedDevice) {
|
||||
device.selected = false;
|
||||
} else if (device.info == _selectedDevice) {
|
||||
|
@ -139,17 +161,47 @@ void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) {
|
|||
.remove("Device")
|
||||
.replace(" )", ")");
|
||||
device.selected = (device.info == _selectedDevice);
|
||||
_devices.push_back(device);
|
||||
_devices.push_back(newDevice(device));
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
bool AudioInputDeviceList::peakValuesAvailable() {
|
||||
std::call_once(_peakFlag, [&] {
|
||||
_peakValuesAvailable = DependencyManager::get<AudioClient>()->peakValuesAvailable();
|
||||
});
|
||||
return _peakValuesAvailable;
|
||||
}
|
||||
|
||||
void AudioInputDeviceList::setPeakValuesEnabled(bool enable) {
|
||||
if (peakValuesAvailable() && (enable != _peakValuesEnabled)) {
|
||||
DependencyManager::get<AudioClient>()->enablePeakValues(enable);
|
||||
_peakValuesEnabled = enable;
|
||||
emit peakValuesEnabledChanged(_peakValuesEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioInputDeviceList::onPeakValueListChanged(const QList<float>& peakValueList) {
|
||||
assert(_mode == QAudio::AudioInput);
|
||||
|
||||
if (peakValueList.length() != rowCount()) {
|
||||
qWarning() << "AudioDeviceList" << __FUNCTION__ << "length mismatch";
|
||||
}
|
||||
|
||||
for (auto i = 0; i < rowCount(); ++i) {
|
||||
std::static_pointer_cast<AudioInputDevice>(_devices[i])->peak = peakValueList[i];
|
||||
}
|
||||
|
||||
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0), { PeakRole });
|
||||
}
|
||||
|
||||
AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) {
|
||||
auto client = DependencyManager::get<AudioClient>();
|
||||
|
||||
connect(client.data(), &AudioClient::deviceChanged, this, &AudioDevices::onDeviceChanged, Qt::QueuedConnection);
|
||||
connect(client.data(), &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection);
|
||||
connect(client.data(), &AudioClient::peakValueListChanged, &_inputs, &AudioInputDeviceList::onPeakValueListChanged, Qt::QueuedConnection);
|
||||
|
||||
// connections are made after client is initialized, so we must also fetch the devices
|
||||
_inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput));
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#ifndef hifi_scripting_AudioDevices_h
|
||||
#define hifi_scripting_AudioDevices_h
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <QObject>
|
||||
#include <QAbstractListModel>
|
||||
#include <QAudioDeviceInfo>
|
||||
|
@ -29,7 +32,11 @@ class AudioDeviceList : public QAbstractListModel {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AudioDeviceList(QAudio::Mode mode) : _mode(mode) {}
|
||||
AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput) : _mode(mode) {}
|
||||
~AudioDeviceList() = default;
|
||||
|
||||
virtual std::shared_ptr<AudioDevice> newDevice(const AudioDevice& device)
|
||||
{ return std::make_shared<AudioDevice>(device); }
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override { Q_UNUSED(parent); return _devices.size(); }
|
||||
QHash<int, QByteArray> roleNames() const override { return _roles; }
|
||||
|
@ -44,25 +51,63 @@ public:
|
|||
signals:
|
||||
void deviceChanged(const QAudioDeviceInfo& device);
|
||||
|
||||
private slots:
|
||||
protected slots:
|
||||
void onDeviceChanged(const QAudioDeviceInfo& device);
|
||||
void onDevicesChanged(const QList<QAudioDeviceInfo>& devices);
|
||||
|
||||
private:
|
||||
protected:
|
||||
friend class AudioDevices;
|
||||
|
||||
static QHash<int, QByteArray> _roles;
|
||||
static Qt::ItemFlags _flags;
|
||||
const QAudio::Mode _mode;
|
||||
QAudioDeviceInfo _selectedDevice;
|
||||
QList<AudioDevice> _devices;
|
||||
QList<std::shared_ptr<AudioDevice>> _devices;
|
||||
};
|
||||
|
||||
class AudioInputDevice : public AudioDevice {
|
||||
public:
|
||||
AudioInputDevice(const AudioDevice& device) : AudioDevice(device) {}
|
||||
float peak { 0.0f };
|
||||
};
|
||||
|
||||
class AudioInputDeviceList : public AudioDeviceList {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool peakValuesAvailable READ peakValuesAvailable)
|
||||
Q_PROPERTY(bool peakValuesEnabled READ peakValuesEnabled WRITE setPeakValuesEnabled NOTIFY peakValuesEnabledChanged)
|
||||
|
||||
public:
|
||||
AudioInputDeviceList() : AudioDeviceList(QAudio::AudioInput) {}
|
||||
virtual ~AudioInputDeviceList() = default;
|
||||
|
||||
virtual std::shared_ptr<AudioDevice> newDevice(const AudioDevice& device) override
|
||||
{ return std::make_shared<AudioInputDevice>(device); }
|
||||
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
|
||||
signals:
|
||||
void peakValuesEnabledChanged(bool enabled);
|
||||
|
||||
protected slots:
|
||||
void onPeakValueListChanged(const QList<float>& peakValueList);
|
||||
|
||||
protected:
|
||||
friend class AudioDevices;
|
||||
|
||||
bool peakValuesAvailable();
|
||||
std::once_flag _peakFlag;
|
||||
bool _peakValuesAvailable;
|
||||
|
||||
bool peakValuesEnabled() const { return _peakValuesEnabled; }
|
||||
void setPeakValuesEnabled(bool enable);
|
||||
bool _peakValuesEnabled { false };
|
||||
};
|
||||
|
||||
class Audio;
|
||||
|
||||
class AudioDevices : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(AudioDeviceList* input READ getInputList NOTIFY nop)
|
||||
Q_PROPERTY(AudioInputDeviceList* input READ getInputList NOTIFY nop)
|
||||
Q_PROPERTY(AudioDeviceList* output READ getOutputList NOTIFY nop)
|
||||
|
||||
public:
|
||||
|
@ -82,10 +127,10 @@ private slots:
|
|||
private:
|
||||
friend class Audio;
|
||||
|
||||
AudioDeviceList* getInputList() { return &_inputs; }
|
||||
AudioInputDeviceList* getInputList() { return &_inputs; }
|
||||
AudioDeviceList* getOutputList() { return &_outputs; }
|
||||
|
||||
AudioDeviceList _inputs { QAudio::AudioInput };
|
||||
AudioInputDeviceList _inputs;
|
||||
AudioDeviceList _outputs { QAudio::AudioOutput };
|
||||
QAudioDeviceInfo _requestedOutputDevice;
|
||||
QAudioDeviceInfo _requestedInputDevice;
|
||||
|
|
|
@ -33,7 +33,12 @@ void DialogsManagerScriptingInterface::showAddressBar() {
|
|||
|
||||
void DialogsManagerScriptingInterface::hideAddressBar() {
|
||||
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
|
||||
"hideAddressBar", Qt::QueuedConnection);
|
||||
"hideAddressBar", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void DialogsManagerScriptingInterface::showLoginDialog() {
|
||||
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
|
||||
"showLoginDialog", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void DialogsManagerScriptingInterface::showFeed() {
|
||||
|
|
|
@ -24,6 +24,7 @@ public:
|
|||
public slots:
|
||||
void showAddressBar();
|
||||
void hideAddressBar();
|
||||
void showLoginDialog();
|
||||
|
||||
signals:
|
||||
void addressBarShown(bool visible);
|
||||
|
|
|
@ -169,12 +169,14 @@ void LoginDialog::openUrl(const QString& url) const {
|
|||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
||||
if (tablet->getToolbarMode()) {
|
||||
auto browser = offscreenUi->load("Browser.qml");
|
||||
browser->setProperty("url", url);
|
||||
offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
|
||||
newObject->setProperty("url", url);
|
||||
});
|
||||
} else {
|
||||
if (!hmd->getShouldShowTablet() && !qApp->isHMDMode()) {
|
||||
auto browser = offscreenUi->load("Browser.qml");
|
||||
browser->setProperty("url", url);
|
||||
offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) {
|
||||
newObject->setProperty("url", url);
|
||||
});
|
||||
} else {
|
||||
tablet->gotoWebScreen(url);
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ Setting::Handle<int> staticJitterBufferFrames("staticJitterBufferFrames",
|
|||
// protect the Qt internal device list
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
static Mutex _deviceMutex;
|
||||
Mutex _deviceMutex;
|
||||
|
||||
// thread-safe
|
||||
QList<QAudioDeviceInfo> getAvailableDevices(QAudio::Mode mode) {
|
||||
|
@ -227,13 +227,18 @@ AudioClient::AudioClient() :
|
|||
// start a thread to detect any device changes
|
||||
_checkDevicesTimer = new QTimer(this);
|
||||
connect(_checkDevicesTimer, &QTimer::timeout, [this] {
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||
checkDevices();
|
||||
});
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkDevices(); });
|
||||
});
|
||||
const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
|
||||
_checkDevicesTimer->start(DEVICE_CHECK_INTERVAL_MSECS);
|
||||
|
||||
// start a thread to detect peak value changes
|
||||
_checkPeakValuesTimer = new QTimer(this);
|
||||
connect(_checkPeakValuesTimer, &QTimer::timeout, [this] {
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkPeakValues(); });
|
||||
});
|
||||
const unsigned long PEAK_VALUES_CHECK_INTERVAL_MSECS = 50;
|
||||
_checkPeakValuesTimer->start(PEAK_VALUES_CHECK_INTERVAL_MSECS);
|
||||
|
||||
configureReverb();
|
||||
|
||||
|
@ -275,6 +280,7 @@ void AudioClient::cleanupBeforeQuit() {
|
|||
|
||||
stop();
|
||||
_checkDevicesTimer->stop();
|
||||
_checkPeakValuesTimer->stop();
|
||||
guard.trigger();
|
||||
}
|
||||
|
||||
|
@ -316,8 +322,6 @@ QString getWinDeviceName(IMMDevice* pEndpoint) {
|
|||
QString deviceName;
|
||||
IPropertyStore* pPropertyStore;
|
||||
pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore);
|
||||
pEndpoint->Release();
|
||||
pEndpoint = nullptr;
|
||||
PROPVARIANT pv;
|
||||
PropVariantInit(&pv);
|
||||
HRESULT hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv);
|
||||
|
@ -346,6 +350,8 @@ QString AudioClient::getWinDeviceName(wchar_t* guid) {
|
|||
deviceName = QString("NONE");
|
||||
} else {
|
||||
deviceName = ::getWinDeviceName(pEndpoint);
|
||||
pEndpoint->Release();
|
||||
pEndpoint = nullptr;
|
||||
}
|
||||
pMMDeviceEnumerator->Release();
|
||||
pMMDeviceEnumerator = nullptr;
|
||||
|
@ -429,6 +435,8 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
|
|||
deviceName = QString("NONE");
|
||||
} else {
|
||||
deviceName = getWinDeviceName(pEndpoint);
|
||||
pEndpoint->Release();
|
||||
pEndpoint = nullptr;
|
||||
}
|
||||
pMMDeviceEnumerator->Release();
|
||||
pMMDeviceEnumerator = NULL;
|
||||
|
|
|
@ -148,6 +148,9 @@ public:
|
|||
QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const;
|
||||
QList<QAudioDeviceInfo> getAudioDevices(QAudio::Mode mode) const;
|
||||
|
||||
void enablePeakValues(bool enable) { _enablePeakValues = enable; }
|
||||
bool peakValuesAvailable() const;
|
||||
|
||||
static const float CALLBACK_ACCELERATOR_RATIO;
|
||||
|
||||
bool getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName);
|
||||
|
@ -224,6 +227,7 @@ signals:
|
|||
|
||||
void deviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device);
|
||||
void devicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices);
|
||||
void peakValueListChanged(const QList<float> peakValueList);
|
||||
|
||||
void receivedFirstPacket();
|
||||
void disconnected();
|
||||
|
@ -242,9 +246,12 @@ private:
|
|||
friend class CheckDevicesThread;
|
||||
friend class LocalInjectorsThread;
|
||||
|
||||
// background tasks
|
||||
void checkDevices();
|
||||
void checkPeakValues();
|
||||
|
||||
void outputFormatChanged();
|
||||
void handleAudioInput(QByteArray& audioBuffer);
|
||||
void checkDevices();
|
||||
void prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLock = nullptr);
|
||||
bool mixLocalAudioInjectors(float* mixBuffer);
|
||||
float azimuthForSource(const glm::vec3& relativePosition);
|
||||
|
@ -298,6 +305,7 @@ private:
|
|||
std::atomic<bool> _localInjectorsAvailable { false };
|
||||
MixedProcessedAudioStream _receivedAudioStream;
|
||||
bool _isStereoInput;
|
||||
std::atomic<bool> _enablePeakValues { false };
|
||||
|
||||
quint64 _outputStarveDetectionStartTimeMsec;
|
||||
int _outputStarveDetectionCount;
|
||||
|
@ -396,6 +404,7 @@ private:
|
|||
RateCounter<> _audioInbound;
|
||||
|
||||
QTimer* _checkDevicesTimer { nullptr };
|
||||
QTimer* _checkPeakValuesTimer { nullptr };
|
||||
};
|
||||
|
||||
|
||||
|
|
176
libraries/audio-client/src/AudioPeakValues.cpp
Normal file
176
libraries/audio-client/src/AudioPeakValues.cpp
Normal file
|
@ -0,0 +1,176 @@
|
|||
//
|
||||
// AudioPeakValues.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Created by Zach Pomerantz on 6/26/2017
|
||||
// Copyright 2013 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 "AudioClient.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <endpointvolume.h>
|
||||
#include <audioclient.h>
|
||||
|
||||
#include <QString>
|
||||
|
||||
#define RETURN_ON_FAIL(result) if (FAILED(result)) { return; }
|
||||
#define CONTINUE_ON_FAIL(result) if (FAILED(result)) { continue; }
|
||||
|
||||
extern QString getWinDeviceName(IMMDevice* pEndpoint);
|
||||
extern std::mutex _deviceMutex;
|
||||
|
||||
std::map<std::wstring, std::shared_ptr<IAudioClient>> activeClients;
|
||||
|
||||
template <class T>
|
||||
void release(T* t) {
|
||||
t->Release();
|
||||
}
|
||||
|
||||
template <>
|
||||
void release(IAudioClient* audioClient) {
|
||||
audioClient->Stop();
|
||||
audioClient->Release();
|
||||
}
|
||||
|
||||
void AudioClient::checkPeakValues() {
|
||||
// prepare the windows environment
|
||||
CoInitialize(NULL);
|
||||
|
||||
// if disabled, clean up active clients
|
||||
if (!_enablePeakValues) {
|
||||
activeClients.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// lock the devices so the _inputDevices list is static
|
||||
std::unique_lock<std::mutex> lock(_deviceMutex);
|
||||
HRESULT result;
|
||||
|
||||
// initialize the payload
|
||||
QList<float> peakValueList;
|
||||
for (int i = 0; i < _inputDevices.size(); ++i) {
|
||||
peakValueList.push_back(0.0f);
|
||||
}
|
||||
|
||||
std::shared_ptr<IMMDeviceEnumerator> enumerator;
|
||||
{
|
||||
IMMDeviceEnumerator* pEnumerator;
|
||||
result = CoCreateInstance(
|
||||
__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
|
||||
__uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
|
||||
RETURN_ON_FAIL(result);
|
||||
enumerator = std::shared_ptr<IMMDeviceEnumerator>(pEnumerator, &release<IMMDeviceEnumerator>);
|
||||
}
|
||||
|
||||
std::shared_ptr<IMMDeviceCollection> endpoints;
|
||||
{
|
||||
IMMDeviceCollection* pEndpoints;
|
||||
result = enumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &pEndpoints);
|
||||
RETURN_ON_FAIL(result);
|
||||
endpoints = std::shared_ptr<IMMDeviceCollection>(pEndpoints, &release<IMMDeviceCollection>);
|
||||
}
|
||||
|
||||
UINT count;
|
||||
{
|
||||
result = endpoints->GetCount(&count);
|
||||
RETURN_ON_FAIL(result);
|
||||
}
|
||||
|
||||
IMMDevice* pDevice;
|
||||
std::shared_ptr<IMMDevice> device;
|
||||
IAudioMeterInformation* pMeterInfo;
|
||||
std::shared_ptr<IAudioMeterInformation> meterInfo;
|
||||
IAudioClient* pAudioClient;
|
||||
std::shared_ptr<IAudioClient> audioClient;
|
||||
DWORD hardwareSupport;
|
||||
LPWSTR pDeviceId = NULL;
|
||||
LPWAVEFORMATEX format;
|
||||
float peakValue;
|
||||
QString deviceName;
|
||||
int deviceIndex;
|
||||
for (UINT i = 0; i < count; ++i) {
|
||||
result = endpoints->Item(i, &pDevice);
|
||||
CONTINUE_ON_FAIL(result);
|
||||
device = std::shared_ptr<IMMDevice>(pDevice, &release<IMMDevice>);
|
||||
|
||||
// if the device isn't listed through Qt, skip it
|
||||
deviceName = ::getWinDeviceName(pDevice);
|
||||
deviceIndex = 0;
|
||||
for (; deviceIndex < _inputDevices.size(); ++deviceIndex) {
|
||||
if (deviceName == _inputDevices[deviceIndex].deviceName()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (deviceIndex >= _inputDevices.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//continue;
|
||||
|
||||
result = device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_ALL, NULL, (void**)&pMeterInfo);
|
||||
CONTINUE_ON_FAIL(result);
|
||||
meterInfo = std::shared_ptr<IAudioMeterInformation>(pMeterInfo, &release<IAudioMeterInformation>);
|
||||
|
||||
//continue;
|
||||
|
||||
hardwareSupport;
|
||||
result = meterInfo->QueryHardwareSupport(&hardwareSupport);
|
||||
CONTINUE_ON_FAIL(result);
|
||||
|
||||
//continue;
|
||||
|
||||
// if the device has no hardware support (USB)...
|
||||
if (!(hardwareSupport & ENDPOINT_HARDWARE_SUPPORT_METER)) {
|
||||
result = device->GetId(&pDeviceId);
|
||||
CONTINUE_ON_FAIL(result);
|
||||
std::wstring deviceId(pDeviceId);
|
||||
CoTaskMemFree(pDeviceId);
|
||||
|
||||
//continue;
|
||||
|
||||
// ...and no active client...
|
||||
if (activeClients.find(deviceId) == activeClients.end()) {
|
||||
result = device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient);
|
||||
CONTINUE_ON_FAIL(result);
|
||||
audioClient = std::shared_ptr<IAudioClient>(pAudioClient, &release<IAudioClient>);
|
||||
|
||||
//continue;
|
||||
|
||||
// ...activate a client
|
||||
audioClient->GetMixFormat(&format);
|
||||
audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 0, 0, format, NULL);
|
||||
audioClient->Start();
|
||||
|
||||
//continue;
|
||||
|
||||
activeClients[deviceId] = audioClient;
|
||||
}
|
||||
}
|
||||
|
||||
// get the peak value and put it in the payload
|
||||
meterInfo->GetPeakValue(&peakValue);
|
||||
peakValueList[deviceIndex] = peakValue;
|
||||
}
|
||||
|
||||
emit peakValueListChanged(peakValueList);
|
||||
}
|
||||
|
||||
bool AudioClient::peakValuesAvailable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
void AudioClient::checkPeakValues() {
|
||||
}
|
||||
|
||||
bool AudioClient::peakValuesAvailable() const {
|
||||
return false;
|
||||
}
|
||||
#endif
|
|
@ -648,7 +648,9 @@ void Avatar::render(RenderArgs* renderArgs) {
|
|||
return;
|
||||
}
|
||||
|
||||
fixupModelsInScene(renderArgs->_scene);
|
||||
if (!isMyAvatar()) {
|
||||
fixupModelsInScene(renderArgs->_scene);
|
||||
}
|
||||
|
||||
if (showCollisionShapes && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) {
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes");
|
||||
|
|
|
@ -62,24 +62,9 @@ QRect HmdDisplayPlugin::getRecommendedOverlayRect() const {
|
|||
return CompositorHelper::VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT;
|
||||
}
|
||||
|
||||
bool HmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||
if (!_vsyncEnabled && !_disablePreviewItemAdded) {
|
||||
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), DISABLE_PREVIEW,
|
||||
[this](bool clicked) {
|
||||
_disablePreview = clicked;
|
||||
_container->setBoolSetting("disableHmdPreview", _disablePreview);
|
||||
if (_disablePreview) {
|
||||
_clearPreviewFlag = true;
|
||||
}
|
||||
}, true, _disablePreview);
|
||||
_disablePreviewItemAdded = true;
|
||||
}
|
||||
return Parent::beginFrameRender(frameIndex);
|
||||
}
|
||||
|
||||
#define DISABLE_PREVIEW_MENU_ITEM_DELAY_MS 500
|
||||
|
||||
bool HmdDisplayPlugin::internalActivate() {
|
||||
_disablePreviewItemAdded = false;
|
||||
_monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW);
|
||||
_clearPreviewFlag = true;
|
||||
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), MONO_PREVIEW,
|
||||
|
@ -94,6 +79,19 @@ bool HmdDisplayPlugin::internalActivate() {
|
|||
_disablePreview = _container->getBoolSetting("disableHmdPreview", DEFAULT_DISABLE_PREVIEW || _vsyncEnabled);
|
||||
#endif
|
||||
|
||||
QTimer::singleShot(DISABLE_PREVIEW_MENU_ITEM_DELAY_MS, [this] {
|
||||
if (isActive() && !_vsyncEnabled) {
|
||||
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), DISABLE_PREVIEW,
|
||||
[this](bool clicked) {
|
||||
_disablePreview = clicked;
|
||||
_container->setBoolSetting("disableHmdPreview", _disablePreview);
|
||||
if (_disablePreview) {
|
||||
_clearPreviewFlag = true;
|
||||
}
|
||||
}, true, _disablePreview);
|
||||
}
|
||||
});
|
||||
|
||||
_container->removeMenu(FRAMERATE);
|
||||
for_each_eye([&](Eye eye) {
|
||||
_eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]);
|
||||
|
|
|
@ -51,7 +51,6 @@ protected:
|
|||
virtual void postPreview() {};
|
||||
virtual void updatePresentPose();
|
||||
|
||||
bool beginFrameRender(uint32_t frameIndex) override;
|
||||
bool internalActivate() override;
|
||||
void internalDeactivate() override;
|
||||
void compositeOverlay() override;
|
||||
|
@ -87,7 +86,6 @@ private:
|
|||
ivec4 getViewportForSourceSize(const uvec2& size) const;
|
||||
float getLeftCenterPixel() const;
|
||||
|
||||
bool _disablePreviewItemAdded { false };
|
||||
bool _monoPreview { true };
|
||||
bool _clearPreviewFlag { false };
|
||||
gpu::TexturePointer _previewTexture;
|
||||
|
|
|
@ -91,7 +91,12 @@ void entitiesScriptEngineDeleter(ScriptEngine* engine) {
|
|||
};
|
||||
|
||||
// Wait for the scripting thread from the thread pool to avoid hanging the main thread
|
||||
QThreadPool::globalInstance()->start(new WaitRunnable(engine));
|
||||
auto threadPool = QThreadPool::globalInstance();
|
||||
if (threadPool) {
|
||||
threadPool->start(new WaitRunnable(engine));
|
||||
} else {
|
||||
delete engine;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::resetEntitiesScriptEngine() {
|
||||
|
|
|
@ -910,6 +910,8 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
|||
|
||||
const QVector<FBXAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
|
||||
auto& fbxJoints = _animation->getGeometry().joints;
|
||||
auto& originalFbxJoints = _model->getFBXGeometry().joints;
|
||||
bool allowTranslation = entity->getAnimationAllowTranslation();
|
||||
int frameCount = frames.size();
|
||||
if (frameCount <= 0) {
|
||||
return;
|
||||
|
@ -952,7 +954,9 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
|||
int index = _jointMapping[j];
|
||||
if (index >= 0) {
|
||||
glm::mat4 translationMat;
|
||||
if (index < translations.size()) {
|
||||
if (!allowTranslation){
|
||||
translationMat = glm::translate(originalFbxJoints[index].translation);
|
||||
} else if (index < translations.size()) {
|
||||
translationMat = glm::translate(translations[index]);
|
||||
}
|
||||
glm::mat4 rotationMat;
|
||||
|
|
|
@ -91,20 +91,27 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
|||
qCWarning(entitiesrenderer) << "Bad particle properties";
|
||||
}
|
||||
}
|
||||
if (_particleProperties != newParticleProperties) {
|
||||
|
||||
if (resultWithReadLock<bool>([&]{ return _particleProperties != newParticleProperties; })) {
|
||||
_timeUntilNextEmit = 0;
|
||||
_particleProperties = newParticleProperties;
|
||||
withWriteLock([&]{
|
||||
_particleProperties = newParticleProperties;
|
||||
});
|
||||
}
|
||||
_emitting = entity->getIsEmitting();
|
||||
|
||||
if (_particleProperties.textures.isEmpty()) {
|
||||
bool hasTexture = resultWithReadLock<bool>([&]{ return _particleProperties.textures.isEmpty(); });
|
||||
if (hasTexture) {
|
||||
if (_networkTexture) {
|
||||
withWriteLock([&] {
|
||||
_networkTexture.reset();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (!_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures)) {
|
||||
bool textureNeedsUpdate = resultWithReadLock<bool>([&]{
|
||||
return !_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures);
|
||||
});
|
||||
if (textureNeedsUpdate) {
|
||||
withWriteLock([&] {
|
||||
_networkTexture = DependencyManager::get<TextureCache>()->getTexture(_particleProperties.textures);
|
||||
});
|
||||
|
@ -115,15 +122,17 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
|||
void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
// Fill in Uniforms structure
|
||||
ParticleUniforms particleUniforms;
|
||||
particleUniforms.radius.start = _particleProperties.radius.range.start;
|
||||
particleUniforms.radius.middle = _particleProperties.radius.gradient.target;
|
||||
particleUniforms.radius.finish = _particleProperties.radius.range.finish;
|
||||
particleUniforms.radius.spread = _particleProperties.radius.gradient.spread;
|
||||
particleUniforms.color.start = _particleProperties.getColorStart();
|
||||
particleUniforms.color.middle = _particleProperties.getColorMiddle();
|
||||
particleUniforms.color.finish = _particleProperties.getColorFinish();
|
||||
particleUniforms.color.spread = _particleProperties.getColorSpread();
|
||||
particleUniforms.lifespan = _particleProperties.lifespan;
|
||||
withReadLock([&]{
|
||||
particleUniforms.radius.start = _particleProperties.radius.range.start;
|
||||
particleUniforms.radius.middle = _particleProperties.radius.gradient.target;
|
||||
particleUniforms.radius.finish = _particleProperties.radius.range.finish;
|
||||
particleUniforms.radius.spread = _particleProperties.radius.gradient.spread;
|
||||
particleUniforms.color.start = _particleProperties.getColorStart();
|
||||
particleUniforms.color.middle = _particleProperties.getColorMiddle();
|
||||
particleUniforms.color.finish = _particleProperties.getColorFinish();
|
||||
particleUniforms.color.spread = _particleProperties.getColorSpread();
|
||||
particleUniforms.lifespan = _particleProperties.lifespan;
|
||||
});
|
||||
// Update particle uniforms
|
||||
memcpy(&_uniformBuffer.edit<ParticleUniforms>(), &particleUniforms, sizeof(ParticleUniforms));
|
||||
}
|
||||
|
@ -146,35 +155,26 @@ Item::Bound ParticleEffectEntityRenderer::getBound() {
|
|||
|
||||
static const size_t VERTEX_PER_PARTICLE = 4;
|
||||
|
||||
bool ParticleEffectEntityRenderer::emitting() const {
|
||||
return (
|
||||
_emitting &&
|
||||
_particleProperties.emission.rate > 0.0f &&
|
||||
_particleProperties.lifespan > 0.0f &&
|
||||
_particleProperties.polar.start <= _particleProperties.polar.finish
|
||||
);
|
||||
}
|
||||
|
||||
void ParticleEffectEntityRenderer::createParticle(uint64_t now) {
|
||||
ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties) {
|
||||
CpuParticle particle;
|
||||
|
||||
const auto& accelerationSpread = _particleProperties.emission.acceleration.spread;
|
||||
const auto& azimuthStart = _particleProperties.azimuth.start;
|
||||
const auto& azimuthFinish = _particleProperties.azimuth.finish;
|
||||
const auto& emitDimensions = _particleProperties.emission.dimensions;
|
||||
const auto& emitAcceleration = _particleProperties.emission.acceleration.target;
|
||||
auto emitOrientation = _particleProperties.emission.orientation;
|
||||
const auto& emitRadiusStart = glm::max(_particleProperties.radiusStart, EPSILON); // Avoid math complications at center
|
||||
const auto& emitSpeed = _particleProperties.emission.speed.target;
|
||||
const auto& speedSpread = _particleProperties.emission.speed.spread;
|
||||
const auto& polarStart = _particleProperties.polar.start;
|
||||
const auto& polarFinish = _particleProperties.polar.finish;
|
||||
const auto& accelerationSpread = particleProperties.emission.acceleration.spread;
|
||||
const auto& azimuthStart = particleProperties.azimuth.start;
|
||||
const auto& azimuthFinish = particleProperties.azimuth.finish;
|
||||
const auto& emitDimensions = particleProperties.emission.dimensions;
|
||||
const auto& emitAcceleration = particleProperties.emission.acceleration.target;
|
||||
auto emitOrientation = particleProperties.emission.orientation;
|
||||
const auto& emitRadiusStart = glm::max(particleProperties.radiusStart, EPSILON); // Avoid math complications at center
|
||||
const auto& emitSpeed = particleProperties.emission.speed.target;
|
||||
const auto& speedSpread = particleProperties.emission.speed.spread;
|
||||
const auto& polarStart = particleProperties.polar.start;
|
||||
const auto& polarFinish = particleProperties.polar.finish;
|
||||
|
||||
particle.seed = randFloatInRange(-1.0f, 1.0f);
|
||||
particle.expiration = now + (uint64_t)(_particleProperties.lifespan * USECS_PER_SECOND);
|
||||
if (_particleProperties.emission.shouldTrail) {
|
||||
particle.position = _modelTransform.getTranslation();
|
||||
emitOrientation = _modelTransform.getRotation() * emitOrientation;
|
||||
particle.expiration = now + (uint64_t)(particleProperties.lifespan * USECS_PER_SECOND);
|
||||
if (particleProperties.emission.shouldTrail) {
|
||||
particle.position = baseTransform.getTranslation();
|
||||
emitOrientation = baseTransform.getRotation() * emitOrientation;
|
||||
}
|
||||
|
||||
// Position, velocity, and acceleration
|
||||
|
@ -232,7 +232,7 @@ void ParticleEffectEntityRenderer::createParticle(uint64_t now) {
|
|||
particle.acceleration = emitAcceleration + randFloatInRange(-1.0f, 1.0f) * accelerationSpread;
|
||||
}
|
||||
|
||||
_cpuParticles.push_back(particle);
|
||||
return particle;
|
||||
}
|
||||
|
||||
void ParticleEffectEntityRenderer::stepSimulation() {
|
||||
|
@ -244,14 +244,19 @@ void ParticleEffectEntityRenderer::stepSimulation() {
|
|||
const auto now = usecTimestampNow();
|
||||
const auto interval = std::min<uint64_t>(USECS_PER_SECOND / 60, now - _lastSimulated);
|
||||
_lastSimulated = now;
|
||||
|
||||
particle::Properties particleProperties;
|
||||
withReadLock([&]{
|
||||
particleProperties = _particleProperties;
|
||||
});
|
||||
|
||||
if (emitting()) {
|
||||
uint64_t emitInterval = (uint64_t)(USECS_PER_SECOND / _particleProperties.emission.rate);
|
||||
if (interval >= _timeUntilNextEmit) {
|
||||
if (_emitting && particleProperties.emitting()) {
|
||||
uint64_t emitInterval = particleProperties.emitIntervalUsecs();
|
||||
if (emitInterval > 0 && interval >= _timeUntilNextEmit) {
|
||||
auto timeRemaining = interval;
|
||||
while (timeRemaining > _timeUntilNextEmit) {
|
||||
// emit particle
|
||||
createParticle(now);
|
||||
_cpuParticles.push_back(createParticle(now, _modelTransform, particleProperties));
|
||||
_timeUntilNextEmit = emitInterval;
|
||||
if (emitInterval < timeRemaining) {
|
||||
timeRemaining -= emitInterval;
|
||||
|
@ -263,7 +268,7 @@ void ParticleEffectEntityRenderer::stepSimulation() {
|
|||
}
|
||||
|
||||
// Kill any particles that have expired or are over the max size
|
||||
while (_cpuParticles.size() > _particleProperties.maxParticles || (!_cpuParticles.empty() && _cpuParticles.front().expiration <= now)) {
|
||||
while (_cpuParticles.size() > particleProperties.maxParticles || (!_cpuParticles.empty() && _cpuParticles.front().expiration <= now)) {
|
||||
_cpuParticles.pop_front();
|
||||
}
|
||||
|
||||
|
|
|
@ -93,9 +93,8 @@ private:
|
|||
};
|
||||
|
||||
|
||||
void createParticle(uint64_t now);
|
||||
static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties);
|
||||
void stepSimulation();
|
||||
bool emitting() const;
|
||||
|
||||
particle::Properties _particleProperties;
|
||||
CpuParticles _cpuParticles;
|
||||
|
|
|
@ -30,14 +30,11 @@ TextEntityRenderer::TextEntityRenderer(const EntityItemPointer& entity) :
|
|||
|
||||
}
|
||||
|
||||
|
||||
void TextEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) {
|
||||
TextEntityRenderer::~TextEntityRenderer() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (_geometryID && geometryCache) {
|
||||
geometryCache->releaseID(_geometryID);
|
||||
}
|
||||
delete _textRenderer;
|
||||
_textRenderer = nullptr;
|
||||
}
|
||||
|
||||
bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
|
|
|
@ -24,14 +24,13 @@ class TextEntityRenderer : public TypedEntityRenderer<TextEntityItem> {
|
|||
using Pointer = std::shared_ptr<TextEntityRenderer>;
|
||||
public:
|
||||
TextEntityRenderer(const EntityItemPointer& entity);
|
||||
|
||||
~TextEntityRenderer();
|
||||
private:
|
||||
virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
int _geometryID{ 0 };
|
||||
TextRenderer3D* _textRenderer;
|
||||
std::shared_ptr<TextRenderer3D> _textRenderer;
|
||||
bool _faceCamera;
|
||||
glm::vec3 _dimensions;
|
||||
glm::vec3 _textColor;
|
||||
|
|
|
@ -33,6 +33,7 @@ bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b
|
|||
|
||||
void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_PLAYING, Animation, animation, Running, running);
|
||||
|
@ -46,6 +47,7 @@ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire
|
|||
void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
|
||||
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, url, QString, setURL);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, allowTranslation, bool, setAllowTranslation);
|
||||
|
||||
// legacy property support
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationURL, QString, setURL, getURL);
|
||||
|
@ -67,6 +69,7 @@ void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, boo
|
|||
|
||||
void AnimationPropertyGroup::merge(const AnimationPropertyGroup& other) {
|
||||
COPY_PROPERTY_IF_CHANGED(url);
|
||||
COPY_PROPERTY_IF_CHANGED(allowTranslation);
|
||||
COPY_PROPERTY_IF_CHANGED(fps);
|
||||
COPY_PROPERTY_IF_CHANGED(currentFrame);
|
||||
COPY_PROPERTY_IF_CHANGED(running);
|
||||
|
@ -88,6 +91,7 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) {
|
|||
float lastFrame = getLastFrame();
|
||||
bool loop = getLoop();
|
||||
bool hold = getHold();
|
||||
bool allowTranslation = getAllowTranslation();
|
||||
|
||||
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
||||
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
||||
|
@ -122,6 +126,11 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) {
|
|||
running = settingsMap["hold"].toBool();
|
||||
}
|
||||
|
||||
if (settingsMap.contains("allowTranslation")) {
|
||||
allowTranslation = settingsMap["allowTranslation"].toBool();
|
||||
}
|
||||
|
||||
setAllowTranslation(allowTranslation);
|
||||
setFPS(fps);
|
||||
setCurrentFrame(currentFrame);
|
||||
setRunning(running);
|
||||
|
@ -137,6 +146,7 @@ void AnimationPropertyGroup::debugDump() const {
|
|||
qCDebug(entities) << " url:" << getURL() << " has changed:" << urlChanged();
|
||||
qCDebug(entities) << " fps:" << getFPS() << " has changed:" << fpsChanged();
|
||||
qCDebug(entities) << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged();
|
||||
qCDebug(entities) << "allowTranslation:" << getAllowTranslation() << " has changed:" << allowTranslationChanged();
|
||||
}
|
||||
|
||||
void AnimationPropertyGroup::listChangedProperties(QList<QString>& out) {
|
||||
|
@ -149,6 +159,9 @@ void AnimationPropertyGroup::listChangedProperties(QList<QString>& out) {
|
|||
if (currentFrameChanged()) {
|
||||
out << "animation-currentFrame";
|
||||
}
|
||||
if (allowTranslationChanged()) {
|
||||
out << "animation-allowTranslation";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -162,6 +175,7 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
|
|||
bool successPropertyFits = true;
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, getAllowTranslation());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning());
|
||||
|
@ -181,6 +195,7 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF
|
|||
bool somethingChanged = false;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, bool, setAllowTranslation);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame);
|
||||
|
@ -198,7 +213,8 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF
|
|||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold);
|
||||
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation);
|
||||
|
||||
processedBytes += bytesRead;
|
||||
|
||||
Q_UNUSED(somethingChanged);
|
||||
|
@ -215,6 +231,7 @@ void AnimationPropertyGroup::markAllChanged() {
|
|||
_firstFrameChanged = true;
|
||||
_lastFrameChanged = true;
|
||||
_holdChanged = true;
|
||||
_allowTranslationChanged = true;
|
||||
}
|
||||
|
||||
EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const {
|
||||
|
@ -228,12 +245,14 @@ EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_ALLOW_TRANSLATION, allowTranslation);
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
||||
void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) const {
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, URL, getURL);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, AllowTranslation, getAllowTranslation);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, getFPS);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, getCurrentFrame);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Running, getRunning);
|
||||
|
@ -247,6 +266,7 @@ bool AnimationPropertyGroup::setProperties(const EntityItemProperties& propertie
|
|||
bool somethingChanged = false;
|
||||
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, URL, url, setURL);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, AllowTranslation, allowTranslation, setAllowTranslation);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, setFPS);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, setCurrentFrame);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Running, running, setRunning);
|
||||
|
@ -268,6 +288,7 @@ EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamP
|
|||
requestedProperties += PROP_ANIMATION_FIRST_FRAME;
|
||||
requestedProperties += PROP_ANIMATION_LAST_FRAME;
|
||||
requestedProperties += PROP_ANIMATION_HOLD;
|
||||
requestedProperties += PROP_ANIMATION_ALLOW_TRANSLATION;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
@ -283,6 +304,7 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En
|
|||
bool successPropertyFits = true;
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, getAllowTranslation());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning());
|
||||
|
@ -301,6 +323,7 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char
|
|||
const unsigned char* dataAt = data;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, bool, setAllowTranslation);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setRunning);
|
||||
|
|
|
@ -85,6 +85,7 @@ public:
|
|||
DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame
|
||||
DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame
|
||||
DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold
|
||||
DEFINE_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation, allowTranslation, bool, true);
|
||||
|
||||
protected:
|
||||
friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b);
|
||||
|
|
|
@ -1033,6 +1033,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
|||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation);
|
||||
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_URL, Skybox, skybox, URL, url);
|
||||
|
|
|
@ -40,6 +40,7 @@ enum EntityPropertyList {
|
|||
PROP_ANIMATION_FPS,
|
||||
PROP_ANIMATION_FRAME_INDEX,
|
||||
PROP_ANIMATION_PLAYING,
|
||||
PROP_ANIMATION_ALLOW_TRANSLATION,
|
||||
|
||||
// these properties are supported by the EntityItem base class
|
||||
PROP_REGISTRATION_POINT,
|
||||
|
|
|
@ -330,6 +330,10 @@ void ModelEntityItem::setAnimationSettings(const QString& value) {
|
|||
setAnimationHold(hold);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("allowTranslation")) {
|
||||
bool allowTranslation = settingsMap["allowTranslation"].toBool();
|
||||
setAnimationAllowTranslation(allowTranslation);
|
||||
}
|
||||
_dirtyFlags |= Simulation::DIRTY_UPDATEABLE;
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,10 @@ public:
|
|||
|
||||
void setAnimationCurrentFrame(float value);
|
||||
void setAnimationIsPlaying(bool value);
|
||||
void setAnimationFPS(float value);
|
||||
void setAnimationFPS(float value);
|
||||
|
||||
void setAnimationAllowTranslation(bool value) { _animationProperties.setAllowTranslation(value); };
|
||||
bool getAnimationAllowTranslation() const { return _animationProperties.getAllowTranslation(); };
|
||||
|
||||
void setAnimationLoop(bool loop);
|
||||
bool getAnimationLoop() const;
|
||||
|
|
|
@ -104,7 +104,7 @@ bool operator!=(const Properties& a, const Properties& b) {
|
|||
return !(a == b);
|
||||
}
|
||||
|
||||
bool particle::Properties::valid() const {
|
||||
bool Properties::valid() const {
|
||||
if (glm::any(glm::isnan(emission.orientation))) {
|
||||
qCWarning(entities) << "Bad particle data";
|
||||
return false;
|
||||
|
@ -133,6 +133,19 @@ bool particle::Properties::valid() const {
|
|||
(radius.gradient.spread == glm::clamp(radius.gradient.spread, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS));
|
||||
}
|
||||
|
||||
bool Properties::emitting() const {
|
||||
return emission.rate > 0.0f && lifespan > 0.0f && polar.start <= polar.finish;
|
||||
|
||||
}
|
||||
|
||||
uint64_t Properties::emitIntervalUsecs() const {
|
||||
if (emission.rate > 0.0f) {
|
||||
return (uint64_t)(USECS_PER_SECOND / emission.rate);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer entity { new ParticleEffectEntityItem(entityID) };
|
||||
entity->setProperties(properties);
|
||||
|
|
|
@ -160,7 +160,9 @@ namespace particle {
|
|||
Properties() {};
|
||||
Properties(const Properties& other) { *this = other; }
|
||||
bool valid() const;
|
||||
|
||||
bool emitting() const;
|
||||
uint64_t emitIntervalUsecs() const;
|
||||
|
||||
Properties& operator =(const Properties& other) {
|
||||
color = other.color;
|
||||
alpha = other.alpha;
|
||||
|
|
|
@ -40,8 +40,11 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
|
|||
|
||||
int glVersionToInteger(QString glVersion) {
|
||||
QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]"));
|
||||
int majorNumber = versionParts[0].toInt();
|
||||
int minorNumber = versionParts[1].toInt();
|
||||
int majorNumber = 0, minorNumber = 0;
|
||||
if (versionParts.size() >= 2) {
|
||||
majorNumber = versionParts[0].toInt();
|
||||
minorNumber = versionParts[1].toInt();
|
||||
}
|
||||
return (majorNumber << 16) | minorNumber;
|
||||
}
|
||||
|
||||
|
|
|
@ -467,12 +467,14 @@ void GLVariableAllocationSupport::updateMemoryPressure() {
|
|||
_demoteQueue = WorkQueue();
|
||||
|
||||
// Populate the existing textures into the queue
|
||||
for (const auto& texture : strongTextures) {
|
||||
// Race conditions can still leave nulls in the list, so we need to check
|
||||
if (!texture) {
|
||||
continue;
|
||||
if (_memoryPressureState != MemoryPressureState::Idle) {
|
||||
for (const auto& texture : strongTextures) {
|
||||
// Race conditions can still leave nulls in the list, so we need to check
|
||||
if (!texture) {
|
||||
continue;
|
||||
}
|
||||
addToWorkQueue(texture);
|
||||
}
|
||||
addToWorkQueue(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
case PacketType::EntityPhysics:
|
||||
return VERSION_ENTITIES_HAS_HIGHLIGHT_SCRIPTING_INTERFACE;
|
||||
return VERSION_ENTITIES_ANIMATION_ALLOW_TRANSLATION_PROPERTIES;
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
|
||||
case PacketType::AvatarIdentity:
|
||||
|
|
|
@ -260,6 +260,7 @@ const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69;
|
|||
const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70;
|
||||
const PacketVersion VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT = 71;
|
||||
const PacketVersion VERSION_ENTITIES_HAS_HIGHLIGHT_SCRIPTING_INTERFACE = 72;
|
||||
const PacketVersion VERSION_ENTITIES_ANIMATION_ALLOW_TRANSLATION_PROPERTIES = 73;
|
||||
|
||||
enum class EntityQueryPacketVersion: PacketVersion {
|
||||
JSONFilter = 18,
|
||||
|
|
|
@ -431,7 +431,7 @@ int Octree::readElementData(const OctreeElementPointer& destinationElement, cons
|
|||
return bytesRead;
|
||||
}
|
||||
|
||||
void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long int bufferSizeBytes,
|
||||
void Octree::readBitstreamToTree(const unsigned char * bitstream, uint64_t bufferSizeBytes,
|
||||
ReadBitstreamToTreeParams& args) {
|
||||
int bytesRead = 0;
|
||||
const unsigned char* bitstreamAt = bitstream;
|
||||
|
@ -925,8 +925,8 @@ int Octree::encodeTreeBitstream(const OctreeElementPointer& element,
|
|||
roomForOctalCode = packetData->startSubTree(newCode);
|
||||
|
||||
if (newCode) {
|
||||
delete[] newCode;
|
||||
codeLength = numberOfThreeBitSectionsInCode(newCode);
|
||||
delete[] newCode;
|
||||
} else {
|
||||
codeLength = 1;
|
||||
}
|
||||
|
@ -1152,7 +1152,6 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element,
|
|||
OctreeElementPointer sortedChildren[NUMBER_OF_CHILDREN] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
|
||||
float distancesToChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
int indexOfChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
int currentCount = 0;
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
OctreeElementPointer childElement = element->getChildAtIndex(i);
|
||||
|
@ -1174,7 +1173,6 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element,
|
|||
sortedChildren[i] = childElement;
|
||||
indexOfChildren[i] = i;
|
||||
distancesToChildren[i] = 0.0f;
|
||||
currentCount++;
|
||||
|
||||
// track stats
|
||||
// must check childElement here, because it could be we got here with no childElement
|
||||
|
@ -1185,7 +1183,7 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element,
|
|||
|
||||
// for each child element in Distance sorted order..., check to see if they exist, are colored, and in view, and if so
|
||||
// add them to our distance ordered array of children
|
||||
for (int i = 0; i < currentCount; i++) {
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
OctreeElementPointer childElement = sortedChildren[i];
|
||||
int originalIndex = indexOfChildren[i];
|
||||
|
||||
|
@ -1276,7 +1274,7 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element,
|
|||
}
|
||||
|
||||
// NOTE: the childrenDataBits indicates that there is an array of child element data included in this packet.
|
||||
// We wil write this bit mask but we may come back later and update the bits that are actually included
|
||||
// We will write this bit mask but we may come back later and update the bits that are actually included
|
||||
packetData->releaseReservedBytes(sizeof(childrenDataBits));
|
||||
continueThisLevel = packetData->appendBitMask(childrenDataBits);
|
||||
|
||||
|
@ -1441,7 +1439,7 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element,
|
|||
|
||||
// for each child element in Distance sorted order..., check to see if they exist, are colored, and in view, and if so
|
||||
// add them to our distance ordered array of children
|
||||
for (int indexByDistance = 0; indexByDistance < currentCount; indexByDistance++) {
|
||||
for (int indexByDistance = 0; indexByDistance < NUMBER_OF_CHILDREN; indexByDistance++) {
|
||||
OctreeElementPointer childElement = sortedChildren[indexByDistance];
|
||||
int originalIndex = indexOfChildren[indexByDistance];
|
||||
|
||||
|
@ -1638,7 +1636,7 @@ bool Octree::readFromFile(const char* fileName) {
|
|||
|
||||
QDataStream fileInputStream(&file);
|
||||
QFileInfo fileInfo(qFileName);
|
||||
unsigned long fileLength = fileInfo.size();
|
||||
uint64_t fileLength = fileInfo.size();
|
||||
|
||||
emit importSize(1.0f, 1.0f, 1.0f);
|
||||
emit importProgress(0);
|
||||
|
@ -1715,7 +1713,7 @@ bool Octree::readFromURL(const QString& urlString) {
|
|||
}
|
||||
|
||||
|
||||
bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream, const QString& marketplaceID) {
|
||||
bool Octree::readFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID) {
|
||||
// decide if this is binary SVO or JSON-formatted SVO
|
||||
QIODevice *device = inputStream.device();
|
||||
char firstChar;
|
||||
|
@ -1732,14 +1730,14 @@ bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream
|
|||
}
|
||||
|
||||
|
||||
bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStream) {
|
||||
bool Octree::readSVOFromStream(uint64_t streamLength, QDataStream& inputStream) {
|
||||
qWarning() << "SVO file format depricated. Support for reading SVO files is no longer support and will be removed soon.";
|
||||
|
||||
bool fileOk = false;
|
||||
|
||||
PacketVersion gotVersion = 0;
|
||||
|
||||
unsigned long headerLength = 0; // bytes in the header
|
||||
uint64_t headerLength = 0; // bytes in the header
|
||||
|
||||
bool wantImportProgress = true;
|
||||
|
||||
|
@ -1751,14 +1749,14 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr
|
|||
if (getWantSVOfileVersions()) {
|
||||
|
||||
// read just enough of the file to parse the header...
|
||||
const unsigned long HEADER_LENGTH = sizeof(int) + sizeof(PacketVersion);
|
||||
const uint64_t HEADER_LENGTH = sizeof(int) + sizeof(PacketVersion);
|
||||
unsigned char fileHeader[HEADER_LENGTH];
|
||||
inputStream.readRawData((char*)&fileHeader, HEADER_LENGTH);
|
||||
|
||||
headerLength = HEADER_LENGTH; // we need this later to skip to the data
|
||||
|
||||
unsigned char* dataAt = (unsigned char*)&fileHeader;
|
||||
unsigned long dataLength = HEADER_LENGTH;
|
||||
uint64_t dataLength = HEADER_LENGTH;
|
||||
|
||||
// if so, read the first byte of the file and see if it matches the expected version code
|
||||
int intPacketType;
|
||||
|
@ -1804,7 +1802,7 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr
|
|||
if (!hasBufferBreaks) {
|
||||
|
||||
// read the entire file into a buffer, WHAT!? Why not.
|
||||
unsigned long dataLength = streamLength - headerLength;
|
||||
uint64_t dataLength = streamLength - headerLength;
|
||||
unsigned char* entireFileDataSection = new unsigned char[dataLength];
|
||||
inputStream.readRawData((char*)entireFileDataSection, dataLength);
|
||||
|
||||
|
@ -1819,9 +1817,9 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr
|
|||
} else {
|
||||
|
||||
|
||||
unsigned long dataLength = streamLength - headerLength;
|
||||
unsigned long remainingLength = dataLength;
|
||||
const unsigned long MAX_CHUNK_LENGTH = MAX_OCTREE_PACKET_SIZE * 2;
|
||||
uint64_t dataLength = streamLength - headerLength;
|
||||
uint64_t remainingLength = dataLength;
|
||||
const uint64_t MAX_CHUNK_LENGTH = MAX_OCTREE_PACKET_SIZE * 2;
|
||||
unsigned char* fileChunk = new unsigned char[MAX_CHUNK_LENGTH];
|
||||
|
||||
while (remainingLength > 0) {
|
||||
|
@ -1847,7 +1845,7 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr
|
|||
remainingLength -= chunkLength;
|
||||
|
||||
unsigned char* dataAt = fileChunk;
|
||||
unsigned long dataLength = chunkLength;
|
||||
uint64_t dataLength = chunkLength;
|
||||
|
||||
ReadBitstreamToTreeParams args(NO_EXISTS_BITS, NULL, 0,
|
||||
SharedNodePointer(), wantImportProgress, gotVersion);
|
||||
|
@ -1887,7 +1885,7 @@ QJsonDocument addMarketplaceIDToDocumentEntities(QJsonDocument& doc, const QStri
|
|||
|
||||
const int READ_JSON_BUFFER_SIZE = 2048;
|
||||
|
||||
bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputStream, const QString& marketplaceID /*=""*/) {
|
||||
bool Octree::readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID /*=""*/) {
|
||||
// if the data is gzipped we may not have a useful bytesAvailable() result, so just keep reading until
|
||||
// we get an eof. Leave streamLength parameter for consistency.
|
||||
|
||||
|
@ -1982,14 +1980,14 @@ bool Octree::writeToJSONFile(const char* fileName, const OctreeElementPointer& e
|
|||
return success;
|
||||
}
|
||||
|
||||
unsigned long Octree::getOctreeElementsCount() {
|
||||
unsigned long nodeCount = 0;
|
||||
uint64_t Octree::getOctreeElementsCount() {
|
||||
uint64_t nodeCount = 0;
|
||||
recurseTreeWithOperation(countOctreeElementsOperation, &nodeCount);
|
||||
return nodeCount;
|
||||
}
|
||||
|
||||
bool Octree::countOctreeElementsOperation(const OctreeElementPointer& element, void* extraData) {
|
||||
(*(unsigned long*)extraData)++;
|
||||
(*(uint64_t*)extraData)++;
|
||||
return true; // keep going
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
|
@ -231,7 +232,7 @@ public:
|
|||
|
||||
virtual void eraseAllOctreeElements(bool createNewRoot = true);
|
||||
|
||||
void readBitstreamToTree(const unsigned char* bitstream, unsigned long int bufferSizeBytes, ReadBitstreamToTreeParams& args);
|
||||
void readBitstreamToTree(const unsigned char* bitstream, uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args);
|
||||
void deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE);
|
||||
void reaverageOctreeElements(OctreeElementPointer startElement = OctreeElementPointer());
|
||||
|
||||
|
@ -301,13 +302,13 @@ public:
|
|||
// Octree importers
|
||||
bool readFromFile(const char* filename);
|
||||
bool readFromURL(const QString& url); // will support file urls as well...
|
||||
bool readFromStream(unsigned long streamLength, QDataStream& inputStream, const QString& marketplaceID="");
|
||||
bool readSVOFromStream(unsigned long streamLength, QDataStream& inputStream);
|
||||
bool readJSONFromStream(unsigned long streamLength, QDataStream& inputStream, const QString& marketplaceID="");
|
||||
bool readFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID="");
|
||||
bool readSVOFromStream(uint64_t streamLength, QDataStream& inputStream);
|
||||
bool readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID="");
|
||||
bool readJSONFromGzippedFile(QString qFileName);
|
||||
virtual bool readFromMap(QVariantMap& entityDescription) = 0;
|
||||
|
||||
unsigned long getOctreeElementsCount();
|
||||
uint64_t getOctreeElementsCount();
|
||||
|
||||
bool getShouldReaverage() const { return _shouldReaverage; }
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ void EntityMotionState::updateServerPhysicsVariables() {
|
|||
|
||||
Transform localTransform;
|
||||
_entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
|
||||
_serverVariablesSet = true;
|
||||
_serverPosition = localTransform.getTranslation();
|
||||
_serverRotation = localTransform.getRotation();
|
||||
_serverAcceleration = _entity->getAcceleration();
|
||||
|
@ -99,18 +100,19 @@ void EntityMotionState::updateServerPhysicsVariables() {
|
|||
}
|
||||
|
||||
void EntityMotionState::handleDeactivation() {
|
||||
// copy _server data to entity
|
||||
bool success;
|
||||
_entity->setPosition(_serverPosition, success, false);
|
||||
_entity->setOrientation(_serverRotation, success, false);
|
||||
_entity->setVelocity(ENTITY_ITEM_ZERO_VEC3);
|
||||
_entity->setAngularVelocity(ENTITY_ITEM_ZERO_VEC3);
|
||||
// and also to RigidBody
|
||||
btTransform worldTrans;
|
||||
worldTrans.setOrigin(glmToBullet(_serverPosition));
|
||||
worldTrans.setRotation(glmToBullet(_serverRotation));
|
||||
_body->setWorldTransform(worldTrans);
|
||||
// no need to update velocities... should already be zero
|
||||
if (_serverVariablesSet) {
|
||||
// copy _server data to entity
|
||||
Transform localTransform = _entity->getLocalTransform();
|
||||
localTransform.setTranslation(_serverPosition);
|
||||
localTransform.setRotation(_serverRotation);
|
||||
_entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3);
|
||||
// and also to RigidBody
|
||||
btTransform worldTrans;
|
||||
worldTrans.setOrigin(glmToBullet(_entity->getPosition()));
|
||||
worldTrans.setRotation(glmToBullet(_entity->getRotation()));
|
||||
_body->setWorldTransform(worldTrans);
|
||||
// no need to update velocities... should already be zero
|
||||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
@ -339,6 +341,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
// if we've never checked before, our _lastStep will be 0, and we need to initialize our state
|
||||
if (_lastStep == 0) {
|
||||
btTransform xform = _body->getWorldTransform();
|
||||
_serverVariablesSet = true;
|
||||
_serverPosition = worldToLocal.transform(bulletToGLM(xform.getOrigin()));
|
||||
_serverRotation = worldToLocal.getRotation() * bulletToGLM(xform.getRotation());
|
||||
_serverVelocity = worldVelocityToLocal.transform(getBodyLinearVelocityGTSigma());
|
||||
|
|
|
@ -107,6 +107,7 @@ protected:
|
|||
// Meanwhile we also keep a raw EntityItem* for internal stuff where the pointer is guaranteed valid.
|
||||
EntityItem* _entity;
|
||||
|
||||
bool _serverVariablesSet { false };
|
||||
glm::vec3 _serverPosition; // in simulation-frame (not world-frame)
|
||||
glm::quat _serverRotation;
|
||||
glm::vec3 _serverVelocity;
|
||||
|
|
13
libraries/render-utils/src/Outline.slf
Normal file
13
libraries/render-utils/src/Outline.slf
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Outline.slf
|
||||
// Add outline effect based on two zbuffers : one containing the total scene z and another
|
||||
// with the z of only the objects to be outlined.
|
||||
// This is the version without the fill effect inside the silhouette.
|
||||
//
|
||||
// Created by Olivier Prat on 08/09/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
<@include Outline.slh@>
|
||||
<$main(0)$>
|
97
libraries/render-utils/src/Outline.slh
Normal file
97
libraries/render-utils/src/Outline.slh
Normal file
|
@ -0,0 +1,97 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
<!
|
||||
// Outline.slh
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Olivier Prat on 9/7/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
!>
|
||||
<@include DeferredTransform.slh@>
|
||||
<$declareDeferredFrameTransform()$>
|
||||
|
||||
<@include Outline_shared.slh@>
|
||||
|
||||
uniform outlineParamsBuffer {
|
||||
OutlineParameters params;
|
||||
};
|
||||
|
||||
uniform sampler2D sceneDepthMap;
|
||||
uniform sampler2D outlinedDepthMap;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
out vec4 outFragColor;
|
||||
|
||||
const float FAR_Z = 1.0;
|
||||
const float LINEAR_DEPTH_BIAS = 5e-3;
|
||||
const float OPACITY_EPSILON = 5e-3;
|
||||
|
||||
<@func main(IS_FILLED)@>
|
||||
|
||||
void main(void) {
|
||||
// We offset by half a texel to be centered on the depth sample. If we don't do this
|
||||
// the blur will have a different width between the left / right sides and top / bottom
|
||||
// sides of the silhouette
|
||||
vec2 halfTexel = getInvWidthHeight() / 2;
|
||||
vec2 texCoord0 = varTexCoord0+halfTexel;
|
||||
float outlinedDepth = texture(outlinedDepthMap, texCoord0).x;
|
||||
float intensity = 0.0;
|
||||
|
||||
if (outlinedDepth < FAR_Z) {
|
||||
// We're not on the far plane so we are on the outlined object, thus no outline to do!
|
||||
<@if IS_FILLED@>
|
||||
// But we need to fill the interior
|
||||
float sceneDepth = texture(sceneDepthMap, texCoord0).x;
|
||||
// Transform to linear depth for better precision
|
||||
outlinedDepth = -evalZeyeFromZdb(outlinedDepth);
|
||||
sceneDepth = -evalZeyeFromZdb(sceneDepth);
|
||||
|
||||
// Are we occluded?
|
||||
if (sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS)) {
|
||||
intensity = params._fillOpacityOccluded;
|
||||
} else {
|
||||
intensity = params._fillOpacityUnoccluded;
|
||||
}
|
||||
<@else@>
|
||||
discard;
|
||||
<@endif@>
|
||||
} else {
|
||||
float weight = 0.0;
|
||||
vec2 deltaUv = params._size / params._blurKernelSize;
|
||||
vec2 lineStartUv = texCoord0 - params._size / 2.0;
|
||||
vec2 uv;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
for (y=0 ; y<params._blurKernelSize ; y++) {
|
||||
uv = lineStartUv;
|
||||
lineStartUv.y += deltaUv.y;
|
||||
|
||||
if (uv.y>=0.0 && uv.y<=1.0) {
|
||||
for (x=0 ; x<params._blurKernelSize ; x++) {
|
||||
if (uv.x>=0.0 && uv.x<=1.0)
|
||||
{
|
||||
outlinedDepth = texture(outlinedDepthMap, uv).x;
|
||||
intensity += (outlinedDepth < FAR_Z) ? 1.0 : 0.0;
|
||||
weight += 1.f;
|
||||
}
|
||||
uv.x += deltaUv.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intensity /= weight;
|
||||
if (intensity < OPACITY_EPSILON) {
|
||||
discard;
|
||||
}
|
||||
|
||||
intensity = min(1.0, intensity / params._threshold) * params._intensity;
|
||||
}
|
||||
|
||||
outFragColor = vec4(params._color.rgb, intensity);
|
||||
}
|
||||
|
||||
<@endfunc@>
|
371
libraries/render-utils/src/OutlineEffect.cpp
Normal file
371
libraries/render-utils/src/OutlineEffect.cpp
Normal file
|
@ -0,0 +1,371 @@
|
|||
//
|
||||
// OutlineEffect.cpp
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 08/08/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "OutlineEffect.h"
|
||||
|
||||
#include "GeometryCache.h"
|
||||
|
||||
#include <render/FilterTask.h>
|
||||
#include <render/SortTask.h>
|
||||
|
||||
#include "gpu/Context.h"
|
||||
#include "gpu/StandardShaderLib.h"
|
||||
|
||||
|
||||
#include "surfaceGeometry_copyDepth_frag.h"
|
||||
#include "debug_deferred_buffer_vert.h"
|
||||
#include "debug_deferred_buffer_frag.h"
|
||||
#include "Outline_frag.h"
|
||||
#include "Outline_filled_frag.h"
|
||||
|
||||
using namespace render;
|
||||
|
||||
extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state);
|
||||
|
||||
OutlineFramebuffer::OutlineFramebuffer() {
|
||||
}
|
||||
|
||||
void OutlineFramebuffer::update(const gpu::TexturePointer& colorBuffer) {
|
||||
// If the depth buffer or size changed, we need to delete our FBOs and recreate them at the
|
||||
// new correct dimensions.
|
||||
if (_depthTexture) {
|
||||
auto newFrameSize = glm::ivec2(colorBuffer->getDimensions());
|
||||
if (_frameSize != newFrameSize) {
|
||||
_frameSize = newFrameSize;
|
||||
clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OutlineFramebuffer::clear() {
|
||||
_depthFramebuffer.reset();
|
||||
_depthTexture.reset();
|
||||
}
|
||||
|
||||
void OutlineFramebuffer::allocate() {
|
||||
|
||||
auto width = _frameSize.x;
|
||||
auto height = _frameSize.y;
|
||||
auto format = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
|
||||
|
||||
_depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(format, width, height));
|
||||
_depthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth"));
|
||||
_depthFramebuffer->setDepthStencilBuffer(_depthTexture, format);
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer OutlineFramebuffer::getDepthFramebuffer() {
|
||||
if (!_depthFramebuffer) {
|
||||
allocate();
|
||||
}
|
||||
return _depthFramebuffer;
|
||||
}
|
||||
|
||||
gpu::TexturePointer OutlineFramebuffer::getDepthTexture() {
|
||||
if (!_depthTexture) {
|
||||
allocate();
|
||||
}
|
||||
return _depthTexture;
|
||||
}
|
||||
|
||||
void DrawOutlineDepth::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
auto& inShapes = inputs.get0();
|
||||
auto& deferredFrameBuffer = inputs.get1();
|
||||
|
||||
if (!inShapes.empty()) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
ShapeKey::Builder defaultKeyBuilder;
|
||||
|
||||
if (!_outlineFramebuffer) {
|
||||
_outlineFramebuffer = std::make_shared<OutlineFramebuffer>();
|
||||
}
|
||||
_outlineFramebuffer->update(deferredFrameBuffer->getDeferredColorTexture());
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
|
||||
batch.setFramebuffer(_outlineFramebuffer->getDepthFramebuffer());
|
||||
// Clear it
|
||||
batch.clearFramebuffer(
|
||||
gpu::Framebuffer::BUFFER_DEPTH,
|
||||
vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, false);
|
||||
|
||||
// Setup camera, projection and viewport for all items
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setStateScissorRect(args->_viewport);
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
|
||||
auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
|
||||
auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
|
||||
|
||||
std::vector<ShapeKey> skinnedShapeKeys{};
|
||||
|
||||
// Iterate through all inShapes and render the unskinned
|
||||
args->_shapePipeline = shadowPipeline;
|
||||
batch.setPipeline(shadowPipeline->pipeline);
|
||||
for (auto items : inShapes) {
|
||||
if (items.first.isSkinned()) {
|
||||
skinnedShapeKeys.push_back(items.first);
|
||||
}
|
||||
else {
|
||||
renderItems(renderContext, items.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Reiterate to render the skinned
|
||||
args->_shapePipeline = shadowSkinnedPipeline;
|
||||
batch.setPipeline(shadowSkinnedPipeline->pipeline);
|
||||
for (const auto& key : skinnedShapeKeys) {
|
||||
renderItems(renderContext, inShapes.at(key));
|
||||
}
|
||||
|
||||
args->_shapePipeline = nullptr;
|
||||
args->_batch = nullptr;
|
||||
});
|
||||
|
||||
output = _outlineFramebuffer;
|
||||
} else {
|
||||
output = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DrawOutline::DrawOutline() {
|
||||
}
|
||||
|
||||
void DrawOutline::configure(const Config& config) {
|
||||
_color = config.color;
|
||||
_blurKernelSize = std::min(10, std::max(2, (int)floorf(config.width*2 + 0.5f)));
|
||||
// Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400.
|
||||
_size = config.width / 400.f;
|
||||
_fillOpacityUnoccluded = config.fillOpacityUnoccluded;
|
||||
_fillOpacityOccluded = config.fillOpacityOccluded;
|
||||
_threshold = config.glow ? 1.f : 1e-3f;
|
||||
_intensity = config.intensity * (config.glow ? 2.f : 1.f);
|
||||
}
|
||||
|
||||
void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
auto outlineFrameBuffer = inputs.get1();
|
||||
|
||||
if (outlineFrameBuffer) {
|
||||
auto sceneDepthBuffer = inputs.get2();
|
||||
const auto frameTransform = inputs.get0();
|
||||
auto outlinedDepthTexture = outlineFrameBuffer->getDepthTexture();
|
||||
auto destinationFrameBuffer = inputs.get3();
|
||||
auto framebufferSize = glm::ivec2(outlinedDepthTexture->getDimensions());
|
||||
|
||||
if (!_primaryWithoutDepthBuffer || framebufferSize!=_frameBufferSize) {
|
||||
// Failing to recreate this frame buffer when the screen has been resized creates a bug on Mac
|
||||
_primaryWithoutDepthBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth"));
|
||||
_primaryWithoutDepthBuffer->setRenderBuffer(0, destinationFrameBuffer->getRenderBuffer(0));
|
||||
_frameBufferSize = framebufferSize;
|
||||
}
|
||||
|
||||
if (sceneDepthBuffer) {
|
||||
const auto OPACITY_EPSILON = 5e-3f;
|
||||
auto pipeline = getPipeline(_fillOpacityUnoccluded>OPACITY_EPSILON || _fillOpacityOccluded>OPACITY_EPSILON);
|
||||
auto args = renderContext->args;
|
||||
{
|
||||
auto& configuration = _configuration.edit();
|
||||
configuration._color = _color;
|
||||
configuration._intensity = _intensity;
|
||||
configuration._fillOpacityUnoccluded = _fillOpacityUnoccluded;
|
||||
configuration._fillOpacityOccluded = _fillOpacityOccluded;
|
||||
configuration._threshold = _threshold;
|
||||
configuration._blurKernelSize = _blurKernelSize;
|
||||
configuration._size.x = _size * _frameBufferSize.y / _frameBufferSize.x;
|
||||
configuration._size.y = _size;
|
||||
}
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setFramebuffer(_primaryWithoutDepthBuffer);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(_frameBufferSize, args->_viewport));
|
||||
batch.setPipeline(pipeline);
|
||||
|
||||
batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration);
|
||||
batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer());
|
||||
batch.setResourceTexture(SCENE_DEPTH_SLOT, sceneDepthBuffer->getPrimaryDepthTexture());
|
||||
batch.setResourceTexture(OUTLINED_DEPTH_SLOT, outlinedDepthTexture);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
// Restore previous frame buffer
|
||||
batch.setFramebuffer(destinationFrameBuffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& DrawOutline::getPipeline(bool isFilled) {
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(Outline_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("outlineParamsBuffer", OUTLINE_PARAMS_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", FRAME_TRANSFORM_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("outlinedDepthMap", OUTLINED_DEPTH_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
|
||||
ps = gpu::Shader::createPixel(std::string(Outline_filled_frag));
|
||||
program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
_pipelineFilled = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
return isFilled ? _pipelineFilled : _pipeline;
|
||||
}
|
||||
|
||||
DebugOutline::DebugOutline() {
|
||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
DebugOutline::~DebugOutline() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (geometryCache) {
|
||||
geometryCache->releaseID(_geometryId);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugOutline::configure(const Config& config) {
|
||||
_isDisplayDepthEnabled = config.viewOutlinedDepth;
|
||||
}
|
||||
|
||||
void DebugOutline::run(const render::RenderContextPointer& renderContext, const Inputs& input) {
|
||||
const auto outlineFramebuffer = input;
|
||||
|
||||
if (_isDisplayDepthEnabled && outlineFramebuffer) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
|
||||
const auto geometryBuffer = DependencyManager::get<GeometryCache>();
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat, true);
|
||||
batch.setModelTransform(Transform());
|
||||
|
||||
batch.setPipeline(getDebugPipeline());
|
||||
batch.setResourceTexture(0, outlineFramebuffer->getDepthTexture());
|
||||
|
||||
const glm::vec4 color(1.0f, 0.5f, 0.2f, 1.0f);
|
||||
const glm::vec2 bottomLeft(-1.0f, -1.0f);
|
||||
const glm::vec2 topRight(1.0f, 1.0f);
|
||||
geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryId);
|
||||
|
||||
batch.setResourceTexture(0, nullptr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& DebugOutline::getDebugPipeline() {
|
||||
if (!_debugPipeline) {
|
||||
static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert };
|
||||
static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag };
|
||||
static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" };
|
||||
static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER);
|
||||
Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO,
|
||||
"Could not find source placeholder");
|
||||
static const std::string DEFAULT_DEPTH_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x;"
|
||||
" Zdb = 1.0-(1.0-Zdb)*100;"
|
||||
" return vec4(Zdb, Zdb, Zdb, 1.0);"
|
||||
" }"
|
||||
};
|
||||
|
||||
auto bakedFragmentShader = FRAGMENT_SHADER;
|
||||
bakedFragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), DEFAULT_DEPTH_SHADER);
|
||||
|
||||
static const auto vs = gpu::Shader::createVertex(VERTEX_SHADER);
|
||||
const auto ps = gpu::Shader::createPixel(bakedFragmentShader);
|
||||
const auto program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("depthMap", 0));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
_debugPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
return _debugPipeline;
|
||||
}
|
||||
|
||||
DrawOutlineTask::DrawOutlineTask() {
|
||||
|
||||
}
|
||||
|
||||
void DrawOutlineTask::configure(const Config& config) {
|
||||
|
||||
}
|
||||
|
||||
void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
|
||||
const auto input = inputs.get<Inputs>();
|
||||
const auto selectedMetas = inputs.getN<Inputs>(0);
|
||||
const auto shapePlumber = input.get1();
|
||||
const auto sceneFrameBuffer = inputs.getN<Inputs>(2);
|
||||
const auto primaryFramebuffer = inputs.getN<Inputs>(3);
|
||||
const auto deferredFrameTransform = inputs.getN<Inputs>(4);
|
||||
|
||||
// Prepare the ShapePipeline
|
||||
ShapePlumberPointer shapePlumberZPass = std::make_shared<ShapePlumber>();
|
||||
{
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setColorWriteMask(false, false, false, false);
|
||||
|
||||
initZPassPipelines(*shapePlumberZPass, state);
|
||||
}
|
||||
|
||||
const auto outlinedItemIDs = task.addJob<render::MetaToSubItems>("OutlineMetaToSubItemIDs", selectedMetas);
|
||||
const auto outlinedItems = task.addJob<render::IDsToBounds>("OutlineMetaToSubItems", outlinedItemIDs, true);
|
||||
|
||||
// Sort
|
||||
const auto sortedPipelines = task.addJob<render::PipelineSortShapes>("OutlinePipelineSort", outlinedItems);
|
||||
const auto sortedShapes = task.addJob<render::DepthSortShapes>("OutlineDepthSort", sortedPipelines);
|
||||
|
||||
// Draw depth of outlined objects in separate buffer
|
||||
const auto drawOutlineDepthInputs = DrawOutlineDepth::Inputs(sortedShapes, sceneFrameBuffer).asVarying();
|
||||
const auto outlinedFrameBuffer = task.addJob<DrawOutlineDepth>("OutlineDepth", drawOutlineDepthInputs, shapePlumberZPass);
|
||||
|
||||
// Draw outline
|
||||
const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlinedFrameBuffer, sceneFrameBuffer, primaryFramebuffer).asVarying();
|
||||
task.addJob<DrawOutline>("OutlineEffect", drawOutlineInputs);
|
||||
|
||||
// Debug outline
|
||||
task.addJob<DebugOutline>("OutlineDebug", outlinedFrameBuffer);
|
||||
}
|
183
libraries/render-utils/src/OutlineEffect.h
Normal file
183
libraries/render-utils/src/OutlineEffect.h
Normal file
|
@ -0,0 +1,183 @@
|
|||
//
|
||||
// OutlineEffect.h
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 08/08/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_render_utils_OutlineEffect_h
|
||||
#define hifi_render_utils_OutlineEffect_h
|
||||
|
||||
#include <render/Engine.h>
|
||||
#include "DeferredFramebuffer.h"
|
||||
#include "DeferredFrameTransform.h"
|
||||
|
||||
class OutlineFramebuffer {
|
||||
public:
|
||||
OutlineFramebuffer();
|
||||
|
||||
gpu::FramebufferPointer getDepthFramebuffer();
|
||||
gpu::TexturePointer getDepthTexture();
|
||||
|
||||
// Update the source framebuffer size which will drive the allocation of all the other resources.
|
||||
void update(const gpu::TexturePointer& colorBuffer);
|
||||
const glm::ivec2& getSourceFrameSize() const { return _frameSize; }
|
||||
|
||||
protected:
|
||||
|
||||
void clear();
|
||||
void allocate();
|
||||
|
||||
gpu::FramebufferPointer _depthFramebuffer;
|
||||
gpu::TexturePointer _depthTexture;
|
||||
|
||||
glm::ivec2 _frameSize;
|
||||
};
|
||||
|
||||
using OutlineFramebufferPointer = std::shared_ptr<OutlineFramebuffer>;
|
||||
|
||||
class DrawOutlineDepth {
|
||||
public:
|
||||
|
||||
using Inputs = render::VaryingSet2<render::ShapeBounds, DeferredFramebufferPointer>;
|
||||
// Output will contain outlined objects only z-depth texture and the input primary buffer but without the primary depth buffer
|
||||
using Outputs = OutlineFramebufferPointer;
|
||||
using JobModel = render::Job::ModelIO<DrawOutlineDepth, Inputs, Outputs>;
|
||||
|
||||
DrawOutlineDepth(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output);
|
||||
|
||||
protected:
|
||||
|
||||
render::ShapePlumberPointer _shapePlumber;
|
||||
OutlineFramebufferPointer _outlineFramebuffer;
|
||||
};
|
||||
|
||||
class DrawOutlineConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool glow MEMBER glow NOTIFY dirty)
|
||||
Q_PROPERTY(float width MEMBER width NOTIFY dirty)
|
||||
Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty)
|
||||
Q_PROPERTY(float colorR READ getColorR WRITE setColorR NOTIFY dirty)
|
||||
Q_PROPERTY(float colorG READ getColorG WRITE setColorG NOTIFY dirty)
|
||||
Q_PROPERTY(float colorB READ getColorB WRITE setColorB NOTIFY dirty)
|
||||
Q_PROPERTY(float fillOpacityUnoccluded MEMBER fillOpacityUnoccluded NOTIFY dirty)
|
||||
Q_PROPERTY(float fillOpacityOccluded MEMBER fillOpacityOccluded NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
void setColorR(float value) { color.r = value; emit dirty(); }
|
||||
float getColorR() const { return color.r; }
|
||||
|
||||
void setColorG(float value) { color.g = value; emit dirty(); }
|
||||
float getColorG() const { return color.g; }
|
||||
|
||||
void setColorB(float value) { color.b = value; emit dirty(); }
|
||||
float getColorB() const { return color.b; }
|
||||
|
||||
glm::vec3 color{ 1.f, 0.7f, 0.2f };
|
||||
float width{ 3.f };
|
||||
float intensity{ 1.f };
|
||||
float fillOpacityUnoccluded{ 0.35f };
|
||||
float fillOpacityOccluded{ 0.1f };
|
||||
bool glow{ false };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class DrawOutline {
|
||||
public:
|
||||
using Inputs = render::VaryingSet4<DeferredFrameTransformPointer, OutlineFramebufferPointer, DeferredFramebufferPointer, gpu::FramebufferPointer>;
|
||||
using Config = DrawOutlineConfig;
|
||||
using JobModel = render::Job::ModelI<DrawOutline, Inputs, Config>;
|
||||
|
||||
DrawOutline();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
|
||||
enum {
|
||||
SCENE_DEPTH_SLOT = 0,
|
||||
OUTLINED_DEPTH_SLOT,
|
||||
|
||||
OUTLINE_PARAMS_SLOT = 0,
|
||||
FRAME_TRANSFORM_SLOT
|
||||
};
|
||||
|
||||
#include "Outline_shared.slh"
|
||||
|
||||
using OutlineConfigurationBuffer = gpu::StructBuffer<OutlineParameters>;
|
||||
|
||||
const gpu::PipelinePointer& getPipeline(bool isFilled);
|
||||
|
||||
gpu::FramebufferPointer _primaryWithoutDepthBuffer;
|
||||
glm::ivec2 _frameBufferSize {0, 0};
|
||||
gpu::PipelinePointer _pipeline;
|
||||
gpu::PipelinePointer _pipelineFilled;
|
||||
OutlineConfigurationBuffer _configuration;
|
||||
glm::vec3 _color;
|
||||
float _size;
|
||||
int _blurKernelSize;
|
||||
float _intensity;
|
||||
float _fillOpacityUnoccluded;
|
||||
float _fillOpacityOccluded;
|
||||
float _threshold;
|
||||
};
|
||||
|
||||
class DebugOutlineConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool viewOutlinedDepth MEMBER viewOutlinedDepth NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
bool viewOutlinedDepth{ false };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class DebugOutline {
|
||||
public:
|
||||
using Inputs = OutlineFramebufferPointer;
|
||||
using Config = DebugOutlineConfig;
|
||||
using JobModel = render::Job::ModelI<DebugOutline, Inputs, Config>;
|
||||
|
||||
DebugOutline();
|
||||
~DebugOutline();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
|
||||
const gpu::PipelinePointer& getDebugPipeline();
|
||||
|
||||
gpu::PipelinePointer _debugPipeline;
|
||||
int _geometryId{ 0 };
|
||||
bool _isDisplayDepthEnabled{ false };
|
||||
};
|
||||
|
||||
class DrawOutlineTask {
|
||||
public:
|
||||
using Inputs = render::VaryingSet5<render::ItemBounds, render::ShapePlumberPointer, DeferredFramebufferPointer, gpu::FramebufferPointer, DeferredFrameTransformPointer>;
|
||||
using Config = render::Task::Config;
|
||||
using JobModel = render::Task::ModelI<DrawOutlineTask, Inputs, Config>;
|
||||
|
||||
DrawOutlineTask();
|
||||
|
||||
void configure(const Config& config);
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_render_utils_OutlineEffect_h
|
||||
|
||||
|
13
libraries/render-utils/src/Outline_filled.slf
Normal file
13
libraries/render-utils/src/Outline_filled.slf
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Outline_filled.slf
|
||||
// Add outline effect based on two zbuffers : one containing the total scene z and another
|
||||
// with the z of only the objects to be outlined.
|
||||
// This is the version with the fill effect inside the silhouette.
|
||||
//
|
||||
// Created by Olivier Prat on 09/07/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
<@include Outline.slh@>
|
||||
<$main(1)$>
|
28
libraries/render-utils/src/Outline_shared.slh
Normal file
28
libraries/render-utils/src/Outline_shared.slh
Normal file
|
@ -0,0 +1,28 @@
|
|||
// glsl / C++ compatible source as interface for Outline
|
||||
#ifdef __cplusplus
|
||||
# define VEC2 glm::vec2
|
||||
# define VEC3 glm::vec3
|
||||
#else
|
||||
# define VEC2 vec2
|
||||
# define VEC3 vec3
|
||||
#endif
|
||||
|
||||
struct OutlineParameters
|
||||
{
|
||||
VEC3 _color;
|
||||
float _intensity;
|
||||
|
||||
VEC2 _size;
|
||||
float _fillOpacityUnoccluded;
|
||||
float _fillOpacityOccluded;
|
||||
|
||||
float _threshold;
|
||||
int _blurKernelSize;
|
||||
float padding2;
|
||||
float padding3;
|
||||
};
|
||||
|
||||
// <@if 1@>
|
||||
// Trigger Scribe include
|
||||
// <@endif@> <!def that !>
|
||||
//
|
56
libraries/render-utils/src/PickItemsJob.cpp
Normal file
56
libraries/render-utils/src/PickItemsJob.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// PickItemsJob.cpp
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 08/08/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "PickItemsJob.h"
|
||||
|
||||
PickItemsJob::PickItemsJob(render::ItemKey::Flags validKeys, render::ItemKey::Flags excludeKeys) : _validKeys{ validKeys }, _excludeKeys{ excludeKeys } {
|
||||
}
|
||||
|
||||
void PickItemsJob::configure(const Config& config) {
|
||||
_isEnabled = config.pick;
|
||||
}
|
||||
|
||||
void PickItemsJob::run(const render::RenderContextPointer& renderContext, const PickItemsJob::Input& input, PickItemsJob::Output& output) {
|
||||
output.clear();
|
||||
if (_isEnabled) {
|
||||
float minIsectDistance = std::numeric_limits<float>::max();
|
||||
auto& itemBounds = input;
|
||||
auto item = findNearestItem(renderContext, itemBounds, minIsectDistance);
|
||||
|
||||
if (render::Item::isValidID(item.id)) {
|
||||
output.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render::ItemBound PickItemsJob::findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const {
|
||||
const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition();
|
||||
const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection();
|
||||
BoxFace face;
|
||||
glm::vec3 normal;
|
||||
float isectDistance;
|
||||
render::ItemBound nearestItem( render::Item::INVALID_ITEM_ID );
|
||||
const float minDistance = 0.2f;
|
||||
const float maxDistance = 50.f;
|
||||
render::ItemKey itemKey;
|
||||
|
||||
for (const auto& itemBound : inputs) {
|
||||
if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) {
|
||||
auto& item = renderContext->_scene->getItem(itemBound.id);
|
||||
itemKey = item.getKey();
|
||||
if (itemKey.isWorldSpace() && isectDistance>minDistance && isectDistance < minIsectDistance && isectDistance<maxDistance
|
||||
&& (itemKey._flags & _validKeys)!=0 && (itemKey._flags & _excludeKeys)==0) {
|
||||
nearestItem = itemBound;
|
||||
minIsectDistance = isectDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nearestItem;
|
||||
}
|
55
libraries/render-utils/src/PickItemsJob.h
Normal file
55
libraries/render-utils/src/PickItemsJob.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// PickItemsJob.h
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 08/08/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_render_utils_PickItemsJob_h
|
||||
#define hifi_render_utils_PickItemsJob_h
|
||||
|
||||
#include <render/Engine.h>
|
||||
#include <render/Item.h>
|
||||
|
||||
class PickItemsConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool pick MEMBER pick NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
bool pick{ false };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class PickItemsJob {
|
||||
|
||||
public:
|
||||
|
||||
using Config = PickItemsConfig;
|
||||
using Input = render::ItemBounds;
|
||||
using Output = render::ItemBounds;
|
||||
using JobModel = render::Job::ModelIO<PickItemsJob, Input, Output, Config>;
|
||||
|
||||
PickItemsJob(render::ItemKey::Flags validKeys = render::ItemKey::Builder().withTypeMeta().withTypeShape().build()._flags, render::ItemKey::Flags excludeKeys = 0);
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const PickItemsJob::Input& input, PickItemsJob::Output& output);
|
||||
|
||||
private:
|
||||
|
||||
render::ItemKey::Flags _validKeys;
|
||||
render::ItemKey::Flags _excludeKeys;
|
||||
bool _isEnabled{ false };
|
||||
|
||||
render::ItemBound findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const;
|
||||
};
|
||||
|
||||
#endif // hifi_render_utils_PickItemsJob_h
|
||||
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
#include "AntialiasingEffect.h"
|
||||
#include "ToneMappingEffect.h"
|
||||
#include "SubsurfaceScattering.h"
|
||||
#include "OutlineEffect.h"
|
||||
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
|
@ -93,6 +94,15 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
// draw a stencil mask in hidden regions of the framebuffer.
|
||||
task.addJob<PrepareStencil>("PrepareStencil", primaryFramebuffer);
|
||||
|
||||
// Select items that need to be outlined
|
||||
const auto selectionName = "contextOverlayHighlightList";
|
||||
const auto selectMetaInput = SelectItems::Inputs(metas, Varying()).asVarying();
|
||||
const auto selectedMetas = task.addJob<SelectItems>("PassTestMetaSelection", selectMetaInput, selectionName);
|
||||
const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas).asVarying();
|
||||
const auto selectedMetasAndOpaques = task.addJob<SelectItems>("PassTestOpaqueSelection", selectMetaAndOpaqueInput, selectionName);
|
||||
const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques).asVarying();
|
||||
const auto selectedItems = task.addJob<SelectItems>("PassTestTransparentSelection", selectItemInput, selectionName);
|
||||
|
||||
// Render opaque objects in DeferredBuffer
|
||||
const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).asVarying();
|
||||
task.addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaqueInputs, shapePlumber);
|
||||
|
@ -159,10 +169,15 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
const auto toneAndPostRangeTimer = task.addJob<BeginGPURangeTimer>("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing");
|
||||
|
||||
// Lighting Buffer ready for tone mapping
|
||||
const auto toneMappingInputs = render::Varying(ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer));
|
||||
const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying();
|
||||
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
|
||||
|
||||
{ // Debug the bounds of the rendered items, still look at the zbuffer
|
||||
const auto outlineRangeTimer = task.addJob<BeginGPURangeTimer>("BeginOutlineRangeTimer", "Outline");
|
||||
const auto outlineInputs = DrawOutlineTask::Inputs(selectedItems, shapePlumber, deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying();
|
||||
task.addJob<DrawOutlineTask>("DrawOutline", outlineInputs);
|
||||
task.addJob<EndGPURangeTimer>("EndOutlineRangeTimer", outlineRangeTimer);
|
||||
|
||||
{ // DEbug the bounds of the rendered items, still look at the zbuffer
|
||||
task.addJob<DrawBounds>("DrawMetaBounds", metas);
|
||||
task.addJob<DrawBounds>("DrawOpaqueBounds", opaques);
|
||||
task.addJob<DrawBounds>("DrawTransparentBounds", transparents);
|
||||
|
@ -212,8 +227,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
task.addJob<DebugZoneLighting>("DrawZoneStack", deferredFrameTransform);
|
||||
|
||||
// Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true
|
||||
const auto selectedMetas = task.addJob<SelectItems>("PassTestSelection", metas, "contextOverlayHighlightList");
|
||||
task.addJob<DrawBounds>("DrawSelectionBounds", selectedMetas);
|
||||
task.addJob<DrawBounds>("DrawSelectionBounds", selectedItems);
|
||||
}
|
||||
|
||||
// AA job to be revisited
|
||||
|
|
|
@ -21,19 +21,15 @@
|
|||
#include "render/DrawTask.h"
|
||||
|
||||
#include "model_vert.h"
|
||||
#include "model_shadow_vert.h"
|
||||
#include "model_normal_map_vert.h"
|
||||
#include "model_lightmap_vert.h"
|
||||
#include "model_lightmap_normal_map_vert.h"
|
||||
#include "skin_model_vert.h"
|
||||
#include "skin_model_shadow_vert.h"
|
||||
#include "skin_model_normal_map_vert.h"
|
||||
|
||||
#include "model_shadow_fade_vert.h"
|
||||
#include "model_lightmap_fade_vert.h"
|
||||
#include "model_lightmap_normal_map_fade_vert.h"
|
||||
#include "skin_model_fade_vert.h"
|
||||
#include "skin_model_shadow_fade_vert.h"
|
||||
#include "skin_model_normal_map_fade_vert.h"
|
||||
|
||||
#include "simple_vert.h"
|
||||
|
@ -50,7 +46,6 @@
|
|||
|
||||
#include "model_frag.h"
|
||||
#include "model_unlit_frag.h"
|
||||
#include "model_shadow_frag.h"
|
||||
#include "model_normal_map_frag.h"
|
||||
#include "model_normal_specular_map_frag.h"
|
||||
#include "model_specular_map_frag.h"
|
||||
|
@ -59,7 +54,6 @@
|
|||
#include "model_normal_map_fade_vert.h"
|
||||
|
||||
#include "model_fade_frag.h"
|
||||
#include "model_shadow_fade_frag.h"
|
||||
#include "model_unlit_fade_frag.h"
|
||||
#include "model_normal_map_fade_frag.h"
|
||||
#include "model_normal_specular_map_fade_frag.h"
|
||||
|
@ -95,6 +89,17 @@
|
|||
#include "overlay3D_model_unlit_frag.h"
|
||||
#include "overlay3D_model_translucent_unlit_frag.h"
|
||||
|
||||
#include "model_shadow_vert.h"
|
||||
#include "skin_model_shadow_vert.h"
|
||||
|
||||
#include "model_shadow_frag.h"
|
||||
#include "skin_model_shadow_frag.h"
|
||||
|
||||
#include "model_shadow_fade_vert.h"
|
||||
#include "skin_model_shadow_fade_vert.h"
|
||||
|
||||
#include "model_shadow_fade_frag.h"
|
||||
#include "skin_model_shadow_fade_frag.h"
|
||||
|
||||
using namespace render;
|
||||
using namespace std::placeholders;
|
||||
|
@ -102,6 +107,7 @@ using namespace std::placeholders;
|
|||
void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest = false);
|
||||
void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter);
|
||||
void initForwardPipelines(ShapePlumber& plumber);
|
||||
void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state);
|
||||
|
||||
void addPlumberPipeline(ShapePlumber& plumber,
|
||||
const ShapeKey& key, const gpu::ShaderPointer& vertex, const gpu::ShaderPointer& pixel,
|
||||
|
@ -566,3 +572,33 @@ void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderAr
|
|||
pipeline.locations->lightAmbientMapUnit);
|
||||
}
|
||||
}
|
||||
|
||||
void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state) {
|
||||
auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert));
|
||||
auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag));
|
||||
gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel);
|
||||
shapePlumber.addPipeline(
|
||||
ShapeKey::Filter::Builder().withoutSkinned().withoutFade(),
|
||||
modelProgram, state);
|
||||
|
||||
auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert));
|
||||
auto skinPixel = gpu::Shader::createPixel(std::string(skin_model_shadow_frag));
|
||||
gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, skinPixel);
|
||||
shapePlumber.addPipeline(
|
||||
ShapeKey::Filter::Builder().withSkinned().withoutFade(),
|
||||
skinProgram, state);
|
||||
|
||||
auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_shadow_fade_vert));
|
||||
auto modelFadePixel = gpu::Shader::createPixel(std::string(model_shadow_fade_frag));
|
||||
gpu::ShaderPointer modelFadeProgram = gpu::Shader::createProgram(modelFadeVertex, modelFadePixel);
|
||||
shapePlumber.addPipeline(
|
||||
ShapeKey::Filter::Builder().withoutSkinned().withFade(),
|
||||
modelFadeProgram, state);
|
||||
|
||||
auto skinFadeVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_fade_vert));
|
||||
auto skinFadePixel = gpu::Shader::createPixel(std::string(skin_model_shadow_fade_frag));
|
||||
gpu::ShaderPointer skinFadeProgram = gpu::Shader::createProgram(skinFadeVertex, skinFadePixel);
|
||||
shapePlumber.addPipeline(
|
||||
ShapeKey::Filter::Builder().withSkinned().withFade(),
|
||||
skinFadeProgram, state);
|
||||
}
|
||||
|
|
|
@ -22,20 +22,10 @@
|
|||
#include "DeferredLightingEffect.h"
|
||||
#include "FramebufferCache.h"
|
||||
|
||||
#include "model_shadow_vert.h"
|
||||
#include "skin_model_shadow_vert.h"
|
||||
|
||||
#include "model_shadow_frag.h"
|
||||
#include "skin_model_shadow_frag.h"
|
||||
|
||||
#include "model_shadow_fade_vert.h"
|
||||
#include "skin_model_shadow_fade_vert.h"
|
||||
|
||||
#include "model_shadow_fade_frag.h"
|
||||
#include "skin_model_shadow_fade_frag.h"
|
||||
|
||||
using namespace render;
|
||||
|
||||
extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state);
|
||||
|
||||
void RenderShadowMap::run(const render::RenderContextPointer& renderContext,
|
||||
const render::ShapeBounds& inShapes) {
|
||||
assert(renderContext->args);
|
||||
|
@ -108,33 +98,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
|||
state->setCullMode(gpu::State::CULL_BACK);
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
|
||||
auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert));
|
||||
auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag));
|
||||
gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel);
|
||||
shapePlumber->addPipeline(
|
||||
ShapeKey::Filter::Builder().withoutSkinned().withoutFade(),
|
||||
modelProgram, state);
|
||||
|
||||
auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert));
|
||||
auto skinPixel = gpu::Shader::createPixel(std::string(skin_model_shadow_frag));
|
||||
gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, skinPixel);
|
||||
shapePlumber->addPipeline(
|
||||
ShapeKey::Filter::Builder().withSkinned().withoutFade(),
|
||||
skinProgram, state);
|
||||
|
||||
auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_shadow_fade_vert));
|
||||
auto modelFadePixel = gpu::Shader::createPixel(std::string(model_shadow_fade_frag));
|
||||
gpu::ShaderPointer modelFadeProgram = gpu::Shader::createProgram(modelFadeVertex, modelFadePixel);
|
||||
shapePlumber->addPipeline(
|
||||
ShapeKey::Filter::Builder().withoutSkinned().withFade(),
|
||||
modelFadeProgram, state);
|
||||
|
||||
auto skinFadeVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_fade_vert));
|
||||
auto skinFadePixel = gpu::Shader::createPixel(std::string(skin_model_shadow_fade_frag));
|
||||
gpu::ShaderPointer skinFadeProgram = gpu::Shader::createProgram(skinFadeVertex, skinFadePixel);
|
||||
shapePlumber->addPipeline(
|
||||
ShapeKey::Filter::Builder().withSkinned().withFade(),
|
||||
skinFadeProgram, state);
|
||||
initZPassPipelines(*shapePlumber, state);
|
||||
}
|
||||
|
||||
const auto cachedMode = task.addJob<RenderShadowSetup>("ShadowSetup");
|
||||
|
|
20
libraries/render-utils/src/surfaceGeometry_copyDepth.slf
Normal file
20
libraries/render-utils/src/surfaceGeometry_copyDepth.slf
Normal file
|
@ -0,0 +1,20 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// Created by Olivier Prat on 08/08/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
uniform sampler2D depthMap;
|
||||
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x;
|
||||
outFragColor = vec4(Zdb, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <LogHandler.h>
|
||||
|
||||
#include "DrawTask.h"
|
||||
#include "Logging.h"
|
||||
|
||||
|
@ -48,12 +50,14 @@ void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, cons
|
|||
if (args->_shapePipeline) {
|
||||
args->_shapePipeline->prepareShapeItem(args, key, item);
|
||||
item.render(args);
|
||||
}
|
||||
}
|
||||
args->_shapePipeline = nullptr;
|
||||
} else if (key.hasOwnPipeline()) {
|
||||
item.render(args);
|
||||
} else {
|
||||
qCDebug(renderlogging) << "Item could not be rendered with invalid key" << key;
|
||||
static QString repeatedCouldNotBeRendered = LogHandler::getInstance().addRepeatedMessageRegex(
|
||||
"Item could not be rendered with invalid key.*");
|
||||
}
|
||||
args->_itemShapeKey = 0;
|
||||
}
|
||||
|
@ -62,7 +66,7 @@ void render::renderShapes(const RenderContextPointer& renderContext,
|
|||
const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems, const ShapeKey& globalKey) {
|
||||
auto& scene = renderContext->_scene;
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
|
||||
int numItemsToDraw = (int)inItems.size();
|
||||
if (maxDrawnItems != -1) {
|
||||
numItemsToDraw = glm::min(numItemsToDraw, maxDrawnItems);
|
||||
|
@ -104,6 +108,8 @@ void render::renderStateSortShapes(const RenderContextPointer& renderContext,
|
|||
} else if (key.hasOwnPipeline()) {
|
||||
ownPipelineBucket.push_back( std::make_tuple(item, key) );
|
||||
} else {
|
||||
static QString repeatedCouldNotBeRendered = LogHandler::getInstance().addRepeatedMessageRegex(
|
||||
"Item could not be rendered with invalid key.*");
|
||||
qCDebug(renderlogging) << "Item could not be rendered with invalid key" << key;
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +119,7 @@ void render::renderStateSortShapes(const RenderContextPointer& renderContext,
|
|||
for (auto& pipelineKey : sortedPipelines) {
|
||||
auto& bucket = sortedShapes[pipelineKey];
|
||||
args->_shapePipeline = shapeContext->pickPipeline(args, pipelineKey);
|
||||
if (!args->_shapePipeline) {
|
||||
if (!args->_shapePipeline) {
|
||||
continue;
|
||||
}
|
||||
args->_itemShapeKey = pipelineKey._flags.to_ulong();
|
||||
|
@ -182,7 +188,7 @@ void DrawBounds::run(const RenderContextPointer& renderContext,
|
|||
if (!_drawBuffer) {
|
||||
_drawBuffer = std::make_shared<gpu::Buffer>(sizeOfItemBound);
|
||||
}
|
||||
|
||||
|
||||
_drawBuffer->setData(numItems * sizeOfItemBound, (const gpu::Byte*) items.data());
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
|
|
|
@ -51,13 +51,20 @@ void SliceItems::run(const RenderContextPointer& renderContext, const ItemBounds
|
|||
|
||||
}
|
||||
|
||||
void SelectItems::run(const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) {
|
||||
void SelectItems::run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems) {
|
||||
auto selection = renderContext->_scene->getSelection(_name);
|
||||
const auto& selectedItems = selection.getItems();
|
||||
outItems.clear();
|
||||
const auto& inItems = inputs.get0();
|
||||
const auto itemsToAppend = inputs[1];
|
||||
|
||||
if (itemsToAppend.isNull()) {
|
||||
outItems.clear();
|
||||
} else {
|
||||
outItems = itemsToAppend.get<ItemBounds>();
|
||||
}
|
||||
|
||||
if (!selectedItems.empty()) {
|
||||
outItems.reserve(selectedItems.size());
|
||||
outItems.reserve(outItems.size()+selectedItems.size());
|
||||
|
||||
for (auto src : inItems) {
|
||||
if (selection.contains(src.id)) {
|
||||
|
@ -111,3 +118,21 @@ void MetaToSubItems::run(const RenderContextPointer& renderContext, const ItemBo
|
|||
}
|
||||
}
|
||||
|
||||
void IDsToBounds::run(const RenderContextPointer& renderContext, const ItemIDs& inItems, ItemBounds& outItems) {
|
||||
auto& scene = renderContext->_scene;
|
||||
|
||||
// Now we have a selection of items to render
|
||||
outItems.clear();
|
||||
|
||||
if (!_disableAABBs) {
|
||||
for (auto id : inItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
|
||||
outItems.emplace_back(ItemBound{ id, item.getBound() });
|
||||
}
|
||||
} else {
|
||||
for (auto id : inItems) {
|
||||
outItems.emplace_back(ItemBound{ id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue