diff --git a/CMakeLists.txt b/CMakeLists.txt index e67002b89d..be513abddb 100644 --- a/CMakeLists.txt +++ b/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() diff --git a/cmake/compiler.cmake b/cmake/compiler.cmake new file mode 100644 index 0000000000..968a65a6dd --- /dev/null +++ b/cmake/compiler.cmake @@ -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 () \ No newline at end of file diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index c98aa8a04a..97508be0c5 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -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) diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index 01650a432d..0d66b365a2 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -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= -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=/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") diff --git a/cmake/init.cmake b/cmake/init.cmake new file mode 100644 index 0000000000..75fb3a4b52 --- /dev/null +++ b/cmake/init.cmake @@ -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 () diff --git a/cmake/macros/SetupHifiClientServerPlugin.cmake b/cmake/macros/SetupHifiClientServerPlugin.cmake index 37b000efb5..bc66484c30 100644 --- a/cmake/macros/SetupHifiClientServerPlugin.cmake +++ b/cmake/macros/SetupHifiClientServerPlugin.cmake @@ -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") diff --git a/cmake/macros/SetupHifiPlugin.cmake b/cmake/macros/SetupHifiPlugin.cmake index 7e56ea3db2..023c7603dc 100644 --- a/cmake/macros/SetupHifiPlugin.cmake +++ b/cmake/macros/SetupHifiPlugin.cmake @@ -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") diff --git a/cmake/macros/SetupQt.cmake b/cmake/macros/SetupQt.cmake new file mode 100644 index 0000000000..b2a89f81e5 --- /dev/null +++ b/cmake/macros/SetupQt.cmake @@ -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() \ No newline at end of file diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml index 0460b5b16a..13a435c4e5 100644 --- a/interface/resources/qml/controls/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -19,6 +19,7 @@ Flickable { signal newViewRequestedCallback(var request) signal loadingChangedCallback(var loadRequest) + pressDelay: 300 boundsBehavior: Flickable.StopAtBounds @@ -167,4 +168,11 @@ Flickable { playing: visible z: 10000 } + + MouseArea { + anchors.fill: parent + onWheel: { + flick.flick(0, wheel.angleDelta.y*10) + } + } } diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 04f106d0f9..14a84311aa 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -40,12 +40,20 @@ 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.getKeyFilePathIfExists(); + commerce.account(); } } @@ -203,7 +211,7 @@ Rectangle { commerce.getLoginStatus(); } } - + HifiWallet.NeedsLogIn { id: needsLogIn; visible: root.activeView === "needsLogIn"; @@ -239,7 +247,7 @@ Rectangle { } } } - + // // "WALLET NOT SET UP" START // @@ -250,7 +258,7 @@ Rectangle { anchors.bottom: parent.bottom; anchors.left: parent.left; anchors.right: parent.right; - + RalewayRegular { id: notSetUpText; text: "Your wallet isn't set up.

Set up your Wallet (no credit card necessary) to claim your free HFC " + @@ -281,7 +289,7 @@ Rectangle { anchors.left: parent.left; anchors.bottom: parent.bottom; anchors.bottomMargin: 24; - + // "Cancel" button HifiControlsUit.Button { id: cancelButton; diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 383223a49c..697249f740 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -35,12 +35,20 @@ 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.getKeyFilePathIfExists(); + commerce.account(); } } @@ -174,7 +182,7 @@ Rectangle { commerce.getLoginStatus(); } } - + HifiWallet.NeedsLogIn { id: needsLogIn; visible: root.activeView === "needsLogIn"; @@ -210,7 +218,7 @@ Rectangle { } } } - + // // "WALLET NOT SET UP" START // @@ -221,7 +229,7 @@ Rectangle { anchors.bottom: parent.bottom; anchors.left: parent.left; anchors.right: parent.right; - + RalewayRegular { id: notSetUpText; text: "Your wallet isn't set up.

Set up your Wallet (no credit card necessary) to claim your free HFC " + @@ -252,7 +260,7 @@ Rectangle { anchors.left: parent.left; anchors.bottom: parent.bottom; anchors.bottomMargin: 24; - + // "Cancel" button HifiControlsUit.Button { id: cancelButton; @@ -309,7 +317,7 @@ Rectangle { anchors.topMargin: 8; anchors.bottom: actionButtonsContainer.top; anchors.bottomMargin: 8; - + // // FILTER BAR START // @@ -383,7 +391,7 @@ Rectangle { itemHref: root_file_url; anchors.topMargin: 12; anchors.bottomMargin: 12; - + Connections { onSendToPurchases: { if (msg.method === 'purchases_itemInfoClicked') { @@ -402,7 +410,7 @@ Rectangle { anchors.left: parent.left; anchors.bottom: parent.bottom; width: parent.width; - + // Explanitory text RalewayRegular { id: haventPurchasedYet; diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 2faa08064d..a0664f4f9b 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -31,6 +31,13 @@ #include #include +// 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 +#endif + static const char* KEY_FILE = "hifikey"; static const char* IMAGE_FILE = "hifi_image"; // eventually this will live in keyfile diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 503daa177d..cb90160cfe 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -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(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(argvExtended.data()), startupTime, runningMarkerExisted); +#if 0 // If we failed the OpenGLVersion check, log it. - if (override) { + if (overrideGLcheck) { auto accountManager = DependencyManager::get(); 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 }; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 4e8f2ad572..444147aeba 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -649,7 +649,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"); diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index aef5c73fa3..caeba37839 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -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]); diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index 23e0893644..bcf2b16ac6 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -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; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 9f4a9a3c4a..d754d59721 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -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() { diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 7349e9147e..3328076911 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -91,20 +91,27 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi qCWarning(entitiesrenderer) << "Bad particle properties"; } } - if (_particleProperties != newParticleProperties) { + + if (resultWithReadLock([&]{ return _particleProperties != newParticleProperties; })) { _timeUntilNextEmit = 0; - _particleProperties = newParticleProperties; + withWriteLock([&]{ + _particleProperties = newParticleProperties; + }); } _emitting = entity->getIsEmitting(); - if (_particleProperties.textures.isEmpty()) { + bool hasTexture = resultWithReadLock([&]{ return _particleProperties.textures.isEmpty(); }); + if (hasTexture) { if (_networkTexture) { withWriteLock([&] { _networkTexture.reset(); }); } } else { - if (!_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures)) { + bool textureNeedsUpdate = resultWithReadLock([&]{ + return !_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures); + }); + if (textureNeedsUpdate) { withWriteLock([&] { _networkTexture = DependencyManager::get()->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, 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(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(); } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index e4c1d4be13..be2641c0c9 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -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; diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index d39139257b..8757bcbb0f 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -30,14 +30,11 @@ TextEntityRenderer::TextEntityRenderer(const EntityItemPointer& entity) : } - -void TextEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) { +TextEntityRenderer::~TextEntityRenderer() { auto geometryCache = DependencyManager::get(); if (_geometryID && geometryCache) { geometryCache->releaseID(_geometryID); } - delete _textRenderer; - _textRenderer = nullptr; } bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index 1d8227069e..b0a72cf253 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -24,14 +24,13 @@ class TextEntityRenderer : public TypedEntityRenderer { using Pointer = std::shared_ptr; 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 _textRenderer; bool _faceCamera; glm::vec3 _dimensions; glm::vec3 _textColor; diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index bc6830e1a7..9bbf4323da 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -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); diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 6a3791f77e..9c0fd0ef95 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -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; diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index ab91ca0902..28982703dd 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -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; } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 7758ddaf49..943b8148ef 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -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); } } } diff --git a/libraries/render-utils/src/Outline.slf b/libraries/render-utils/src/Outline.slf new file mode 100644 index 0000000000..68ef870cba --- /dev/null +++ b/libraries/render-utils/src/Outline.slf @@ -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)$> diff --git a/libraries/render-utils/src/Outline.slh b/libraries/render-utils/src/Outline.slh new file mode 100644 index 0000000000..ac56e4c95c --- /dev/null +++ b/libraries/render-utils/src/Outline.slh @@ -0,0 +1,97 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> + +<@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=0.0 && uv.y<=1.0) { + for (x=0 ; 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@> diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp new file mode 100644 index 0000000000..d5b3b1c3bb --- /dev/null +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -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 +#include + +#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->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 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()->allocateID(); +} + +DebugOutline::~DebugOutline() { + auto geometryCache = DependencyManager::get(); + 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(); + + 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(); + 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(); + const auto selectedMetas = inputs.getN(0); + const auto shapePlumber = input.get1(); + const auto sceneFrameBuffer = inputs.getN(2); + const auto primaryFramebuffer = inputs.getN(3); + const auto deferredFrameTransform = inputs.getN(4); + + // Prepare the ShapePipeline + ShapePlumberPointer shapePlumberZPass = std::make_shared(); + { + auto state = std::make_shared(); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setColorWriteMask(false, false, false, false); + + initZPassPipelines(*shapePlumberZPass, state); + } + + const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", selectedMetas); + const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs, true); + + // Sort + const auto sortedPipelines = task.addJob("OutlinePipelineSort", outlinedItems); + const auto sortedShapes = task.addJob("OutlineDepthSort", sortedPipelines); + + // Draw depth of outlined objects in separate buffer + const auto drawOutlineDepthInputs = DrawOutlineDepth::Inputs(sortedShapes, sceneFrameBuffer).asVarying(); + const auto outlinedFrameBuffer = task.addJob("OutlineDepth", drawOutlineDepthInputs, shapePlumberZPass); + + // Draw outline + const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlinedFrameBuffer, sceneFrameBuffer, primaryFramebuffer).asVarying(); + task.addJob("OutlineEffect", drawOutlineInputs); + + // Debug outline + task.addJob("OutlineDebug", outlinedFrameBuffer); +} diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h new file mode 100644 index 0000000000..36dc59f29e --- /dev/null +++ b/libraries/render-utils/src/OutlineEffect.h @@ -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 +#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; + +class DrawOutlineDepth { +public: + + using Inputs = render::VaryingSet2; + // 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(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; + using Config = DrawOutlineConfig; + using JobModel = render::Job::ModelI; + + 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; + + 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(); + ~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; + using Config = render::Task::Config; + using JobModel = render::Task::ModelI; + + DrawOutlineTask(); + + void configure(const Config& config); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); + +}; + +#endif // hifi_render_utils_OutlineEffect_h + + diff --git a/libraries/render-utils/src/Outline_filled.slf b/libraries/render-utils/src/Outline_filled.slf new file mode 100644 index 0000000000..aaa3396bac --- /dev/null +++ b/libraries/render-utils/src/Outline_filled.slf @@ -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)$> diff --git a/libraries/render-utils/src/Outline_shared.slh b/libraries/render-utils/src/Outline_shared.slh new file mode 100644 index 0000000000..902bbd20ad --- /dev/null +++ b/libraries/render-utils/src/Outline_shared.slh @@ -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@> +// diff --git a/libraries/render-utils/src/PickItemsJob.cpp b/libraries/render-utils/src/PickItemsJob.cpp new file mode 100644 index 0000000000..48ba605d7b --- /dev/null +++ b/libraries/render-utils/src/PickItemsJob.cpp @@ -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::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 +#include + +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(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 + + diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 27abc8c282..c67a1c7875 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -40,6 +40,7 @@ #include "AntialiasingEffect.h" #include "ToneMappingEffect.h" #include "SubsurfaceScattering.h" +#include "OutlineEffect.h" #include @@ -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", 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("PassTestMetaSelection", selectMetaInput, selectionName); + const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas).asVarying(); + const auto selectedMetasAndOpaques = task.addJob("PassTestOpaqueSelection", selectMetaAndOpaqueInput, selectionName); + const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques).asVarying(); + const auto selectedItems = task.addJob("PassTestTransparentSelection", selectItemInput, selectionName); + // Render opaque objects in DeferredBuffer const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).asVarying(); task.addJob("DrawOpaqueDeferred", opaqueInputs, shapePlumber); @@ -159,10 +169,15 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto toneAndPostRangeTimer = task.addJob("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("ToneMapping", toneMappingInputs); - { // Debug the bounds of the rendered items, still look at the zbuffer + const auto outlineRangeTimer = task.addJob("BeginOutlineRangeTimer", "Outline"); + const auto outlineInputs = DrawOutlineTask::Inputs(selectedItems, shapePlumber, deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying(); + task.addJob("DrawOutline", outlineInputs); + task.addJob("EndOutlineRangeTimer", outlineRangeTimer); + + { // DEbug the bounds of the rendered items, still look at the zbuffer task.addJob("DrawMetaBounds", metas); task.addJob("DrawOpaqueBounds", opaques); task.addJob("DrawTransparentBounds", transparents); @@ -212,8 +227,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawZoneStack", deferredFrameTransform); // Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true - const auto selectedMetas = task.addJob("PassTestSelection", metas, "contextOverlayHighlightList"); - task.addJob("DrawSelectionBounds", selectedMetas); + task.addJob("DrawSelectionBounds", selectedItems); } // AA job to be revisited diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 0353e10407..7f644add72 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -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); +} diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index ac80c03873..d32857bc65 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -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("ShadowSetup"); diff --git a/libraries/render-utils/src/surfaceGeometry_copyDepth.slf b/libraries/render-utils/src/surfaceGeometry_copyDepth.slf new file mode 100644 index 0000000000..9db8cdbb01 --- /dev/null +++ b/libraries/render-utils/src/surfaceGeometry_copyDepth.slf @@ -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); +} + diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 8372231597..710507bd79 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #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(sizeOfItemBound); } - + _drawBuffer->setData(numItems * sizeOfItemBound, (const gpu::Byte*) items.data()); gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { diff --git a/libraries/render/src/render/FilterTask.cpp b/libraries/render/src/render/FilterTask.cpp index 19953f3399..e0298c2a44 100644 --- a/libraries/render/src/render/FilterTask.cpp +++ b/libraries/render/src/render/FilterTask.cpp @@ -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(); + } 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 }); + } + } +} diff --git a/libraries/render/src/render/FilterTask.h b/libraries/render/src/render/FilterTask.h index 1c4611ee9f..1e023a8bb9 100644 --- a/libraries/render/src/render/FilterTask.h +++ b/libraries/render/src/render/FilterTask.h @@ -113,22 +113,23 @@ namespace render { // Keep items belonging to the job selection class SelectItems { public: - using JobModel = Job::ModelIO; + using Inputs = VaryingSet2; + using JobModel = Job::ModelIO; std::string _name; SelectItems(const Selection::Name& name) : _name(name) {} - void run(const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); + void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); }; // Same as SelectItems but reorder the output to match the selection order class SelectSortItems { public: using JobModel = Job::ModelIO; - + std::string _name; SelectSortItems(const Selection::Name& name) : _name(name) {} - + void run(const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems); }; @@ -142,6 +143,19 @@ namespace render { void run(const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemIDs& outItems); }; + // From item IDs build item bounds + class IDsToBounds { + public: + using JobModel = Job::ModelIO; + + IDsToBounds(bool disableAABBs = false) : _disableAABBs(disableAABBs) {} + + void run(const RenderContextPointer& renderContext, const ItemIDs& inItems, ItemBounds& outItems); + + private: + bool _disableAABBs{ false }; + }; + } #endif // hifi_render_FilterTask_h; \ No newline at end of file diff --git a/libraries/render/src/task/Varying.h b/libraries/render/src/task/Varying.h index 0144801701..9ce234061e 100644 --- a/libraries/render/src/task/Varying.h +++ b/libraries/render/src/task/Varying.h @@ -238,6 +238,24 @@ public: const T5& get5() const { return std::get<5>((*this)).template get(); } T5& edit5() { return std::get<5>((*this)).template edit(); } + virtual Varying operator[] (uint8_t index) const { + switch (index) { + default: + return std::get<0>((*this)); + case 1: + return std::get<1>((*this)); + case 2: + return std::get<2>((*this)); + case 3: + return std::get<3>((*this)); + case 4: + return std::get<4>((*this)); + case 5: + return std::get<5>((*this)); + }; + } + virtual uint8_t length() const { return 6; } + Varying asVarying() const { return Varying((*this)); } }; diff --git a/libraries/shared/src/Interpolate.cpp b/libraries/shared/src/Interpolate.cpp index 35c164f0f2..cd595ee1ed 100644 --- a/libraries/shared/src/Interpolate.cpp +++ b/libraries/shared/src/Interpolate.cpp @@ -69,7 +69,7 @@ float Interpolate::simpleNonLinearBlend(float fraction) { } float Interpolate::calculateFadeRatio(quint64 start) { - const float FADE_TIME = 1.0f; + const float FADE_TIME = 0.5f; float t = 2.0f * std::min(((float)(usecTimestampNow() - start)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-powf(2.0f, -10.0f * (t - 1.0f)) + 2.0f); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index d29dc7a0f9..d03842d45a 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -528,6 +528,12 @@ void OffscreenQmlSurface::create() { connect(_quickWindow, &QQuickWindow::focusObjectChanged, this, &OffscreenQmlSurface::onFocusObjectChanged); + // acquireEngine interrogates the GL context, so we need to have the context current here + if (!_canvas->makeCurrent()) { + qFatal("Failed to make context current for QML Renderer"); + return; + } + // Create a QML engine. auto qmlEngine = acquireEngine(_quickWindow); @@ -540,11 +546,6 @@ void OffscreenQmlSurface::create() { // FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper // Find a way to flag older scripts using this mechanism and wanr that this is deprecated _qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, _qmlContext)); - - if (!_canvas->makeCurrent()) { - qWarning("Failed to make context current for QML Renderer"); - return; - } _renderControl->initialize(_canvas->getContext()); // When Quick says there is a need to render, we will not render immediately. Instead, @@ -639,7 +640,7 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) { _qmlContext->setBaseUrl(baseUrl); } -void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function f) { +void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function onQmlLoadedCallback) { if (QThread::currentThread() != thread()) { qCWarning(uiLogging) << "Called load on a non-surface thread"; } @@ -659,28 +660,28 @@ void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std auto qmlComponent = new QQmlComponent(_qmlContext->engine(), finalQmlSource, QQmlComponent::PreferSynchronous); if (qmlComponent->isLoading()) { connect(qmlComponent, &QQmlComponent::statusChanged, this, - [this, qmlComponent, targetContext, f](QQmlComponent::Status) { - finishQmlLoad(qmlComponent, targetContext, f); + [this, qmlComponent, targetContext, onQmlLoadedCallback](QQmlComponent::Status) { + finishQmlLoad(qmlComponent, targetContext, onQmlLoadedCallback); }); return; } - finishQmlLoad(qmlComponent, targetContext, f); + finishQmlLoad(qmlComponent, targetContext, onQmlLoadedCallback); } -void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function f) { - load(qmlSource, true, f); +void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function onQmlLoadedCallback) { + load(qmlSource, true, onQmlLoadedCallback); } -void OffscreenQmlSurface::load(const QUrl& qmlSource, std::function f) { - load(qmlSource, false, f); +void OffscreenQmlSurface::load(const QUrl& qmlSource, std::function onQmlLoadedCallback) { + load(qmlSource, false, onQmlLoadedCallback); } void OffscreenQmlSurface::clearCache() { _qmlContext->engine()->clearComponentCache(); } -void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function f) { +void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function onQmlLoadedCallback) { disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0); if (qmlComponent->isError()) { for (const auto& error : qmlComponent->errors()) { @@ -690,7 +691,6 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext return; } - QObject* newObject = qmlComponent->beginCreate(qmlContext); if (qmlComponent->isError()) { for (const auto& error : qmlComponent->errors()) { @@ -704,7 +704,20 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext } qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership); - f(qmlContext, newObject); + + // All quick items should be focusable + QQuickItem* newItem = qobject_cast(newObject); + if (newItem) { + // Make sure we make items focusable (critical for + // supporting keyboard shortcuts) + newItem->setFlag(QQuickItem::ItemIsFocusScope, true); + } + + // Make sure we will call callback for this codepath + // Call this before qmlComponent->completeCreate() otherwise ghost window appears + if (newItem && _rootItem) { + onQmlLoadedCallback(qmlContext, newObject); + } QObject* eventBridge = qmlContext->contextProperty("eventBridge").value(); if (qmlContext != _qmlContext && eventBridge && eventBridge != this) { @@ -716,15 +729,6 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext qmlComponent->completeCreate(); qmlComponent->deleteLater(); - - // All quick items should be focusable - QQuickItem* newItem = qobject_cast(newObject); - if (newItem) { - // Make sure we make items focusable (critical for - // supporting keyboard shortcuts) - newItem->setFlag(QQuickItem::ItemIsFocusScope, true); - } - // If we already have a root, just set a couple of flags and the ancestry if (newItem && _rootItem) { // Allow child windows to be destroyed from JS @@ -747,6 +751,8 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext _rootItem = newItem; _rootItem->setParentItem(_quickWindow->contentItem()); _rootItem->setSize(_quickWindow->renderTargetSize()); + // Call this callback after rootitem is set, otherwise VrMenu wont work + onQmlLoadedCallback(qmlContext, newObject); } void OffscreenQmlSurface::updateQuick() { diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 7da929723c..95dabdef0f 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -50,11 +50,11 @@ public: void resize(const QSize& size, bool forceResize = false); QSize size() const; - Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, std::function f = [](QQmlContext*, QObject*) {}); - Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); - Q_INVOKABLE void load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); - Q_INVOKABLE void load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { - return load(QUrl(qmlSourceFile), f); + Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {}); + Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {}); + Q_INVOKABLE void load(const QUrl& qmlSource, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {}); + Q_INVOKABLE void load(const QString& qmlSourceFile, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {}) { + return load(QUrl(qmlSourceFile), onQmlLoadedCallback); } void clearCache(); void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; } @@ -120,7 +120,7 @@ protected: private: static QOpenGLContext* getSharedContext(); - void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function f); + void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function onQmlLoadedCallback); QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject); void setupFbo(); bool allowNewFrame(uint8_t fps); diff --git a/plugins/hifiSdl2/src/SDL2Manager.cpp b/plugins/hifiSdl2/src/SDL2Manager.cpp index 021cb4dfec..a5376af24e 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.cpp +++ b/plugins/hifiSdl2/src/SDL2Manager.cpp @@ -54,49 +54,6 @@ SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) { } void SDL2Manager::init() { - loadSettings(); - - auto preferences = DependencyManager::get(); - static const QString SDL2_PLUGIN { "Game Controller" }; - { - auto getter = [this]()->bool { return _isEnabled; }; - auto setter = [this](bool value) { - _isEnabled = value; - saveSettings(); - }; - auto preference = new CheckPreference(SDL2_PLUGIN, "Enabled", getter, setter); - preferences->addPreference(preference); - } - - bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == 0); - - if (initSuccess) { - int joystickCount = SDL_NumJoysticks(); - - for (int i = 0; i < joystickCount; i++) { - SDL_GameController* controller = SDL_GameControllerOpen(i); - - if (controller) { - SDL_JoystickID id = getInstanceId(controller); - if (!_openJoysticks.contains(id)) { - //Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); - Joystick::Pointer joystick = std::make_shared(id, controller); - _openJoysticks[id] = joystick; - auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(joystick); - auto name = SDL_GameControllerName(controller); - _subdeviceNames << name; - emit joystickAdded(joystick.get()); - emit subdeviceConnected(getName(), name); - } - } - } - - _isInitialized = true; - } - else { - qDebug() << "Error initializing SDL2 Manager"; - } } QStringList SDL2Manager::getSubdeviceNames() { @@ -110,8 +67,61 @@ void SDL2Manager::deinit() { } bool SDL2Manager::activate() { + + // FIXME for some reason calling this code in the `init` function triggers a crash + // on OSX in PR builds, but not on my local debug build. Attempting a workaround by + // + static std::once_flag once; + std::call_once(once, [&]{ + loadSettings(); + + auto preferences = DependencyManager::get(); + static const QString SDL2_PLUGIN { "Game Controller" }; + { + auto getter = [this]()->bool { return _isEnabled; }; + auto setter = [this](bool value) { + _isEnabled = value; + saveSettings(); + }; + auto preference = new CheckPreference(SDL2_PLUGIN, "Enabled", getter, setter); + preferences->addPreference(preference); + } + + bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == 0); + + if (initSuccess) { + int joystickCount = SDL_NumJoysticks(); + + for (int i = 0; i < joystickCount; i++) { + SDL_GameController* controller = SDL_GameControllerOpen(i); + + if (controller) { + SDL_JoystickID id = getInstanceId(controller); + if (!_openJoysticks.contains(id)) { + //Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); + Joystick::Pointer joystick = std::make_shared(id, controller); + _openJoysticks[id] = joystick; + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(joystick); + auto name = SDL_GameControllerName(controller); + _subdeviceNames << name; + emit joystickAdded(joystick.get()); + emit subdeviceConnected(getName(), name); + } + } + } + + _isInitialized = true; + } else { + qDebug() << "Error initializing SDL2 Manager"; + } + }); + + if (!_isInitialized) { + return false; + } + InputPlugin::activate(); - auto userInputMapper = DependencyManager::get(); for (auto joystick : _openJoysticks) { userInputMapper->registerDevice(joystick); diff --git a/scripts/developer/utilities/render/debugOutline.js b/scripts/developer/utilities/render/debugOutline.js new file mode 100644 index 0000000000..e333ab5869 --- /dev/null +++ b/scripts/developer/utilities/render/debugOutline.js @@ -0,0 +1,20 @@ +// +// debugOutline.js +// developer/utilities/render +// +// Olivier Prat, created on 08/08/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 +// + +// Set up the qml ui +var qml = Script.resolvePath('outline.qml'); +var window = new OverlayWindow({ + title: 'Outline', + source: qml, + width: 285, + height: 370, +}); +window.closed.connect(function() { Script.stop(); }); \ No newline at end of file diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml new file mode 100644 index 0000000000..e17f7c1f1c --- /dev/null +++ b/scripts/developer/utilities/render/outline.qml @@ -0,0 +1,119 @@ +// +// outline.qml +// developer/utilities/render +// +// Olivier Prat, created on 08/08/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "configSlider" + +Item { + id: root + property var debugConfig: Render.getConfig("RenderMainView.OutlineDebug") + property var drawConfig: Render.getConfig("RenderMainView.OutlineEffect") + + Column { + spacing: 8 + + CheckBox { + text: "View Outlined Depth" + checked: root.debugConfig["viewOutlinedDepth"] + onCheckedChanged: { + root.debugConfig["viewOutlinedDepth"] = checked; + } + } + CheckBox { + text: "Glow" + checked: root.drawConfig["glow"] + onCheckedChanged: { + root.drawConfig["glow"] = checked; + } + } + ConfigSlider { + label: "Width" + integral: false + config: root.drawConfig + property: "width" + max: 15.0 + min: 0.0 + width: 280 + } + ConfigSlider { + label: "Intensity" + integral: false + config: root.drawConfig + property: "intensity" + max: 1.0 + min: 0.0 + width: 280 + } + + GroupBox { + title: "Color" + width: 280 + Column { + spacing: 8 + + ConfigSlider { + label: "Red" + integral: false + config: root.drawConfig + property: "colorR" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Green" + integral: false + config: root.drawConfig + property: "colorG" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Blue" + integral: false + config: root.drawConfig + property: "colorB" + max: 1.0 + min: 0.0 + width: 270 + } + } + } + + GroupBox { + title: "Fill Opacity" + width: 280 + Column { + spacing: 8 + + ConfigSlider { + label: "Unoccluded" + integral: false + config: root.drawConfig + property: "fillOpacityUnoccluded" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Occluded" + integral: false + config: root.drawConfig + property: "fillOpacityOccluded" + max: 1.0 + min: 0.0 + width: 270 + } + } + } + } +}