From 074d7f40bba8be14d567a866c3a08bf4040293c0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 15:10:32 -0700 Subject: [PATCH 01/57] add dynamic selection of debug or release for oculus libraries --- cmake/modules/FindLibOVR.cmake | 29 ++++++++++++++++++++--------- interface/CMakeLists.txt | 8 ++------ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 91714e8a59..ac54814717 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -27,10 +27,11 @@ else (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_DIRS}) if (APPLE) - find_library(LIBOVR_LIBRARIES "Lib/MacOS/Release/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_RELEASE "Lib/MacOS/Release/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) elseif (UNIX) - find_library(UDEV_LIBRARY libudev.a /usr/lib/x86_64-linux-gnu/) - find_library(XINERAMA_LIBRARY libXinerama.a /usr/lib/x86_64-linux-gnu/) + find_library(UDEV_LIBRARY_RELEASE libudev.a /usr/lib/x86_64-linux-gnu/) + find_library(XINERAMA_LIBRARY_RELEASE libXinerama.a /usr/lib/x86_64-linux-gnu/) if (CMAKE_CL_64) set(LINUX_ARCH_DIR "i386") @@ -38,17 +39,27 @@ else (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) set(LINUX_ARCH_DIR "x86_64") endif() - find_library(OVR_LIBRARY "Lib/Linux/${CMAKE_BUILD_TYPE}/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_DEBUG "Lib/Linux/Debug/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_RELEASE "Lib/Linux/Release/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) + if (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY) set(LIBOVR_LIBRARIES "${OVR_LIBRARY};${UDEV_LIBRARY};${XINERAMA_LIBRARY}" CACHE INTERNAL "Oculus libraries") endif (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY) - elseif (WIN32) - find_library(LIBOVR_RELEASE_LIBRARIES "Lib/Win32/libovr.lib" HINTS ${LIBOVR_SEARCH_DIRS}) - find_library(LIBOVR_DEBUG_LIBRARIES "Lib/Win32/libovrd.lib" HINTS ${LIBOVR_SEARCH_DIRS}) - - set(LIBOVR_LIBRARIES "${LIBOVR_RELEASE_LIBRARIES} ${LIBOVR_DEBUG_LIBRARIES}") + elseif (WIN32) + find_library(LIBOVR_LIBRARY_DEBUG "Lib/Win32/libovrd.lib" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_RELEASE "Lib/Win32/libovr.lib" HINTS ${LIBOVR_SEARCH_DIRS}) endif () + include(SelectLibraryConfigurations) + select_library_configurations(LIBOVR) + + if (UNIX) + select_library_configurations(UDEV) + select_library_configurations(XINERAMA) + + set(${LIBOVR_LIBRARIES} ${LIBOVR_LIBRARIES} ${UDEV_LIBRARY} ${XINERAMA_LIBRARY}) + endif () + if (LIBOVR_INCLUDE_DIRS AND LIBOVR_LIBRARIES) set(LIBOVR_FOUND TRUE) endif (LIBOVR_INCLUDE_DIRS AND LIBOVR_LIBRARIES) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index a5d1b0193e..f3335279d3 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -187,12 +187,8 @@ if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) if (APPLE OR UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LIBOVR_INCLUDE_DIRS}") endif () - - if (WIN32) - target_link_libraries(${TARGET_NAME} optimized "${LIBOVR_RELEASE_LIBRARIES}" debug "${LIBOVR_DEBUG_LIBRARIES}") - else () - target_link_libraries(${TARGET_NAME} "${LIBOVR_LIBRARIES}") - endif () + + target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) # and with PrioVR library From 03a052d89c176aece6f8e219721c48cda7881573 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 15:27:55 -0700 Subject: [PATCH 02/57] add debug/release linking to sixense via select_library_configurations --- cmake/modules/FindSixense.cmake | 12 +++++++++--- interface/CMakeLists.txt | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cmake/modules/FindSixense.cmake b/cmake/modules/FindSixense.cmake index 02b6e3fc21..9f408bacef 100644 --- a/cmake/modules/FindSixense.cmake +++ b/cmake/modules/FindSixense.cmake @@ -28,12 +28,18 @@ else () find_path(SIXENSE_INCLUDE_DIRS sixense.h PATH_SUFFIXES include HINTS ${SIXENSE_SEARCH_DIRS}) if (APPLE) - find_library(SIXENSE_LIBRARIES lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) + find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) + find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) elseif (UNIX) - find_library(SIXENSE_LIBRARIES lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) + find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) + find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) elseif (WIN32) - find_library(SIXENSE_LIBRARIES lib/win32/release_dll/sixense.lib HINTS ${SIXENSE_SEARCH_DIRS}) + find_library(SIXENSE_LIBRARY_RELEASE lib/win32/release_dll/sixense.lib HINTS ${SIXENSE_SEARCH_DIRS}) + find_library(SIXENSE_LIBRARY_DEBUG lib/win32/debug_dll/sixensed.lib HINTS ${SIXENSE_SEARCH_DIRS}) endif () + + include(SelectLibraryConfigurations) + select_library_configurations(SIXENSE) if (SIXENSE_INCLUDE_DIRS AND SIXENSE_LIBRARIES) set(SIXENSE_FOUND TRUE) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index f3335279d3..2d48d57410 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -153,7 +153,7 @@ if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE) if (APPLE OR UNIX) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${SIXENSE_INCLUDE_DIRS}") endif (APPLE OR UNIX) - target_link_libraries(${TARGET_NAME} "${SIXENSE_LIBRARIES}") + target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES}) endif (SIXENSE_FOUND AND NOT DISABLE_SIXENSE) # likewise with Visage library for webcam feature tracking From e7269e809ee79d9aad851fa4676738de9a000916 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 15:41:47 -0700 Subject: [PATCH 03/57] allow use of debug/release leapmotion library with select_library_configurations --- cmake/modules/FindLeapMotion.cmake | 8 ++++++-- cmake/modules/FindPrioVR.cmake | 2 +- interface/CMakeLists.txt | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cmake/modules/FindLeapMotion.cmake b/cmake/modules/FindLeapMotion.cmake index 40958ece95..2c6a2ae1c0 100644 --- a/cmake/modules/FindLeapMotion.cmake +++ b/cmake/modules/FindLeapMotion.cmake @@ -21,11 +21,15 @@ else (LEAPMOTION_LIBRARIES AND LEAPMOTION_INCLUDE_DIRS) find_path(LEAPMOTION_INCLUDE_DIRS Leap.h PATH_SUFFIXES include HINTS ${LEAPMOTION_SEARCH_DIRS}) if (WIN32) - find_library(LEAPMOTION_LIBRARIES "lib/x86/Leap.lib" HINTS ${LEAPMOTION_SEARCH_DIRS}) + find_library(LEAPMOTION_LIBRARY_DEBUG "lib/x86/Leapd.lib" HINTS ${LEAPMOTION_SEARCH_DIRS}) + find_library(LEAPMOTION_LIBRARY_RELEASE "lib/x86/Leap.lib" HINTS ${LEAPMOTION_SEARCH_DIRS}) endif (WIN32) if (APPLE) - find_library(LEAPMOTION_LIBRARIES "lib/libLeap.dylib" HINTS ${LEAPMOTION_SEARCH_DIRS}) + find_library(LEAPMOTION_LIBRARY_RELEASE "lib/libLeap.dylib" HINTS ${LEAPMOTION_SEARCH_DIRS}) endif (APPLE) + + include(SelectLibraryConfigurations) + select_library_configurations(LEAPMOTION) if (LEAPMOTION_INCLUDE_DIRS AND LEAPMOTION_LIBRARIES) set(LEAPMOTION_FOUND TRUE) diff --git a/cmake/modules/FindPrioVR.cmake b/cmake/modules/FindPrioVR.cmake index 6e78045321..46710d8b5e 100644 --- a/cmake/modules/FindPrioVR.cmake +++ b/cmake/modules/FindPrioVR.cmake @@ -1,4 +1,4 @@ -# Try to find the PrioVT library +# Try to find the PrioVR library # # You must provide a PRIOVR_ROOT_DIR which contains lib and include directories # diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 2d48d57410..564923b53d 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -206,7 +206,7 @@ if (LEAPMOTION_FOUND AND NOT DISABLE_LEAPMOTION) if (APPLE OR UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LEAPMOTION_INCLUDE_DIRS}") endif () - target_link_libraries(${TARGET_NAME} "${LEAPMOTION_LIBRARIES}") + target_link_libraries(${TARGET_NAME} ${LEAPMOTION_LIBRARIES}) endif (LEAPMOTION_FOUND AND NOT DISABLE_LEAPMOTION) # and with SDL for joysticks From 45f4e033f362b3943a7ce4822cc005569e0bc068 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 15:55:10 -0700 Subject: [PATCH 04/57] fix the LibOVR find module to grab a util header --- cmake/modules/FindLibOVR.cmake | 30 +++++++++------------------ interface/src/devices/OculusManager.h | 2 +- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index ac54814717..136cb7da41 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -18,13 +18,17 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -if (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) +if (LIBOVR_LIBRARIES AND LIBOVR_UTIL_INCLUDE_DIR AND LIBOVR_INCLUDE_DIRS) # in cache already - set(LIBOVR_FOUND TRUE) -else (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) + set(LIBOVR_FIND_QUIETLY TRUE) +else () set(LIBOVR_SEARCH_DIRS "${LIBOVR_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/oculus") find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_DIRS}) + find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${LIBOVR_SEARCH_DIRS}) + + # add the util include dir to the general include dirs + set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS} ${LIBOVR_UTIL_INCLUDE_DIR}") if (APPLE) find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) @@ -60,21 +64,7 @@ else (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) set(${LIBOVR_LIBRARIES} ${LIBOVR_LIBRARIES} ${UDEV_LIBRARY} ${XINERAMA_LIBRARY}) endif () - if (LIBOVR_INCLUDE_DIRS AND LIBOVR_LIBRARIES) - set(LIBOVR_FOUND TRUE) - endif (LIBOVR_INCLUDE_DIRS AND LIBOVR_LIBRARIES) - - if (LIBOVR_FOUND) - if (NOT LibOVR_FIND_QUIETLY) - message(STATUS "Found LibOVR: ${LIBOVR_LIBRARIES}") - endif (NOT LibOVR_FIND_QUIETLY) - else (LIBOVR_FOUND) - if (LibOVR_FIND_REQUIRED) - message(FATAL_ERROR "Could not find LibOVR") - endif (LibOVR_FIND_REQUIRED) - endif (LIBOVR_FOUND) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LIBOVR DEFAULT_MSG LIBOVR_INCLUDE_DIRS LIBOVR_UTIL_INCLUDE_DIR LIBOVR_LIBRARIES) - # show the LIBOVR_INCLUDE_DIRS and LIBOVR_LIBRARIES variables only in the advanced view - mark_as_advanced(LIBOVR_INCLUDE_DIRS LIBOVR_LIBRARIES) - -endif (LIBOVR_LIBRARIES AND LIBOVR_INCLUDE_DIRS) +endif () diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 871e5d649a..0ef7abc3dc 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -15,7 +15,7 @@ #ifdef HAVE_LIBOVR #include -#include "../src/Util/Util_Render_Stereo.h" +#include #endif #include "renderer/ProgramObject.h" From 1cfa4d7990ec4d2d991c65cb61f10bc267f8c9fa Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 15:57:29 -0700 Subject: [PATCH 05/57] simplify the sixense find module removing unecessary checks --- cmake/modules/FindSixense.cmake | 51 +++++++++++---------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/cmake/modules/FindSixense.cmake b/cmake/modules/FindSixense.cmake index 9f408bacef..d1958e2ab3 100644 --- a/cmake/modules/FindSixense.cmake +++ b/cmake/modules/FindSixense.cmake @@ -18,40 +18,23 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -if (SIXENSE_LIBRARIES AND SIXENSE_INCLUDE_DIRS) - # in cache already - set(SIXENSE_FOUND TRUE) -else () - - set(SIXENSE_SEARCH_DIRS "${SIXENSE_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/sixense") - - find_path(SIXENSE_INCLUDE_DIRS sixense.h PATH_SUFFIXES include HINTS ${SIXENSE_SEARCH_DIRS}) +set(SIXENSE_SEARCH_DIRS "${SIXENSE_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/sixense") - if (APPLE) - find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) - find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) - elseif (UNIX) - find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) - find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) - elseif (WIN32) - find_library(SIXENSE_LIBRARY_RELEASE lib/win32/release_dll/sixense.lib HINTS ${SIXENSE_SEARCH_DIRS}) - find_library(SIXENSE_LIBRARY_DEBUG lib/win32/debug_dll/sixensed.lib HINTS ${SIXENSE_SEARCH_DIRS}) - endif () - - include(SelectLibraryConfigurations) - select_library_configurations(SIXENSE) +find_path(SIXENSE_INCLUDE_DIRS sixense.h PATH_SUFFIXES include HINTS ${SIXENSE_SEARCH_DIRS}) - if (SIXENSE_INCLUDE_DIRS AND SIXENSE_LIBRARIES) - set(SIXENSE_FOUND TRUE) - endif () - - if (SIXENSE_FOUND) - if (NOT SIXENSE_FIND_QUIETLY) - message(STATUS "Found Sixense: ${SIXENSE_LIBRARIES}") - endif (NOT SIXENSE_FIND_QUIETLY) - else () - if (SIXENSE_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Sixense") - endif (SIXENSE_FIND_REQUIRED) - endif () +if (APPLE) + find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) + find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) +elseif (UNIX) + find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) + find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) +elseif (WIN32) + find_library(SIXENSE_LIBRARY_RELEASE lib/win32/release_dll/sixense.lib HINTS ${SIXENSE_SEARCH_DIRS}) + find_library(SIXENSE_LIBRARY_DEBUG lib/win32/debug_dll/sixensed.lib HINTS ${SIXENSE_SEARCH_DIRS}) endif () + +include(SelectLibraryConfigurations) +select_library_configurations(SIXENSE) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SIXENSE DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES) From 1f0611ab09a3d990bfcb835a4b7e74b5b5fe9bcc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 15:58:29 -0700 Subject: [PATCH 06/57] cleanup the LibOVR and LeapMotion find modules by removing checks --- cmake/modules/FindLeapMotion.cmake | 46 +++++----------- cmake/modules/FindLibOVR.cmake | 88 ++++++++++++++---------------- 2 files changed, 54 insertions(+), 80 deletions(-) diff --git a/cmake/modules/FindLeapMotion.cmake b/cmake/modules/FindLeapMotion.cmake index 2c6a2ae1c0..1066be46af 100644 --- a/cmake/modules/FindLeapMotion.cmake +++ b/cmake/modules/FindLeapMotion.cmake @@ -12,40 +12,20 @@ # Copyright (c) 2014 High Fidelity # -if (LEAPMOTION_LIBRARIES AND LEAPMOTION_INCLUDE_DIRS) - # in cache already - set(LEAPMOTION_FOUND TRUE) -else (LEAPMOTION_LIBRARIES AND LEAPMOTION_INCLUDE_DIRS) - set(LEAPMOTION_SEARCH_DIRS "${LEAPMOTION_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/leapmotion") +set(LEAPMOTION_SEARCH_DIRS "${LEAPMOTION_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/leapmotion") - find_path(LEAPMOTION_INCLUDE_DIRS Leap.h PATH_SUFFIXES include HINTS ${LEAPMOTION_SEARCH_DIRS}) +find_path(LEAPMOTION_INCLUDE_DIRS Leap.h PATH_SUFFIXES include HINTS ${LEAPMOTION_SEARCH_DIRS}) - if (WIN32) - find_library(LEAPMOTION_LIBRARY_DEBUG "lib/x86/Leapd.lib" HINTS ${LEAPMOTION_SEARCH_DIRS}) - find_library(LEAPMOTION_LIBRARY_RELEASE "lib/x86/Leap.lib" HINTS ${LEAPMOTION_SEARCH_DIRS}) - endif (WIN32) - if (APPLE) - find_library(LEAPMOTION_LIBRARY_RELEASE "lib/libLeap.dylib" HINTS ${LEAPMOTION_SEARCH_DIRS}) - endif (APPLE) - - include(SelectLibraryConfigurations) - select_library_configurations(LEAPMOTION) - - if (LEAPMOTION_INCLUDE_DIRS AND LEAPMOTION_LIBRARIES) - set(LEAPMOTION_FOUND TRUE) - endif (LEAPMOTION_INCLUDE_DIRS AND LEAPMOTION_LIBRARIES) +if (WIN32) + find_library(LEAPMOTION_LIBRARY_DEBUG "lib/x86/Leapd.lib" HINTS ${LEAPMOTION_SEARCH_DIRS}) + find_library(LEAPMOTION_LIBRARY_RELEASE "lib/x86/Leap.lib" HINTS ${LEAPMOTION_SEARCH_DIRS}) +endif (WIN32) +if (APPLE) + find_library(LEAPMOTION_LIBRARY_RELEASE "lib/libLeap.dylib" HINTS ${LEAPMOTION_SEARCH_DIRS}) +endif (APPLE) - if (LEAPMOTION_FOUND) - if (NOT LEAPMOTION_FIND_QUIETLY) - message(STATUS "Found LEAPMOTION... ${LEAPMOTION_LIBRARIES}") - endif (NOT LEAPMOTION_FIND_QUIETLY) - else () - if (LEAPMOTION_FIND_REQUIRED) - message(FATAL_ERROR "Could not find LEAPMOTION") - endif (LEAPMOTION_FIND_REQUIRED) - endif () +include(SelectLibraryConfigurations) +select_library_configurations(LEAPMOTION) - # show the LEAPMOTION_INCLUDE_DIRS and LEAPMOTION_LIBRARIES variables only in the advanced view - mark_as_advanced(LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES) - -endif (LEAPMOTION_LIBRARIES AND LEAPMOTION_INCLUDE_DIRS) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LEAPMOTION DEFAULT_MSG LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 136cb7da41..270d65a84d 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -18,53 +18,47 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -if (LIBOVR_LIBRARIES AND LIBOVR_UTIL_INCLUDE_DIR AND LIBOVR_INCLUDE_DIRS) - # in cache already - set(LIBOVR_FIND_QUIETLY TRUE) -else () - set(LIBOVR_SEARCH_DIRS "${LIBOVR_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/oculus") - - find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_DIRS}) - find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${LIBOVR_SEARCH_DIRS}) - - # add the util include dir to the general include dirs - set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS} ${LIBOVR_UTIL_INCLUDE_DIR}") - - if (APPLE) - find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) - find_library(LIBOVR_LIBRARY_RELEASE "Lib/MacOS/Release/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) - elseif (UNIX) - find_library(UDEV_LIBRARY_RELEASE libudev.a /usr/lib/x86_64-linux-gnu/) - find_library(XINERAMA_LIBRARY_RELEASE libXinerama.a /usr/lib/x86_64-linux-gnu/) - - if (CMAKE_CL_64) - set(LINUX_ARCH_DIR "i386") - else() - set(LINUX_ARCH_DIR "x86_64") - endif() - - find_library(LIBOVR_LIBRARY_DEBUG "Lib/Linux/Debug/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) - find_library(LIBOVR_LIBRARY_RELEASE "Lib/Linux/Release/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) - - if (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY) - set(LIBOVR_LIBRARIES "${OVR_LIBRARY};${UDEV_LIBRARY};${XINERAMA_LIBRARY}" CACHE INTERNAL "Oculus libraries") - endif (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY) - elseif (WIN32) - find_library(LIBOVR_LIBRARY_DEBUG "Lib/Win32/libovrd.lib" HINTS ${LIBOVR_SEARCH_DIRS}) - find_library(LIBOVR_LIBRARY_RELEASE "Lib/Win32/libovr.lib" HINTS ${LIBOVR_SEARCH_DIRS}) - endif () +set(LIBOVR_SEARCH_DIRS "${LIBOVR_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/oculus") - include(SelectLibraryConfigurations) - select_library_configurations(LIBOVR) - - if (UNIX) - select_library_configurations(UDEV) - select_library_configurations(XINERAMA) - - set(${LIBOVR_LIBRARIES} ${LIBOVR_LIBRARIES} ${UDEV_LIBRARY} ${XINERAMA_LIBRARY}) - endif () - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(LIBOVR DEFAULT_MSG LIBOVR_INCLUDE_DIRS LIBOVR_UTIL_INCLUDE_DIR LIBOVR_LIBRARIES) +find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_DIRS}) +find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${LIBOVR_SEARCH_DIRS}) +# add the util include dir to the general include dirs +set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS} ${LIBOVR_UTIL_INCLUDE_DIR}") + +if (APPLE) + find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_RELEASE "Lib/MacOS/Release/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) +elseif (UNIX) + find_library(UDEV_LIBRARY_RELEASE libudev.a /usr/lib/x86_64-linux-gnu/) + find_library(XINERAMA_LIBRARY_RELEASE libXinerama.a /usr/lib/x86_64-linux-gnu/) + + if (CMAKE_CL_64) + set(LINUX_ARCH_DIR "i386") + else() + set(LINUX_ARCH_DIR "x86_64") + endif() + + find_library(LIBOVR_LIBRARY_DEBUG "Lib/Linux/Debug/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_RELEASE "Lib/Linux/Release/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) + + if (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY) + set(LIBOVR_LIBRARIES "${OVR_LIBRARY};${UDEV_LIBRARY};${XINERAMA_LIBRARY}" CACHE INTERNAL "Oculus libraries") + endif (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY) +elseif (WIN32) + find_library(LIBOVR_LIBRARY_DEBUG "Lib/Win32/libovrd.lib" HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_RELEASE "Lib/Win32/libovr.lib" HINTS ${LIBOVR_SEARCH_DIRS}) endif () + +include(SelectLibraryConfigurations) +select_library_configurations(LIBOVR) + +if (UNIX) + select_library_configurations(UDEV) + select_library_configurations(XINERAMA) + + set(${LIBOVR_LIBRARIES} ${LIBOVR_LIBRARIES} ${UDEV_LIBRARY} ${XINERAMA_LIBRARY}) +endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LIBOVR DEFAULT_MSG LIBOVR_INCLUDE_DIRS LIBOVR_UTIL_INCLUDE_DIR LIBOVR_LIBRARIES) From 8e65645e4a7e2f933130ae26e5f15f2b2f9848ef Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 16:12:12 -0700 Subject: [PATCH 07/57] cleanup RtMidi find module --- cmake/modules/FindRtMidi.cmake | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/cmake/modules/FindRtMidi.cmake b/cmake/modules/FindRtMidi.cmake index ad1167c5d6..0b3084b7fb 100644 --- a/cmake/modules/FindRtMidi.cmake +++ b/cmake/modules/FindRtMidi.cmake @@ -18,16 +18,10 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -if (RTMIDI_LIBRARIES AND RTMIDI_INCLUDE_DIRS) - # in cache already - set(RTMIDI_FOUND TRUE) -else () - - set(RTMIDI_SEARCH_DIRS "${RTMIDI_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/rtmidi") - - find_path(RTMIDI_INCLUDE_DIR RtMidi.h PATH_SUFFIXES include HINTS ${RTMIDI_SEARCH_DIRS}) - find_library(RTMIDI_LIBRARY NAMES rtmidi PATH_SUFFIXES lib HINTS ${RTMIDI_SEARCH_DIRS}) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(RTMIDI DEFAULT_MSG RTMIDI_INCLUDE_DIR RTMIDI_LIBRARY) -endif () \ No newline at end of file +set(RTMIDI_SEARCH_DIRS "${RTMIDI_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/rtmidi") + +find_path(RTMIDI_INCLUDE_DIR RtMidi.h PATH_SUFFIXES include HINTS ${RTMIDI_SEARCH_DIRS}) +find_library(RTMIDI_LIBRARY NAMES rtmidi PATH_SUFFIXES lib HINTS ${RTMIDI_SEARCH_DIRS}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(RTMIDI DEFAULT_MSG RTMIDI_INCLUDE_DIR RTMIDI_LIBRARY) \ No newline at end of file From 04c237bb66766f3ecd064fa25f15386bb09a4990 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 16:21:32 -0700 Subject: [PATCH 08/57] fix stitched-together LibOVR include --- cmake/modules/FindLibOVR.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 270d65a84d..4ec85c8161 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -24,7 +24,7 @@ find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_ find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${LIBOVR_SEARCH_DIRS}) # add the util include dir to the general include dirs -set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS} ${LIBOVR_UTIL_INCLUDE_DIR}") +set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS}" "${LIBOVR_UTIL_INCLUDE_DIR}") if (APPLE) find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) From 7447379e50cdfa9352fed9b1c3b96de2fd4ba1d1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 16:23:22 -0700 Subject: [PATCH 09/57] cleanup the OpenSSL find module --- cmake/modules/FindOpenSSL.cmake | 158 +++++++------------------------- 1 file changed, 35 insertions(+), 123 deletions(-) diff --git a/cmake/modules/FindOpenSSL.cmake b/cmake/modules/FindOpenSSL.cmake index 1dbac311da..403bb4e246 100644 --- a/cmake/modules/FindOpenSSL.cmake +++ b/cmake/modules/FindOpenSSL.cmake @@ -35,43 +35,24 @@ endif () if (WIN32) # http://www.slproweb.com/products/Win32OpenSSL.html - set(_OPENSSL_ROOT_HINTS - ${OPENSSL_ROOT_DIR} - $ENV{OPENSSL_ROOT_DIR} - $ENV{HIFI_LIB_DIR}/openssl + set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" ) file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS - "${_programfiles}/OpenSSL" - "${_programfiles}/OpenSSL-Win32" - "${_programfiles}/OpenSSL-Win64" - "C:/OpenSSL/" - "C:/OpenSSL-Win32/" - "C:/OpenSSL-Win64/" - ) + set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win32" "${_programfiles}/OpenSSL-Win64" + "C:/OpenSSL/" "C:/OpenSSL-Win32/" "C:/OpenSSL-Win64/" + ) unset(_programfiles) - set(_OPENSSL_ROOT_HINTS_AND_PATHS - HINTS ${_OPENSSL_ROOT_HINTS} - PATHS ${_OPENSSL_ROOT_PATHS} - ) + set(_OPENSSL_ROOT_HINTS_AND_PATHS HINTS ${_OPENSSL_ROOT_HINTS} PATHS ${_OPENSSL_ROOT_PATHS}) else () set(_OPENSSL_ROOT_HINTS_AND_PATHS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl) endif () -find_path(OPENSSL_INCLUDE_DIR - NAMES - openssl/ssl.h - HINTS - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - ${_OPENSSL_INCLUDEDIR} - PATH_SUFFIXES - include -) +find_path(OPENSSL_INCLUDE_DIR NAMES openssl/ssl.h HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} ${_OPENSSL_INCLUDEDIR} PATH_SUFFIXES include) -if(WIN32 AND NOT CYGWIN) - if(MSVC) +if (WIN32 AND NOT CYGWIN) + if (MSVC) # /MD and /MDd are the standard values - if someone wants to use # others, the libnames have to change here too # use also ssl and ssleay32 in debug as fallback for openssl < 0.9.8b @@ -86,49 +67,20 @@ if(WIN32 AND NOT CYGWIN) # We are using the libraries located in the VC subdir instead of the parent directory eventhough : # libeay32MD.lib is identical to ../libeay32.lib, and # ssleay32MD.lib is identical to ../ssleay32.lib - find_library(LIB_EAY_DEBUG - NAMES - libeay32MDd - libeay32d - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - "lib" - "VC" - "lib/VC" + find_library(LIB_EAY_DEBUG NAMES libeay32MDd libeay32d + ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib" "VC" "lib/VC" ) - find_library(LIB_EAY_RELEASE - NAMES - libeay32MD - libeay32 - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - "lib" - "VC" - "lib/VC" + find_library(LIB_EAY_RELEASE NAMES libeay32MD libeay32 + ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib" "VC" "lib/VC" ) - find_library(SSL_EAY_DEBUG - NAMES - ssleay32MDd - ssleay32d - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - "lib" - "VC" - "lib/VC" + find_library(SSL_EAY_DEBUG NAMES ssleay32MDd ssleay32d + ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib" "VC" "lib/VC" ) - find_library(SSL_EAY_RELEASE - NAMES - ssleay32MD - ssleay32 - ssl - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - "lib" - "VC" - "lib/VC" + find_library(SSL_EAY_RELEASE NAMES ssleay32MD ssleay32 ssl + ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib" "VC" "lib/VC" ) set(LIB_EAY_LIBRARY_DEBUG "${LIB_EAY_DEBUG}") @@ -140,85 +92,45 @@ if(WIN32 AND NOT CYGWIN) select_library_configurations(LIB_EAY) select_library_configurations(SSL_EAY) - set( OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY} ) - elseif(MINGW) + set(OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY}) + elseif (MINGW) # same player, for MinGW set(LIB_EAY_NAMES libeay32) set(SSL_EAY_NAMES ssleay32) - if(CMAKE_CROSSCOMPILING) + + if (CMAKE_CROSSCOMPILING) list(APPEND LIB_EAY_NAMES crypto) list(APPEND SSL_EAY_NAMES ssl) - endif() - find_library(LIB_EAY - NAMES - ${LIB_EAY_NAMES} - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - "lib" - "lib/MinGW" + endif () + + find_library(LIB_EAY NAMES ${LIB_EAY_NAMES} + ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib" "lib/MinGW" ) - find_library(SSL_EAY - NAMES - ${SSL_EAY_NAMES} - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - "lib" - "lib/MinGW" + find_library(SSL_EAY NAMES ${SSL_EAY_NAMES} + ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib" "lib/MinGW" ) mark_as_advanced(SSL_EAY LIB_EAY) - set( OPENSSL_LIBRARIES ${SSL_EAY} ${LIB_EAY} ) + set(OPENSSL_LIBRARIES ${SSL_EAY} ${LIB_EAY}) unset(LIB_EAY_NAMES) unset(SSL_EAY_NAMES) - else() + else () # Not sure what to pick for -say- intel, let's use the toplevel ones and hope someone report issues: - find_library(LIB_EAY - NAMES - libeay32 - HINTS - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - ${_OPENSSL_LIBDIR} - PATH_SUFFIXES - lib - ) + find_library(LIB_EAY NAMES libeay32 HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} ${_OPENSSL_LIBDIR} PATH_SUFFIXES lib) - find_library(SSL_EAY - NAMES - ssleay32 - HINTS - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - ${_OPENSSL_LIBDIR} - PATH_SUFFIXES - lib - ) + find_library(SSL_EAY NAMES ssleay32 HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} ${_OPENSSL_LIBDIR} PATH_SUFFIXES lib) mark_as_advanced(SSL_EAY LIB_EAY) - set( OPENSSL_LIBRARIES ${SSL_EAY} ${LIB_EAY} ) + set(OPENSSL_LIBRARIES ${SSL_EAY} ${LIB_EAY}) endif() else() - find_library(OPENSSL_SSL_LIBRARY - NAMES - ssl - ssleay32 - ssleay32MD - HINTS - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - ${_OPENSSL_LIBDIR} - PATH_SUFFIXES - lib + find_library(OPENSSL_SSL_LIBRARY NAMES ssl ssleay32 ssleay32MD HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} ${_OPENSSL_LIBDIR} + PATH_SUFFIXES lib ) - find_library(OPENSSL_CRYPTO_LIBRARY - NAMES - crypto - HINTS - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - ${_OPENSSL_LIBDIR} - PATH_SUFFIXES - lib - ) + find_library(OPENSSL_CRYPTO_LIBRARY NAMES crypto HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} ${_OPENSSL_LIBDIR} PATH_SUFFIXES lib) mark_as_advanced(OPENSSL_CRYPTO_LIBRARY OPENSSL_SSL_LIBRARY) @@ -228,7 +140,7 @@ else() set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY}) -endif() +endif () function(from_hex HEX DEC) string(TOUPPER "${HEX}" HEX) From 5f8b6de153dc3ab62402c11d04d312003a0a249c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 16:28:15 -0700 Subject: [PATCH 10/57] cleanup the Faceplus find module --- cmake/modules/FindFaceplus.cmake | 35 ++++++++------------------------ 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/cmake/modules/FindFaceplus.cmake b/cmake/modules/FindFaceplus.cmake index 2a2083d6e4..6783262254 100644 --- a/cmake/modules/FindFaceplus.cmake +++ b/cmake/modules/FindFaceplus.cmake @@ -12,31 +12,14 @@ # Copyright (c) 2014 High Fidelity # -if (FACEPLUS_LIBRARIES AND FACEPLUS_INCLUDE_DIRS) - # in cache already - set(FACEPLUS_FOUND TRUE) -else (FACEPLUS_LIBRARIES AND FACEPLUS_INCLUDE_DIRS) - find_path(FACEPLUS_INCLUDE_DIRS faceplus.h ${FACEPLUS_ROOT_DIR}/include) +find_path(FACEPLUS_INCLUDE_DIRS faceplus.h ${FACEPLUS_ROOT_DIR}/include) - if (WIN32) - find_library(FACEPLUS_LIBRARIES faceplus.lib ${FACEPLUS_ROOT_DIR}/win32/) - endif (WIN32) +if (WIN32) + find_library(FACEPLUS_LIBRARIES faceplus.lib ${FACEPLUS_ROOT_DIR}/win32/) +endif (WIN32) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FACEPLUS DEFAULT_MSG FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES) - if (FACEPLUS_INCLUDE_DIRS AND FACEPLUS_LIBRARIES) - set(FACEPLUS_FOUND TRUE) - endif (FACEPLUS_INCLUDE_DIRS AND FACEPLUS_LIBRARIES) - - if (FACEPLUS_FOUND) - if (NOT FACEPLUS_FIND_QUIETLY) - message(STATUS "Found Faceplus... ${FACEPLUS_LIBRARIES}") - endif (NOT FACEPLUS_FIND_QUIETLY) - else () - if (FACEPLUS_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Faceplus") - endif (FACEPLUS_FIND_REQUIRED) - endif () - - # show the FACEPLUS_INCLUDE_DIRS and FACEPLUS_LIBRARIES variables only in the advanced view - mark_as_advanced(FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES) - -endif (FACEPLUS_LIBRARIES AND FACEPLUS_INCLUDE_DIRS) +# show the FACEPLUS_INCLUDE_DIRS and FACEPLUS_LIBRARIES variables only in the advanced view +mark_as_advanced(FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES) From 68f933462803def9840705e35cbb19f9f7bb0555 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 16:37:10 -0700 Subject: [PATCH 11/57] cleanup Faceplus and Faceshift find modules, fix for OVR stitched include --- cmake/modules/FindFaceplus.cmake | 5 +- cmake/modules/FindFaceshift.cmake | 78 ++++++++++++++----------------- cmake/modules/FindLibOVR.cmake | 2 +- interface/CMakeLists.txt | 2 +- 4 files changed, 39 insertions(+), 48 deletions(-) diff --git a/cmake/modules/FindFaceplus.cmake b/cmake/modules/FindFaceplus.cmake index 6783262254..bdb783685f 100644 --- a/cmake/modules/FindFaceplus.cmake +++ b/cmake/modules/FindFaceplus.cmake @@ -19,7 +19,4 @@ if (WIN32) endif (WIN32) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(FACEPLUS DEFAULT_MSG FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES) - -# show the FACEPLUS_INCLUDE_DIRS and FACEPLUS_LIBRARIES variables only in the advanced view -mark_as_advanced(FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES) +find_package_handle_standard_args(FACEPLUS DEFAULT_MSG FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES) \ No newline at end of file diff --git a/cmake/modules/FindFaceshift.cmake b/cmake/modules/FindFaceshift.cmake index faf556e06c..39febe3eb3 100644 --- a/cmake/modules/FindFaceshift.cmake +++ b/cmake/modules/FindFaceshift.cmake @@ -18,47 +18,41 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -if (FACESHIFT_LIBRARIES AND FACESHIFT_INCLUDE_DIRS) - # in cache already - set(FACESHIFT_FOUND TRUE) -else () - find_path(FACESHIFT_INCLUDE_DIRS fsbinarystream.h ${FACESHIFT_ROOT_DIR}/include) - - if (APPLE) - find_library(FACESHIFT_LIBRARIES libfaceshift.a ${FACESHIFT_ROOT_DIR}/lib/MacOS/) - elseif (UNIX) - find_library(FACESHIFT_LIBRARIES libfaceshift.a ${FACESHIFT_ROOT_DIR}/lib/UNIX/) - elseif (WIN32) - # For windows, we're going to build the faceshift sources directly into the interface build - # and not link to a prebuilt library. This is because the VS2010 linker doesn't like cross-linking - # between release and debug libraries. If we change that in the future we can make win32 more - # like the other platforms - #find_library(FACESHIFT_LIBRARIES faceshift.lib ${FACESHIFT_ROOT_DIR}/lib/WIN32/) - endif () - - if (WIN32) - # Windows only cares about the headers - if (FACESHIFT_INCLUDE_DIRS) - set(FACESHIFT_FOUND TRUE) - endif (FACESHIFT_INCLUDE_DIRS) - else () - # Mac and Unix requires libraries - if (FACESHIFT_INCLUDE_DIRS AND FACESHIFT_LIBRARIES) - set(FACESHIFT_FOUND TRUE) - endif (FACESHIFT_INCLUDE_DIRS AND FACESHIFT_LIBRARIES) - endif () - - if (FACESHIFT_FOUND) - if (NOT FACESHIFT_FIND_QUIETLY) - message(STATUS "Found Faceshift... ${FACESHIFT_LIBRARIES}") - endif (NOT FACESHIFT_FIND_QUIETLY) - else () - if (FACESHIFT_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Faceshift") - endif (FACESHIFT_FIND_REQUIRED) - endif () - - # show the FACESHIFT_INCLUDE_DIRS and FACESHIFT_LIBRARIES variables only in the advanced view - mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES) +find_path(FACESHIFT_INCLUDE_DIRS fsbinarystream.h ${FACESHIFT_ROOT_DIR}/include) +if (APPLE) + find_library(FACESHIFT_LIBRARIES libfaceshift.a ${FACESHIFT_ROOT_DIR}/lib/MacOS/) +elseif (UNIX) + find_library(FACESHIFT_LIBRARIES libfaceshift.a ${FACESHIFT_ROOT_DIR}/lib/UNIX/) +elseif (WIN32) +# For windows, we're going to build the faceshift sources directly into the interface build +# and not link to a prebuilt library. This is because the VS2010 linker doesn't like cross-linking +# between release and debug libraries. If we change that in the future we can make win32 more +# like the other platforms + #find_library(FACESHIFT_LIBRARIES faceshift.lib ${FACESHIFT_ROOT_DIR}/lib/WIN32/) endif () + +if (WIN32) + # Windows only cares about the headers + if (FACESHIFT_INCLUDE_DIRS) + set(FACESHIFT_FOUND TRUE) + endif (FACESHIFT_INCLUDE_DIRS) +else () + # Mac and Unix requires libraries + if (FACESHIFT_INCLUDE_DIRS AND FACESHIFT_LIBRARIES) + set(FACESHIFT_FOUND TRUE) + endif (FACESHIFT_INCLUDE_DIRS AND FACESHIFT_LIBRARIES) +endif () + +if (FACESHIFT_FOUND) + if (NOT FACESHIFT_FIND_QUIETLY) + message(STATUS "Found Faceshift... ${FACESHIFT_LIBRARIES}") + endif (NOT FACESHIFT_FIND_QUIETLY) +else () + if (FACESHIFT_FIND_REQUIRED) + message(FATAL_ERROR "Could not find Faceshift") + endif (FACESHIFT_FIND_REQUIRED) +endif () + +# show the FACESHIFT_INCLUDE_DIRS and FACESHIFT_LIBRARIES variables only in the advanced view +mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES) \ No newline at end of file diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 4ec85c8161..674cbef7a4 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -24,7 +24,7 @@ find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_ find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${LIBOVR_SEARCH_DIRS}) # add the util include dir to the general include dirs -set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS}" "${LIBOVR_UTIL_INCLUDE_DIR}") +set(LIBOVR_INCLUDE_DIRS ${LIBOVR_INCLUDE_DIRS} ${LIBOVR_UTIL_INCLUDE_DIR}) if (APPLE) find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index e0bd8c1899..201487105d 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -183,7 +183,7 @@ endif (FACEPLUS_FOUND AND NOT DISABLE_FACEPLUS) # and with LibOVR for Oculus Rift if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) add_definitions(-DHAVE_LIBOVR) - include_directories(SYSTEM "${LIBOVR_INCLUDE_DIRS}") + include_directories(SYSTEM ${LIBOVR_INCLUDE_DIRS}) if (APPLE OR UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LIBOVR_INCLUDE_DIRS}") From 7f136247b865f8412567287b20f1793353334982 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 16:44:35 -0700 Subject: [PATCH 12/57] more patches to LibOVR module to fix stitched include path --- cmake/modules/FindLibOVR.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 674cbef7a4..598c5d231e 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -24,7 +24,7 @@ find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_ find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${LIBOVR_SEARCH_DIRS}) # add the util include dir to the general include dirs -set(LIBOVR_INCLUDE_DIRS ${LIBOVR_INCLUDE_DIRS} ${LIBOVR_UTIL_INCLUDE_DIR}) +set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS};${LIBOVR_UTIL_INCLUDE_DIR}") if (APPLE) find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) From 3fad0e6ac89573badeaad2550a7c040266404e86 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 16:49:17 -0700 Subject: [PATCH 13/57] cleanup qxmpp find module, use select_library_confgurations --- cmake/modules/FindQxmpp.cmake | 24 +++++++++++------------- interface/CMakeLists.txt | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/cmake/modules/FindQxmpp.cmake b/cmake/modules/FindQxmpp.cmake index 420186d969..ac10982d61 100644 --- a/cmake/modules/FindQxmpp.cmake +++ b/cmake/modules/FindQxmpp.cmake @@ -18,17 +18,15 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -if (QXMPP_LIBRARIES AND QXMPP_INCLUDE_DIRS) - # in cache already - set(QXMPP_FOUND TRUE) -else () - - set(QXMPP_SEARCH_DIRS "${QXMPP_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/qxmpp") - - find_path(QXMPP_INCLUDE_DIR QXmppClient.h PATH_SUFFIXES include/qxmpp HINTS ${QXMPP_SEARCH_DIRS}) +set(QXMPP_SEARCH_DIRS "${QXMPP_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/qxmpp") - find_library(QXMPP_LIBRARY NAMES qxmpp qxmpp0 qxmpp_d PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS}) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(QXMPP DEFAULT_MSG QXMPP_INCLUDE_DIR QXMPP_LIBRARY) -endif () \ No newline at end of file +find_path(QXMPP_INCLUDE_DIR QXmppClient.h PATH_SUFFIXES include/qxmpp HINTS ${QXMPP_SEARCH_DIRS}) + +find_library(QXMPP_LIBRARY_RELEASE NAMES qxmpp PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS}) +find_library(QXMPP_LIBRARY_DEBUG NAMES qxmpp_d PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS}) + +include(SelectLibraryConfigurations) +select_library_configurations(QXMPP) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(QXMPP DEFAULT_MSG QXMPP_INCLUDE_DIR QXMPP_LIBRARY) \ No newline at end of file diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 201487105d..7ce9144031 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -222,7 +222,7 @@ if (QXMPP_FOUND AND NOT DISABLE_QXMPP) add_definitions(-DHAVE_QXMPP -DQXMPP_STATIC) include_directories(SYSTEM ${QXMPP_INCLUDE_DIR}) - target_link_libraries(${TARGET_NAME} "${QXMPP_LIBRARY}") + target_link_libraries(${TARGET_NAME} ${QXMPP_LIBRARY}) endif (QXMPP_FOUND AND NOT DISABLE_QXMPP) # and with RtMidi for RtMidi control From a9df8b050e125f0663f689048245e6a3074a291d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 17:04:18 -0700 Subject: [PATCH 14/57] another attempt at fixing double include for LibOVR --- cmake/modules/FindLibOVR.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 598c5d231e..674cbef7a4 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -24,7 +24,7 @@ find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_ find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${LIBOVR_SEARCH_DIRS}) # add the util include dir to the general include dirs -set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS};${LIBOVR_UTIL_INCLUDE_DIR}") +set(LIBOVR_INCLUDE_DIRS ${LIBOVR_INCLUDE_DIRS} ${LIBOVR_UTIL_INCLUDE_DIR}) if (APPLE) find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) From cb384ac01b5a4378858a69af3d0b66c28eeea19e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 17:24:47 -0700 Subject: [PATCH 15/57] final repairs to LibOVR for multiple include folders --- cmake/modules/FindLibOVR.cmake | 2 +- interface/CMakeLists.txt | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 674cbef7a4..4ec85c8161 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -24,7 +24,7 @@ find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_ find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${LIBOVR_SEARCH_DIRS}) # add the util include dir to the general include dirs -set(LIBOVR_INCLUDE_DIRS ${LIBOVR_INCLUDE_DIRS} ${LIBOVR_UTIL_INCLUDE_DIR}) +set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS}" "${LIBOVR_UTIL_INCLUDE_DIR}") if (APPLE) find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 7ce9144031..c0ee8dc7df 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -184,9 +184,11 @@ endif (FACEPLUS_FOUND AND NOT DISABLE_FACEPLUS) if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) add_definitions(-DHAVE_LIBOVR) include_directories(SYSTEM ${LIBOVR_INCLUDE_DIRS}) - - if (APPLE OR UNIX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LIBOVR_INCLUDE_DIRS}") + + if (APPLE) + foreach(LIBOVR_DIR ${LIBOVR_INCLUDE_DIRS}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LIBOVR_DIR}") + endforeach() endif () target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) @@ -204,7 +206,7 @@ if (LEAPMOTION_FOUND AND NOT DISABLE_LEAPMOTION) add_definitions(-DHAVE_LEAPMOTION) include_directories(SYSTEM "${LEAPMOTION_INCLUDE_DIRS}") - if (APPLE OR UNIX) + if (APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${LEAPMOTION_INCLUDE_DIRS}") endif () target_link_libraries(${TARGET_NAME} ${LEAPMOTION_LIBRARIES}) From c8902fe554624192ff89a7d4e6a46b57bcf889ac Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 17:26:45 -0700 Subject: [PATCH 16/57] cleanup GLEW find module --- cmake/modules/FindGLEW.cmake | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGLEW.cmake index b621756885..9c927d43d0 100644 --- a/cmake/modules/FindGLEW.cmake +++ b/cmake/modules/FindGLEW.cmake @@ -18,27 +18,17 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -if (GLEW_INCLUDE_DIRS AND GLEW_LIBRARY) - set(GLEW_FOUND TRUE) -else () - if (WIN32) - set(WIN_GLEW_SEARCH_DIRS "${GLEW_ROOT_DIR}" "$ENV{GLEW_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/glew") - - find_path(GLEW_INCLUDE_DIRS GL/glew.h PATH_SUFFIXES include HINTS ${WIN_GLEW_SEARCH_DIRS}) - - find_library(GLEW_LIBRARY glew32s PATH_SUFFIXES "lib/Release/Win32" "lib" HINTS ${WIN_GLEW_SEARCH_DIRS}) - endif () - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARY) +if (WIN32) + set(WIN_GLEW_SEARCH_DIRS "${GLEW_ROOT_DIR}" "$ENV{GLEW_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/glew") + + find_path(GLEW_INCLUDE_DIRS GL/glew.h PATH_SUFFIXES include HINTS ${WIN_GLEW_SEARCH_DIRS}) - if (GLEW_FOUND) - if (NOT GLEW_FIND_QUIETLY) - message(STATUS "Found GLEW: ${GLEW_LIBRARY}") - endif () - else () - if (GLEW_FIND_REQUIRED) - message(FATAL_ERROR "Could not find GLEW") - endif () - endif () -endif () \ No newline at end of file + find_library(GLEW_LIBRARY_RELEASE glew32s PATH_SUFFIXES "lib/Release/Win32" "lib" HINTS ${WIN_GLEW_SEARCH_DIRS}) + find_library(GLEW_LIBRARY_DEBUG glew32s PATH_SUFFIXES "lib/Debug/Win32" "lib" HINTS ${WIN_GLEW_SEARCH_DIRS}) + + include(SelectLibraryConfigurations) + select_library_configurations(GLEW) +endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARY) \ No newline at end of file From 498510b42ee174d8d55703f27b53547a6ac2c44b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 17:30:06 -0700 Subject: [PATCH 17/57] cleanup GLM find module --- cmake/modules/FindGLM.cmake | 79 +++++++++---------------------------- 1 file changed, 18 insertions(+), 61 deletions(-) diff --git a/cmake/modules/FindGLM.cmake b/cmake/modules/FindGLM.cmake index 1bd1239193..f6247617b1 100644 --- a/cmake/modules/FindGLM.cmake +++ b/cmake/modules/FindGLM.cmake @@ -1,67 +1,24 @@ -# FindGLM - attempts to locate the glm matrix/vector library. # -# This module defines the following variables (on success): -# GLM_INCLUDE_DIRS - where to find glm/glm.hpp -# GLM_FOUND - if the library was successfully located -# -# It is trying a few standard installation locations, but can be customized -# with the following variables: -# GLM_ROOT_DIR - root directory of a glm installation -# Headers are expected to be found in either: -# /glm/glm.hpp OR -# /include/glm/glm.hpp -# This variable can either be a cmake or environment -# variable. Note however that changing the value -# of the environment varible will NOT result in -# re-running the header search and therefore NOT -# adjust the variables set by this module. - -# This is a modified version of the FindGLM module included with CMake. -# Copyright 2014 High Fidelity +# FindGLM.cmake # -#============================================================================= -# Copyright 2012 Carsten Neumann +# Try to find GLM include path. +# Once done this will define # -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) +# GLM_INCLUDE_DIRS +# +# Created on 7/17/2014 by Stephen Birarda +# Copyright 2014 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# -if (GLM_INCLUDE_DIR) - set(GLM_INCLUDE_DIRS "${GLM_INCLUDE_DIR}") - set(GLM_FOUND TRUE) -else () - # default search dirs - set(_glm_HEADER_SEARCH_DIRS "$ENV{HIFI_LIB_DIR}/glm" "/usr/include" "/usr/local/include") +# setup hints for GLM search +set(GLM_HEADER_SEARCH_HINTS "${GLM_ROOT_DIR}" "$ENV{GLM_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/glm") - # check environment variable - set(_glm_ENV_ROOT_DIR "$ENV{GLM_ROOT_DIR}") +# locate header +find_path(GLM_INCLUDE_DIR "glm/glm.hpp" HINTS ${GLM_HEADER_SEARCH_HINTS}) +set(GLM_INCLUDE_DIRS "${GLM_INCLUDE_DIR}") - if (NOT GLM_ROOT_DIR AND _glm_ENV_ROOT_DIR) - set(GLM_ROOT_DIR "${_glm_ENV_ROOT_DIR}") - endif () - - # put user specified location at beginning of search - if (GLM_ROOT_DIR) - set(_glm_HEADER_SEARCH_DIRS "${GLM_ROOT_DIR}" "${GLM_ROOT_DIR}/include" "$ENV{HIFI_LIB_DIR}/glm" ${_glm_HEADER_SEARCH_DIRS}) - ENDIF() - - # locate header - find_path(GLM_INCLUDE_DIR "glm/glm.hpp" PATHS ${_glm_HEADER_SEARCH_DIRS}) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(GLM DEFAULT_MSG GLM_INCLUDE_DIR) - - if (GLM_FOUND) - set(GLM_INCLUDE_DIRS "${GLM_INCLUDE_DIR}") - - if (NOT GLM_FIND_QUIETLY) - MESSAGE(STATUS "GLM_INCLUDE_DIR = ${GLM_INCLUDE_DIR}") - endif (NOT GLM_FIND_QUIETLY) - endif () -endif () \ No newline at end of file +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GLM DEFAULT_MSG GLM_INCLUDE_DIRS) \ No newline at end of file From fa545775a040f710cbcc1c3a377c8e86d0c404b6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 09:26:24 -0700 Subject: [PATCH 18/57] tweaks to FindLibOVR for udev and xinerama --- cmake/modules/FindLibOVR.cmake | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 4ec85c8161..c2f40a3d2d 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -30,8 +30,8 @@ if (APPLE) find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) find_library(LIBOVR_LIBRARY_RELEASE "Lib/MacOS/Release/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) elseif (UNIX) - find_library(UDEV_LIBRARY_RELEASE libudev.a /usr/lib/x86_64-linux-gnu/) - find_library(XINERAMA_LIBRARY_RELEASE libXinerama.a /usr/lib/x86_64-linux-gnu/) + find_library(UDEV_LIBRARY_RELEASE udev /usr/lib/x86_64-linux-gnu/) + find_library(XINERAMA_LIBRARY_RELEASE Xinerama /usr/lib/x86_64-linux-gnu/) if (CMAKE_CL_64) set(LINUX_ARCH_DIR "i386") @@ -42,9 +42,6 @@ elseif (UNIX) find_library(LIBOVR_LIBRARY_DEBUG "Lib/Linux/Debug/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) find_library(LIBOVR_LIBRARY_RELEASE "Lib/Linux/Release/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) - if (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY) - set(LIBOVR_LIBRARIES "${OVR_LIBRARY};${UDEV_LIBRARY};${XINERAMA_LIBRARY}" CACHE INTERNAL "Oculus libraries") - endif (UDEV_LIBRARY AND XINERAMA_LIBRARY AND OVR_LIBRARY) elseif (WIN32) find_library(LIBOVR_LIBRARY_DEBUG "Lib/Win32/libovrd.lib" HINTS ${LIBOVR_SEARCH_DIRS}) find_library(LIBOVR_LIBRARY_RELEASE "Lib/Win32/libovr.lib" HINTS ${LIBOVR_SEARCH_DIRS}) @@ -61,4 +58,8 @@ if (UNIX) endif () include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LIBOVR DEFAULT_MSG LIBOVR_INCLUDE_DIRS LIBOVR_UTIL_INCLUDE_DIR LIBOVR_LIBRARIES) +if (UNIX) + find_package_handle_standard_args(LIBOVR DEFAULT_MSG LIBOVR_INCLUDE_DIRS LIBOVR_UTIL_INCLUDE_DIR LIBOVR_LIBRARIES) +elseif () + find_package_handle_standard_args(LIBOVR DEFAULT_MSG LIBOVR_INCLUDE_DIRS LIBOVR_UTIL_INCLUDE_DIR LIBOVR_LIBRARIES UDEV_LIBRARY XINERAMA_LIBRARY) +endif () From 5ec710ddf6545cb9c18c2fc83f9ead6029c661f1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 09:40:23 -0700 Subject: [PATCH 19/57] fix re-set of LIBOVR_LIBRARIES in find module --- cmake/modules/FindLibOVR.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index c2f40a3d2d..06cee5e87e 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -54,7 +54,7 @@ if (UNIX) select_library_configurations(UDEV) select_library_configurations(XINERAMA) - set(${LIBOVR_LIBRARIES} ${LIBOVR_LIBRARIES} ${UDEV_LIBRARY} ${XINERAMA_LIBRARY}) + set(LIBOVR_LIBRARIES ${LIBOVR_LIBRARIES} ${UDEV_LIBRARY} ${XINERAMA_LIBRARY}) endif () include(FindPackageHandleStandardArgs) From 1bbc042d2b77a09aae7e29fa77b4f1fcdd56e424 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 10:01:23 -0700 Subject: [PATCH 20/57] simplify the FindGLUT.cmake module --- cmake/modules/FindGLUT.cmake | 88 +++++++++--------------------------- 1 file changed, 21 insertions(+), 67 deletions(-) diff --git a/cmake/modules/FindGLUT.cmake b/cmake/modules/FindGLUT.cmake index 280a6ac6db..c84bf49906 100644 --- a/cmake/modules/FindGLUT.cmake +++ b/cmake/modules/FindGLUT.cmake @@ -5,7 +5,7 @@ # Once done this will define # # GLUT_FOUND -# GLUT_INCLUDE_DIR +# GLUT_INCLUDE_DIRS # GLUT_LIBRARIES # GLUT_DLL_PATH - Optionally defined for Win32, if not in path # @@ -19,73 +19,27 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -if (GLUT_INCLUDE_DIR AND GLUT_LIBRARIES) - set(GLUT_FOUND TRUE) -else () - if (WIN32) - set(WIN_GLUT_SEARCH_DIRS "${GLUT_ROOT_DIR}" "$ENV{GLUT_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/freeglut" "${OPENGL_INCLUDE_DIR}") - find_path(GLUT_INCLUDE_DIR GL/glut.h PATH_SUFFIXES include HINTS ${WIN_GLUT_SEARCH_DIRS}) +set(GLUT_HINT_DIRS "${GLUT_ROOT_DIR}" "$ENV{GLUT_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/freeglut") - find_library(GLUT_glut_LIBRARY freeglut PATH_SUFFIXES lib HINTS ${WIN_GLUT_SEARCH_DIRS}) - else () - find_path(GLUT_INCLUDE_DIR GL/glut.h - "${GLUT_LOCATION}/include" - "$ENV{GLUT_LOCATION}/include" - /usr/include - /usr/include/GL - /usr/local/include - /usr/openwin/share/include - /usr/openwin/include - /usr/X11R6/include - /usr/include/X11 - /opt/graphics/OpenGL/include - /opt/graphics/OpenGL/contrib/libglut - ) - find_library(GLUT_glut_LIBRARY glut - "${GLUT_LOCATION}/lib" - "$ENV{GLUT_LOCATION}/lib" - /usr/lib - /usr/local/lib - /usr/openwin/lib - /usr/X11R6/lib - ) - find_library(GLUT_Xi_LIBRARY Xi - "${GLUT_LOCATION}/lib" - "$ENV{GLUT_LOCATION}/lib" - /usr/lib - /usr/local/lib - /usr/openwin/lib - /usr/X11R6/lib - ) - find_library(GLUT_Xmu_LIBRARY Xmu - "${GLUT_LOCATION}/lib" - "$ENV{GLUT_LOCATION}/lib" - /usr/lib - /usr/local/lib - /usr/openwin/lib - /usr/X11R6/lib - ) - endif () - - if (GLUT_INCLUDE_DIR AND GLUT_glut_LIBRARY) - # Is -lXi and -lXmu required on all platforms that have it? - # If not, we need some way to figure out what platform we are on. - set(GLUT_LIBRARIES ${GLUT_glut_LIBRARY} ${GLUT_Xmu_LIBRARY} ${GLUT_Xi_LIBRARY}) - endif () - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(GLUT DEFAULT_MSG GLUT_INCLUDE_DIR GLUT_LIBRARIES) +if (WIN32) + set(GLUT_HINT_DIRS "${GLUT_HINT_DIRS}" "${OPENGL_INCLUDE_DIR}") - if (GLUT_FOUND) - if (NOT GLUT_FIND_QUIETLY) - message(STATUS "Found GLUT: ${GLUT_LIBRARIES}") - endif () - else () - if (GLUT_FIND_REQUIRED) - message(FATAL_ERROR "Could not find GLUT") - endif () - endif () + find_path(GLUT_INCLUDE_DIR GL/glut.h PATH_SUFFIXES include HINTS ${GLUT_HINT_DIRS}) + find_library(GLUT_LIBRARY_RELEASE freeglut PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) +else () + find_path(GLUT_INCLUDE_DIR GL/glut.h PATH_SUFFIXES include HINTS ${GLUT_HINT_DIRS}) + find_library(GLUT_LIBRARY glut PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) +endif () - mark_as_advanced(GLUT_glut_LIBRARY GLUT_Xmu_LIBRARY GLUT_Xi_LIBRARY) +include(FindPackageHandleStandardArgs) -endif () \ No newline at end of file +if (UNIX) + find_library(XI_LIBRARY Xi PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) + find_library(XMU_LIBRARY Xmu PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) + + find_package_handle_standard_args(GLUT DEFAULT_MSG GLUT_INCLUDE_DIRS GLUT_LIBRARIES XI_LIBRARY XMU_LIBRARY) +else () + find_package_handle_standard_args(GLUT DEFAULT_MSG GLUT_INCLUDE_DIRS GLUT_LIBRARIES) +endif () + +set(GLUT_LIBRARIES ${GLUT_LIBRARY} ${XMU_LIBRARY} ${XI_LIBRARY}) \ No newline at end of file From b0864b01de7b96dee11f1b843496c6cfd386d4a8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 10:05:58 -0700 Subject: [PATCH 21/57] cleanup the FindPrioVR module and fix GLUT include --- cmake/modules/FindPrioVR.cmake | 33 ++++++++------------------------- interface/CMakeLists.txt | 2 +- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/cmake/modules/FindPrioVR.cmake b/cmake/modules/FindPrioVR.cmake index 46710d8b5e..dcf9f736e9 100644 --- a/cmake/modules/FindPrioVR.cmake +++ b/cmake/modules/FindPrioVR.cmake @@ -12,31 +12,14 @@ # Copyright (c) 2014 High Fidelity # -if (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) - # in cache already - set(PRIOVR_FOUND TRUE) -else (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) - find_path(PRIOVR_INCLUDE_DIRS yei_skeletal_api.h ${PRIOVR_ROOT_DIR}/include) +find_path(PRIOVR_INCLUDE_DIRS yei_skeletal_api.h ${PRIOVR_ROOT_DIR}/include) - if (WIN32) - find_library(PRIOVR_LIBRARIES Skeletal_API.lib ${PRIOVR_ROOT_DIR}/lib) - endif (WIN32) - - if (PRIOVR_INCLUDE_DIRS AND PRIOVR_LIBRARIES) - set(PRIOVR_FOUND TRUE) - endif (PRIOVR_INCLUDE_DIRS AND PRIOVR_LIBRARIES) +if (WIN32) + find_library(PRIOVR_LIBRARIES Skeletal_API.lib ${PRIOVR_ROOT_DIR}/lib) +endif (WIN32) - if (PRIOVR_FOUND) - if (NOT PRIOVR_FIND_QUIETLY) - message(STATUS "Found PrioVR... ${PRIOVR_LIBRARIES}") - endif (NOT PRIOVR_FIND_QUIETLY) - else () - if (PRIOVR_FIND_REQUIRED) - message(FATAL_ERROR "Could not find PrioVR") - endif (PRIOVR_FIND_REQUIRED) - endif () +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PRIOVR DEFAULT_MSG PRIOVR_INCLUDE_DIRS PRIOVR_LIBRARIES) - # show the PRIOVR_INCLUDE_DIRS and PRIOVR_LIBRARIES variables only in the advanced view - mark_as_advanced(PRIOVR_INCLUDE_DIRS PRIOVR_LIBRARIES) - -endif (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) +# show the PRIOVR_INCLUDE_DIRS and PRIOVR_LIBRARIES variables only in the advanced view +mark_as_advanced(PRIOVR_INCLUDE_DIRS PRIOVR_LIBRARIES) \ No newline at end of file diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index c0ee8dc7df..fc46a2a64b 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -303,7 +303,7 @@ else (APPLE) find_package(OpenGL REQUIRED) find_package(GLUT REQUIRED) - include_directories(SYSTEM "${GLUT_INCLUDE_DIR}") + include_directories(SYSTEM "${GLUT_INCLUDE_DIRS}") if (${OPENGL_INCLUDE_DIR}) include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") From 39230d5dca734ac2736f68e7f92649d0b2261e06 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 10:07:00 -0700 Subject: [PATCH 22/57] pluralize GLUT include var in find module --- cmake/modules/FindGLUT.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/FindGLUT.cmake b/cmake/modules/FindGLUT.cmake index c84bf49906..ed92349f4d 100644 --- a/cmake/modules/FindGLUT.cmake +++ b/cmake/modules/FindGLUT.cmake @@ -24,11 +24,11 @@ set(GLUT_HINT_DIRS "${GLUT_ROOT_DIR}" "$ENV{GLUT_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/ if (WIN32) set(GLUT_HINT_DIRS "${GLUT_HINT_DIRS}" "${OPENGL_INCLUDE_DIR}") - find_path(GLUT_INCLUDE_DIR GL/glut.h PATH_SUFFIXES include HINTS ${GLUT_HINT_DIRS}) + find_path(GLUT_INCLUDE_DIRS GL/glut.h PATH_SUFFIXES include HINTS ${GLUT_HINT_DIRS}) find_library(GLUT_LIBRARY_RELEASE freeglut PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) else () - find_path(GLUT_INCLUDE_DIR GL/glut.h PATH_SUFFIXES include HINTS ${GLUT_HINT_DIRS}) - find_library(GLUT_LIBRARY glut PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) + find_path(GLUT_INCLUDE_DIRS GL/glut.h PATH_SUFFIXES include HINTS ${GLUT_HINT_DIRS}) + find_library(GLUT_LIBRARY glut PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) endif () include(FindPackageHandleStandardArgs) From 2f84e1f2366272485271b47ad8404720372347ae Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 10:12:17 -0700 Subject: [PATCH 23/57] use mark_as_advanced in find modules to hide vars from advanced view --- cmake/modules/FindFaceplus.cmake | 4 +++- cmake/modules/FindGLEW.cmake | 8 ++++++-- cmake/modules/FindGLM.cmake | 4 +++- cmake/modules/FindGLUT.cmake | 5 +++-- cmake/modules/FindLeapMotion.cmake | 4 ++++ cmake/modules/FindPrioVR.cmake | 1 - 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/cmake/modules/FindFaceplus.cmake b/cmake/modules/FindFaceplus.cmake index bdb783685f..1050493c69 100644 --- a/cmake/modules/FindFaceplus.cmake +++ b/cmake/modules/FindFaceplus.cmake @@ -19,4 +19,6 @@ if (WIN32) endif (WIN32) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(FACEPLUS DEFAULT_MSG FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES) \ No newline at end of file +find_package_handle_standard_args(FACEPLUS DEFAULT_MSG FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES) + +mark_as_advanced(FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES) \ No newline at end of file diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGLEW.cmake index 9c927d43d0..59927dc468 100644 --- a/cmake/modules/FindGLEW.cmake +++ b/cmake/modules/FindGLEW.cmake @@ -6,7 +6,7 @@ # # GLEW_FOUND # GLEW_INCLUDE_DIRS -# GLEW_LIBRARY +# GLEW_LIBRARIES # # Created on 2/6/2014 by Stephen Birarda # Copyright 2014 High Fidelity, Inc. @@ -30,5 +30,9 @@ if (WIN32) select_library_configurations(GLEW) endif () +set(GLEW_LIBRARIES "${GLEW_LIBRARY}") + include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARY) \ No newline at end of file +find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES) + +mark_as_advanced(GLEW_INCLUDE_DIRS GLEW_LIBRARIES) \ No newline at end of file diff --git a/cmake/modules/FindGLM.cmake b/cmake/modules/FindGLM.cmake index f6247617b1..04bc252796 100644 --- a/cmake/modules/FindGLM.cmake +++ b/cmake/modules/FindGLM.cmake @@ -21,4 +21,6 @@ find_path(GLM_INCLUDE_DIR "glm/glm.hpp" HINTS ${GLM_HEADER_SEARCH_HINTS}) set(GLM_INCLUDE_DIRS "${GLM_INCLUDE_DIR}") include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GLM DEFAULT_MSG GLM_INCLUDE_DIRS) \ No newline at end of file +find_package_handle_standard_args(GLM DEFAULT_MSG GLM_INCLUDE_DIRS) + +mark_as_advanced(GLM_INCLUDE_DIRS) \ No newline at end of file diff --git a/cmake/modules/FindGLUT.cmake b/cmake/modules/FindGLUT.cmake index ed92349f4d..c6906a5748 100644 --- a/cmake/modules/FindGLUT.cmake +++ b/cmake/modules/FindGLUT.cmake @@ -7,7 +7,6 @@ # GLUT_FOUND # GLUT_INCLUDE_DIRS # GLUT_LIBRARIES -# GLUT_DLL_PATH - Optionally defined for Win32, if not in path # # Created on 2/6/2014 by Stephen Birarda # Copyright 2014 High Fidelity, Inc. @@ -33,6 +32,8 @@ endif () include(FindPackageHandleStandardArgs) +set(GLUT_LIBRARIES "${GLUT_LIBRARY}" "${XMU_LIBRARY}" "${XI_LIBRARY}") + if (UNIX) find_library(XI_LIBRARY Xi PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) find_library(XMU_LIBRARY Xmu PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) @@ -42,4 +43,4 @@ else () find_package_handle_standard_args(GLUT DEFAULT_MSG GLUT_INCLUDE_DIRS GLUT_LIBRARIES) endif () -set(GLUT_LIBRARIES ${GLUT_LIBRARY} ${XMU_LIBRARY} ${XI_LIBRARY}) \ No newline at end of file +mark_as_advanced(GLUT_INCLUDE_DIRS GLUT_LIBRARIES GLUT_LIBRARY XI_LIBRARY XMU_LIBRARY) \ No newline at end of file diff --git a/cmake/modules/FindLeapMotion.cmake b/cmake/modules/FindLeapMotion.cmake index 1066be46af..91ac080c30 100644 --- a/cmake/modules/FindLeapMotion.cmake +++ b/cmake/modules/FindLeapMotion.cmake @@ -27,5 +27,9 @@ endif (APPLE) include(SelectLibraryConfigurations) select_library_configurations(LEAPMOTION) +set(LEAPMOTION_LIBRARIES "${LEAPMOTION_LIBARIES}") + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LEAPMOTION DEFAULT_MSG LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES) + +mark_as_advanced(LEAPMOTION_INCLUDE_DIRS LEAPMOTION_LIBRARIES) diff --git a/cmake/modules/FindPrioVR.cmake b/cmake/modules/FindPrioVR.cmake index dcf9f736e9..af2c0aca52 100644 --- a/cmake/modules/FindPrioVR.cmake +++ b/cmake/modules/FindPrioVR.cmake @@ -21,5 +21,4 @@ endif (WIN32) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PRIOVR DEFAULT_MSG PRIOVR_INCLUDE_DIRS PRIOVR_LIBRARIES) -# show the PRIOVR_INCLUDE_DIRS and PRIOVR_LIBRARIES variables only in the advanced view mark_as_advanced(PRIOVR_INCLUDE_DIRS PRIOVR_LIBRARIES) \ No newline at end of file From e619393ac0001b2bfded1d161af4d5df0ef4a2f9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 10:12:52 -0700 Subject: [PATCH 24/57] link to GLEW_LIBRARIES instead of GLEW_LIBRARY in interface CMakeLists --- interface/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index fc46a2a64b..d6e776aad2 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -322,6 +322,6 @@ else (APPLE) # add a definition for ssize_t so that windows doesn't bail add_definitions(-Dssize_t=long) - target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARY}" wsock32.lib opengl32.lib) + target_link_libraries(${TARGET_NAME} "${GLEW_LIBRARIES}" wsock32.lib opengl32.lib) endif() endif (APPLE) From 9e83040fb4ce1ff34858c40cb29b86a5d8e72371 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 10:20:12 -0700 Subject: [PATCH 25/57] more find module standardization and cleanup --- cmake/modules/FindLibOVR.cmake | 15 ++++++++------- cmake/modules/FindQxmpp.cmake | 10 +++++++--- cmake/modules/FindRtMidi.cmake | 10 ++++++---- cmake/modules/FindSixense.cmake | 4 ++++ interface/CMakeLists.txt | 8 ++++---- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 06cee5e87e..73c69adbcb 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -26,6 +26,8 @@ find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HI # add the util include dir to the general include dirs set(LIBOVR_INCLUDE_DIRS "${LIBOVR_INCLUDE_DIRS}" "${LIBOVR_UTIL_INCLUDE_DIR}") +include(SelectLibraryConfigurations) + if (APPLE) find_library(LIBOVR_LIBRARY_DEBUG "Lib/MacOS/Debug/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) find_library(LIBOVR_LIBRARY_RELEASE "Lib/MacOS/Release/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) @@ -42,20 +44,17 @@ elseif (UNIX) find_library(LIBOVR_LIBRARY_DEBUG "Lib/Linux/Debug/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) find_library(LIBOVR_LIBRARY_RELEASE "Lib/Linux/Release/${LINUX_ARCH_DIR}/libovr.a" HINTS ${LIBOVR_SEARCH_DIRS}) + select_library_configurations(UDEV) + select_library_configurations(XINERAMA) + elseif (WIN32) find_library(LIBOVR_LIBRARY_DEBUG "Lib/Win32/libovrd.lib" HINTS ${LIBOVR_SEARCH_DIRS}) find_library(LIBOVR_LIBRARY_RELEASE "Lib/Win32/libovr.lib" HINTS ${LIBOVR_SEARCH_DIRS}) endif () -include(SelectLibraryConfigurations) select_library_configurations(LIBOVR) -if (UNIX) - select_library_configurations(UDEV) - select_library_configurations(XINERAMA) - - set(LIBOVR_LIBRARIES ${LIBOVR_LIBRARIES} ${UDEV_LIBRARY} ${XINERAMA_LIBRARY}) -endif () +set(LIBOVR_LIBRARIES "${LIBOVR_LIBRARIES}" "${UDEV_LIBRARY}" "${XINERAMA_LIBRARY}") include(FindPackageHandleStandardArgs) if (UNIX) @@ -63,3 +62,5 @@ if (UNIX) elseif () find_package_handle_standard_args(LIBOVR DEFAULT_MSG LIBOVR_INCLUDE_DIRS LIBOVR_UTIL_INCLUDE_DIR LIBOVR_LIBRARIES UDEV_LIBRARY XINERAMA_LIBRARY) endif () + +mark_as_advanced(LIBOVR_INCLUDE_DIRS LIBOVR_LIBRARIES) diff --git a/cmake/modules/FindQxmpp.cmake b/cmake/modules/FindQxmpp.cmake index ac10982d61..71c99369aa 100644 --- a/cmake/modules/FindQxmpp.cmake +++ b/cmake/modules/FindQxmpp.cmake @@ -9,7 +9,7 @@ # # QXMPP_FOUND - system found qxmpp # QXMPP_INCLUDE_DIRS - the qxmpp include directory -# QXMPP_LIBRARY - Link this to use qxmpp +# QXMPP_LIBRARIES - Link this to use qxmpp # # Created on 3/10/2014 by Stephen Birarda # Copyright 2014 High Fidelity, Inc. @@ -20,7 +20,7 @@ set(QXMPP_SEARCH_DIRS "${QXMPP_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/qxmpp") -find_path(QXMPP_INCLUDE_DIR QXmppClient.h PATH_SUFFIXES include/qxmpp HINTS ${QXMPP_SEARCH_DIRS}) +find_path(QXMPP_INCLUDE_DIRS QXmppClient.h PATH_SUFFIXES include/qxmpp HINTS ${QXMPP_SEARCH_DIRS}) find_library(QXMPP_LIBRARY_RELEASE NAMES qxmpp PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS}) find_library(QXMPP_LIBRARY_DEBUG NAMES qxmpp_d PATH_SUFFIXES lib HINTS ${QXMPP_SEARCH_DIRS}) @@ -28,5 +28,9 @@ find_library(QXMPP_LIBRARY_DEBUG NAMES qxmpp_d PATH_SUFFIXES lib HINTS ${QXMPP_S include(SelectLibraryConfigurations) select_library_configurations(QXMPP) +set(QXMPP_LIBRARIES "${QXMPP_LIBRARY}") + include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(QXMPP DEFAULT_MSG QXMPP_INCLUDE_DIR QXMPP_LIBRARY) \ No newline at end of file +find_package_handle_standard_args(QXMPP DEFAULT_MSG QXMPP_INCLUDE_DIRS QXMPP_LIBRARIES) + +mark_as_advanced(QXMPP_INCLUDE_DIRS QXMPP_LIBRARIES) \ No newline at end of file diff --git a/cmake/modules/FindRtMidi.cmake b/cmake/modules/FindRtMidi.cmake index 0b3084b7fb..d03477710c 100644 --- a/cmake/modules/FindRtMidi.cmake +++ b/cmake/modules/FindRtMidi.cmake @@ -9,7 +9,7 @@ # # RTMIDI_FOUND - system found RtMidi # RTMIDI_INCLUDE_DIRS - the RtMidi include directory -# RTMIDI_CPP - Include this with src to use RtMidi +# RTMIDI_LIBRARIES - link to this to use RtMidi # # Created on 6/30/2014 by Stephen Birarda # Copyright 2014 High Fidelity, Inc. @@ -20,8 +20,10 @@ set(RTMIDI_SEARCH_DIRS "${RTMIDI_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/rtmidi") -find_path(RTMIDI_INCLUDE_DIR RtMidi.h PATH_SUFFIXES include HINTS ${RTMIDI_SEARCH_DIRS}) -find_library(RTMIDI_LIBRARY NAMES rtmidi PATH_SUFFIXES lib HINTS ${RTMIDI_SEARCH_DIRS}) +find_path(RTMIDI_INCLUDE_DIRS RtMidi.h PATH_SUFFIXES include HINTS ${RTMIDI_SEARCH_DIRS}) +find_library(RTMIDI_LIBRARIES NAMES rtmidi PATH_SUFFIXES lib HINTS ${RTMIDI_SEARCH_DIRS}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(RTMIDI DEFAULT_MSG RTMIDI_INCLUDE_DIR RTMIDI_LIBRARY) \ No newline at end of file +find_package_handle_standard_args(RTMIDI DEFAULT_MSG RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES) + +mark_as_advanced(RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES) \ No newline at end of file diff --git a/cmake/modules/FindSixense.cmake b/cmake/modules/FindSixense.cmake index d1958e2ab3..9b8209ef29 100644 --- a/cmake/modules/FindSixense.cmake +++ b/cmake/modules/FindSixense.cmake @@ -36,5 +36,9 @@ endif () include(SelectLibraryConfigurations) select_library_configurations(SIXENSE) +set(SIXENSE_LIBRARIES "${SIXENSE_LIBRARY}") + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SIXENSE DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES) + +mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d6e776aad2..97b6413106 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -222,17 +222,17 @@ endif (SDL_FOUND AND NOT DISABLE_SDL) # and with qxmpp for chat if (QXMPP_FOUND AND NOT DISABLE_QXMPP) add_definitions(-DHAVE_QXMPP -DQXMPP_STATIC) - include_directories(SYSTEM ${QXMPP_INCLUDE_DIR}) + include_directories(SYSTEM "${QXMPP_INCLUDE_DIRS}") - target_link_libraries(${TARGET_NAME} ${QXMPP_LIBRARY}) + target_link_libraries(${TARGET_NAME} ${QXMPP_LIBRARIES}) endif (QXMPP_FOUND AND NOT DISABLE_QXMPP) # and with RtMidi for RtMidi control if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI) add_definitions(-DHAVE_RTMIDI) - include_directories(SYSTEM ${RTMIDI_INCLUDE_DIR}) - target_link_libraries(${TARGET_NAME} "${RTMIDI_LIBRARY}") + include_directories(SYSTEM "${RTMIDI_INCLUDE_DIRS}") + target_link_libraries(${TARGET_NAME} ${RTMIDI_LIBRARIES}) if (APPLE) find_library(CoreMIDI CoreMIDI) From 2a74df1dfa207c68358c83c8d282fb53c6604a58 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 10:27:28 -0700 Subject: [PATCH 26/57] GLUT module is not using select_library_configurations --- cmake/modules/FindGLUT.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindGLUT.cmake b/cmake/modules/FindGLUT.cmake index c6906a5748..881e649ecd 100644 --- a/cmake/modules/FindGLUT.cmake +++ b/cmake/modules/FindGLUT.cmake @@ -24,7 +24,7 @@ if (WIN32) set(GLUT_HINT_DIRS "${GLUT_HINT_DIRS}" "${OPENGL_INCLUDE_DIR}") find_path(GLUT_INCLUDE_DIRS GL/glut.h PATH_SUFFIXES include HINTS ${GLUT_HINT_DIRS}) - find_library(GLUT_LIBRARY_RELEASE freeglut PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) + find_library(GLUT_LIBRARY freeglut PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) else () find_path(GLUT_INCLUDE_DIRS GL/glut.h PATH_SUFFIXES include HINTS ${GLUT_HINT_DIRS}) find_library(GLUT_LIBRARY glut PATH_SUFFIXES lib HINTS ${GLUT_HINT_DIRS}) From b5a011724350e41d8a61042790f3e4310403b962 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 10:37:45 -0700 Subject: [PATCH 27/57] cleanup the visage find module --- cmake/modules/FindLibOVR.cmake | 2 +- cmake/modules/FindVisage.cmake | 95 +++++++++++++--------------------- 2 files changed, 36 insertions(+), 61 deletions(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index 73c69adbcb..738a30519c 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -18,7 +18,7 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -set(LIBOVR_SEARCH_DIRS "${LIBOVR_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/oculus") +set(LIBOVR_SEARCH_DIRS "${LIBOVR_ROOT_DIR}" "$ENV{LIBOVR_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/oculus") find_path(LIBOVR_INCLUDE_DIRS OVR.h PATH_SUFFIXES Include HINTS ${LIBOVR_SEARCH_DIRS}) find_path(LIBOVR_UTIL_INCLUDE_DIR Util_Render_Stereo.h PATH_SUFFIXES Src/Util HINTS ${LIBOVR_SEARCH_DIRS}) diff --git a/cmake/modules/FindVisage.cmake b/cmake/modules/FindVisage.cmake index 0aa57f6508..46cf9e684a 100644 --- a/cmake/modules/FindVisage.cmake +++ b/cmake/modules/FindVisage.cmake @@ -18,65 +18,40 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -if (VISAGE_LIBRARIES AND VISAGE_INCLUDE_DIRS) - # in cache already - set(VISAGE_FOUND TRUE) -else () - find_path(VISAGE_INCLUDE_DIR VisageTracker2.h ${VISAGE_ROOT_DIR}/include) +set(VISAGE_SEARCH_DIRS "${VISAGE_ROOT_DIR}" "$ENV{VISAGE_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/visage") + +find_path(VISAGE_INCLUDE_DIR VisageTracker2.h ${VISAGE_ROOT_DIR}/include) + +if (APPLE) + find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h HINTS ${VISAGE_SEARCH_DIRS} /usr/include/libxml2) + find_path(VISAGE_OPENCV_INCLUDE_DIR cv.h PATH_SUFFIXES dependencies/OpenCV_MacOSX/include HINTS ${VISAGE_SEARCH_DIRS}) + find_path(VISAGE_OPENCV2_INCLUDE_DIR opencv.hpp PATH_SUFFIXES dependencies/OpenCV_MacOSX/include/opencv2 HINTS ${VISAGE_SEARCH_DIRS}) - if (APPLE) - find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h /usr/include/libxml2) - find_path(VISAGE_OPENCV_INCLUDE_DIR cv.h ${VISAGE_ROOT_DIR}/dependencies/OpenCV_MacOSX/include) - find_path(VISAGE_OPENCV2_INCLUDE_DIR opencv.hpp ${VISAGE_ROOT_DIR}/dependencies/OpenCV_MacOSX/include/opencv2) - if (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR) - set(VISAGE_INCLUDE_DIRS - "${VISAGE_INCLUDE_DIR};${VISAGE_XML_INCLUDE_DIR};${VISAGE_OPENCV_INCLUDE_DIR};${VISAGE_OPENCV2_INCLUDE_DIR}" - CACHE INTERNAL "Visage include dirs") - endif (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR) - - find_library(VISAGE_CORE_LIBRARY libvscore.a ${VISAGE_ROOT_DIR}/lib) - find_library(VISAGE_VISION_LIBRARY libvsvision.a ${VISAGE_ROOT_DIR}/lib) - find_library(VISAGE_OPENCV_LIBRARY libOpenCV.a ${VISAGE_ROOT_DIR}/dependencies/OpenCV_MacOSX/lib) - if (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY) - set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY};${VISAGE_VISION_LIBRARY};${VISAGE_OPENCV_LIBRARY}" - CACHE INTERNAL "Visage libraries") - endif (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY) - - elseif (WIN32) - find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h ${VISAGE_ROOT_DIR}/dependencies/libxml2/include) - find_path(VISAGE_OPENCV_INCLUDE_DIR opencv/cv.h ${VISAGE_ROOT_DIR}/dependencies/OpenCV/include) - find_path(VISAGE_OPENCV2_INCLUDE_DIR cv.h ${VISAGE_ROOT_DIR}/dependencies/OpenCV/include/opencv) - if (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR) - set(VISAGE_INCLUDE_DIRS - "${VISAGE_INCLUDE_DIR};${VISAGE_XML_INCLUDE_DIR};${VISAGE_OPENCV_INCLUDE_DIR};${VISAGE_OPENCV2_INCLUDE_DIR}" - CACHE INTERNAL "Visage include dirs") - endif (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR) - - find_library(VISAGE_CORE_LIBRARY vscore.lib ${VISAGE_ROOT_DIR}/lib) - find_library(VISAGE_VISION_LIBRARY vsvision.lib ${VISAGE_ROOT_DIR}/lib) - find_library(VISAGE_OPENCV_LIBRARY opencv_core243.lib ${VISAGE_ROOT_DIR}/dependencies/OpenCV/lib) - if (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY) - set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY};${VISAGE_VISION_LIBRARY};${VISAGE_OPENCV_LIBRARY}" - CACHE INTERNAL "Visage libraries") - endif (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY) - - endif () - - if (VISAGE_INCLUDE_DIRS AND VISAGE_LIBRARIES) - set(VISAGE_FOUND TRUE) - endif (VISAGE_INCLUDE_DIRS AND VISAGE_LIBRARIES) - - if (VISAGE_FOUND) - if (NOT VISAGE_FIND_QUIETLY) - message(STATUS "Found Visage: ${VISAGE_LIBRARIES}") - endif (NOT VISAGE_FIND_QUIETLY) - else (VISAGE_FOUND) - if (VISAGE_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Visage") - endif (VISAGE_FIND_REQUIRED) - endif (VISAGE_FOUND) - - # show the VISAGE_INCLUDE_DIRS and VISAGE_LIBRARIES variables only in the advanced view - mark_as_advanced(VISAGE_INCLUDE_DIRS VISAGE_LIBRARIES) - + find_library(VISAGE_CORE_LIBRARY NAME vscore PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS}) + find_library(VISAGE_VISION_LIBRARY NAME vsvision PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS}) + find_library(VISAGE_OPENCV_LIBRARY NAME OpenCV PATH_SUFFIXES dependencies/OpenCV_MacOSX/lib ${VISAGE_SEARCH_DIRS}) + +elseif (WIN32) + find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h PATH_SUFFIXES dependencies/libxml2/include HINTS ${VISAGE_SEARCH_DIRS}) + find_path(VISAGE_OPENCV_INCLUDE_DIR opencv/cv.h PATH_SUFFIXES dependencies/OpenCV/include HINTS ${VISAGE_SEARCH_DIRS}) + find_path(VISAGE_OPENCV2_INCLUDE_DIR cv.h PATH_SUFFIXES dependencies/OpenCV/include/opencv HINTS ${VISAGE_SEARCH_DIRS}) + + find_library(VISAGE_CORE_LIBRARY NAME vscore PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS}) + find_library(VISAGE_VISION_LIBRARY NAME vsvision PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS}) + find_library(VISAGE_OPENCV_LIBRARY NAME opencv_core243 PATH_SUFFIXES dependencies/OpenCV/lib HINTS ${VISAGE_SEARCH_DIRS}) endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(VISAGE DEFAULT_MSG + VISAGE_INCLUDE_DIR VISAGE_XML_INCLUDE_DIR VISAGE_OPENCV_INCLUDE_DIR VISAGE_OPENCV2_INCLUDE_DIR + VISAGE_CORE_LIBRARY VISAGE_VISION_LIBRARY VISAGE_OPENCV_LIBRARY +) + +set(VISAGE_INCLUDE_DIRS "${VISAGE_XML_INCLUDE_DIR}" "${VISAGE_OPENCV_INCLUDE_DIR}" "${VISAGE_OPENCV2_INCLUDE_DIR}") +set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY}" "${VISAGE_VISION_LIBRARY}" "${VISAGE_OPENCV_LIBRARY}") + +mark_as_advanced( + VISAGE_INCLUDE_DIRS VISAGE_LIBRARIES + VISAGE_INCLUDE_DIR VISAGE_XML_INCLUDE_DIR VISAGE_OPENCV_INCLUDE_DIR VISAGE_OPENCV2_INCLUDE_DIR + VISAGE_CORE_LIBRARY VISAGE_VISION_LIBRARY VISAGE_OPENCV_LIBRARY +) From 4a2e086912b15875a23a4e25b4b6685dd1bc8909 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 10:38:31 -0700 Subject: [PATCH 28/57] fix include lookup for visage find module --- cmake/modules/FindVisage.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindVisage.cmake b/cmake/modules/FindVisage.cmake index 46cf9e684a..25a11fc500 100644 --- a/cmake/modules/FindVisage.cmake +++ b/cmake/modules/FindVisage.cmake @@ -20,7 +20,7 @@ set(VISAGE_SEARCH_DIRS "${VISAGE_ROOT_DIR}" "$ENV{VISAGE_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/visage") -find_path(VISAGE_INCLUDE_DIR VisageTracker2.h ${VISAGE_ROOT_DIR}/include) +find_path(VISAGE_INCLUDE_DIR VisageTracker2.h PATH_SUFFIXES include HINTS ${VISAGE_SEARCH_DIRS}) if (APPLE) find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h HINTS ${VISAGE_SEARCH_DIRS} /usr/include/libxml2) From 65a6a17fe37327ee245899adc67ce616b55b79ca Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 10:39:57 -0700 Subject: [PATCH 29/57] fix order of path lookup for visage find module --- cmake/modules/FindVisage.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindVisage.cmake b/cmake/modules/FindVisage.cmake index 25a11fc500..4151d3bec0 100644 --- a/cmake/modules/FindVisage.cmake +++ b/cmake/modules/FindVisage.cmake @@ -23,7 +23,7 @@ set(VISAGE_SEARCH_DIRS "${VISAGE_ROOT_DIR}" "$ENV{VISAGE_ROOT_DIR}" "$ENV{HIFI_L find_path(VISAGE_INCLUDE_DIR VisageTracker2.h PATH_SUFFIXES include HINTS ${VISAGE_SEARCH_DIRS}) if (APPLE) - find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h HINTS ${VISAGE_SEARCH_DIRS} /usr/include/libxml2) + find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h HINTS /usr/include/libxml2 ${VISAGE_SEARCH_DIRS}) find_path(VISAGE_OPENCV_INCLUDE_DIR cv.h PATH_SUFFIXES dependencies/OpenCV_MacOSX/include HINTS ${VISAGE_SEARCH_DIRS}) find_path(VISAGE_OPENCV2_INCLUDE_DIR opencv.hpp PATH_SUFFIXES dependencies/OpenCV_MacOSX/include/opencv2 HINTS ${VISAGE_SEARCH_DIRS}) From 882d0382bc0da9cd3a05efb8632b7c6458761192 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 11:23:50 -0700 Subject: [PATCH 30/57] tweaked FindFaceshift to pass along both debug and release libraries --- cmake/modules/FindFaceshift.cmake | 44 +++++++++++-------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/cmake/modules/FindFaceshift.cmake b/cmake/modules/FindFaceshift.cmake index 39febe3eb3..e11c21cd3f 100644 --- a/cmake/modules/FindFaceshift.cmake +++ b/cmake/modules/FindFaceshift.cmake @@ -18,41 +18,27 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -find_path(FACESHIFT_INCLUDE_DIRS fsbinarystream.h ${FACESHIFT_ROOT_DIR}/include) +set(FACESHIFT_SEARCH_DIRS "${FACESHIFT_ROOT_DIR}" "$ENV{FACESHIFT_ROOT_DIR}" "$ENV{HIFI_LIB_DIR}/faceshift") + +find_path(FACESHIFT_INCLUDE_DIRS fsbinarystream.h PATH_SUFFIXES include HINTS ${FACESHIFT_SEARCH_DIRS}) if (APPLE) - find_library(FACESHIFT_LIBRARIES libfaceshift.a ${FACESHIFT_ROOT_DIR}/lib/MacOS/) + set(ARCH_DIR "MacOS") elseif (UNIX) - find_library(FACESHIFT_LIBRARIES libfaceshift.a ${FACESHIFT_ROOT_DIR}/lib/UNIX/) + set(ARCH_DIR "UNIX") elseif (WIN32) -# For windows, we're going to build the faceshift sources directly into the interface build -# and not link to a prebuilt library. This is because the VS2010 linker doesn't like cross-linking -# between release and debug libraries. If we change that in the future we can make win32 more -# like the other platforms - #find_library(FACESHIFT_LIBRARIES faceshift.lib ${FACESHIFT_ROOT_DIR}/lib/WIN32/) + set(ARCH_DIR "Win32") endif () -if (WIN32) - # Windows only cares about the headers - if (FACESHIFT_INCLUDE_DIRS) - set(FACESHIFT_FOUND TRUE) - endif (FACESHIFT_INCLUDE_DIRS) -else () - # Mac and Unix requires libraries - if (FACESHIFT_INCLUDE_DIRS AND FACESHIFT_LIBRARIES) - set(FACESHIFT_FOUND TRUE) - endif (FACESHIFT_INCLUDE_DIRS AND FACESHIFT_LIBRARIES) -endif () +find_library(FACESHIFT_LIBRARY_RELEASE NAME faceshift PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS}) +find_library(FACESHIFT_LIBRARY_DEBUG NAME faceshiftd PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS}) -if (FACESHIFT_FOUND) - if (NOT FACESHIFT_FIND_QUIETLY) - message(STATUS "Found Faceshift... ${FACESHIFT_LIBRARIES}") - endif (NOT FACESHIFT_FIND_QUIETLY) -else () - if (FACESHIFT_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Faceshift") - endif (FACESHIFT_FIND_REQUIRED) -endif () +include(SelectLibraryConfigurations) +select_library_configurations(FACESHIFT) + +set(FACESHIFT_LIBRARIES ${FACESHIFT_LIBRARY}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FACESHIFT DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES) -# show the FACESHIFT_INCLUDE_DIRS and FACESHIFT_LIBRARIES variables only in the advanced view mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES) \ No newline at end of file From e060693856e80f653770a1cde1788a17d6f9f328 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 13:54:44 -0700 Subject: [PATCH 31/57] remove faceshift from repository, make external dependency --- .gitignore | 4 + interface/CMakeLists.txt | 40 +- interface/external/faceshift/CMakeLists.txt | 11 - .../faceshift/include/fsbinarystream.h | 410 -------------- .../faceshift/lib/MacOS/libfaceshift.a | Bin 370592 -> 0 bytes .../faceshift/lib/UNIX/libfaceshift.a | Bin 523750 -> 0 bytes interface/external/faceshift/readme.txt | 18 + .../external/faceshift/src/fsbinarystream.cpp | 502 ------------------ 8 files changed, 38 insertions(+), 947 deletions(-) delete mode 100644 interface/external/faceshift/CMakeLists.txt delete mode 100644 interface/external/faceshift/include/fsbinarystream.h delete mode 100644 interface/external/faceshift/lib/MacOS/libfaceshift.a delete mode 100644 interface/external/faceshift/lib/UNIX/libfaceshift.a create mode 100644 interface/external/faceshift/readme.txt delete mode 100644 interface/external/faceshift/src/fsbinarystream.cpp diff --git a/.gitignore b/.gitignore index 4176dcc652..dcef7b5b69 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,10 @@ interface/resources/visage/* interface/external/faceplus/* !interface/external/faceplus/readme.txt +# Ignore Faceshift +interface/external/faceshift/* +!interface/external/faceshift/readme.txt + # Ignore PrioVR interface/external/priovr/* !interface/external/priovr/readme.txt diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 97b6413106..5461bc2556 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -58,19 +58,6 @@ foreach(SUBDIR avatar devices renderer ui starfield location scripting voxels pa set(INTERFACE_SRCS ${INTERFACE_SRCS} "${SUBDIR_SRCS}") endforeach(SUBDIR) -# windows also includes the faceshift externals, because using a lib doesn't work due to debug/release mismatch -if (WIN32) - set(EXTERNAL_SOURCE_SUBDIRS "faceshift") -endif () - -foreach(EXTERNAL_SOURCE_SUBDIR ${EXTERNAL_SOURCE_SUBDIRS}) - file(GLOB_RECURSE SUBDIR_SRCS - "external/${EXTERNAL_SOURCE_SUBDIR}/src/*.cpp" - "external/${EXTERNAL_SOURCE_SUBDIR}/src/*.c" - "external/${EXTERNAL_SOURCE_SUBDIR}/src/*.h") - set(INTERFACE_SRCS ${INTERFACE_SRCS} "${SUBDIR_SRCS}") -endforeach(EXTERNAL_SOURCE_SUBDIR) - find_package(Qt5 COMPONENTS Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets Xml UiTools) # grab the ui files in resources/ui @@ -229,16 +216,22 @@ endif (QXMPP_FOUND AND NOT DISABLE_QXMPP) # and with RtMidi for RtMidi control if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI) + add_definitions(-DHAVE_RTMIDI) + include_directories(SYSTEM "${RTMIDI_INCLUDE_DIRS}") + target_link_libraries(${TARGET_NAME} ${RTMIDI_LIBRARIES}) - add_definitions(-DHAVE_RTMIDI) - include_directories(SYSTEM "${RTMIDI_INCLUDE_DIRS}") - target_link_libraries(${TARGET_NAME} ${RTMIDI_LIBRARIES}) - - if (APPLE) - find_library(CoreMIDI CoreMIDI) - add_definitions(-D__MACOSX_CORE__) - target_link_libraries(${TARGET_NAME} ${CoreMIDI}) - endif() + if (APPLE) + find_library(CoreMIDI CoreMIDI) + add_definitions(-D__MACOSX_CORE__) + target_link_libraries(${TARGET_NAME} ${CoreMIDI}) + endif() +endif() + +# and with Faceshift for depth camera face tracking +if (FACESHIFT_FOUND AND NOT DISABLE_FACESHIFT) + add_definitions(-DAHAVE_FACESHIFT) + include_directories(SYSTEM "${FACESHIFT_INCLUDE_DIRS}") + target_link_libraries(${TARGET_NAME} ${FACESHIFT_LIBRARIES}) endif() # include headers for interface and InterfaceConfig. @@ -246,11 +239,10 @@ include_directories("${PROJECT_SOURCE_DIR}/src" "${PROJECT_BINARY_DIR}/includes" # include external library headers # use system flag so warnings are supressed -include_directories(SYSTEM "${FACESHIFT_INCLUDE_DIRS}" "${OPENSSL_INCLUDE_DIR}") +include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") target_link_libraries( ${TARGET_NAME} - "${FACESHIFT_LIBRARIES}" "${ZLIB_LIBRARIES}" ${OPENSSL_LIBRARIES} Qt5::Core Qt5::Gui Qt5::Multimedia Qt5::Network Qt5::OpenGL diff --git a/interface/external/faceshift/CMakeLists.txt b/interface/external/faceshift/CMakeLists.txt deleted file mode 100644 index be1c320c29..0000000000 --- a/interface/external/faceshift/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 2.8) - -set(TARGET_NAME faceshift) -project(${TARGET_NAME}) - -# grab the implemenation and header files -file(GLOB FACESHIFT_SRCS include/*.h src/*.cpp) - -include_directories(include) - -add_library(${TARGET_NAME} "${FACESHIFT_SRCS}") diff --git a/interface/external/faceshift/include/fsbinarystream.h b/interface/external/faceshift/include/fsbinarystream.h deleted file mode 100644 index 8fa585397b..0000000000 --- a/interface/external/faceshift/include/fsbinarystream.h +++ /dev/null @@ -1,410 +0,0 @@ -#pragma once - -#ifndef FSBINARYSTREAM_H -#define FSBINARYSTREAM_H - -// ========================================================================== -// Copyright (C) 2012 faceshift AG, and/or its licensors. All rights reserved. -// -// the software is free to use and provided "as is", without warranty of any kind. -// faceshift AG does not make and hereby disclaims any express or implied -// warranties including, but not limited to, the warranties of -// non-infringement, merchantability or fitness for a particular purpose, -// or arising from a course of dealing, usage, or trade practice. in no -// event will faceshift AG and/or its licensors be liable for any lost -// revenues, data, or profits, or special, direct, indirect, or -// consequential damages, even if faceshift AG and/or its licensors has -// been advised of the possibility or probability of such damages. -// ========================================================================== - - -/** - * Define the HAVE_EIGEN preprocessor define, if you are using the Eigen library, it allows you to easily convert our tracked data from and to eigen - * See fsVector3f and fsQuaternionf for more details - **/ - -#ifdef HAVE_EIGEN -#include -#include -#endif - -#ifdef _MSC_VER -#include -#else -#include -#endif - -#include -#include -#include - -/******************************************************************************************* - * This first part of the file contains a definition of the datastructures holding the - * tracking results - ******************************************************************************************/ - -namespace fs { - -/** - * A floating point three-vector. - * - * To keep these networking classes as simple as possible, we do not implement the - * vector semantics here, use Eigen for that purpose. The class just holds three named floats, - * and you have to interpret them yourself. - **/ -struct fsVector3f { - float x,y,z; - - fsVector3f() {} -#ifdef HAVE_EIGEN - explicit fsVector3f(const Eigen::Matrix &v) : x(v[0]), y(v[1]), z(v[2]) {} - Eigen::Map< Eigen::Matrix > eigen() const { return Eigen::Map >((float*)this); } -#endif -}; - -/** - * An integer three-vector. - **/ -struct fsVector3i { - int32_t x,y,z; - - fsVector3i() {} -#ifdef HAVE_EIGEN - explicit fsVector3i(const Eigen::Matrix &v) : x(v[0]), y(v[1]), z(v[2]) {} - Eigen::Map > eigen() const { return Eigen::Map >((int32_t*)this); } -#endif -}; - -/** - * An integer four-vector. - **/ -struct fsVector4i { - int32_t x,y,z,w; - - fsVector4i() {} -#ifdef HAVE_EIGEN - explicit fsVector4i(const Eigen::Matrix &v) : x(v[0]), y(v[1]), z(v[2]), w(v[3]) {} - Eigen::Map > eigen() const { return Eigen::Map >((int32_t*)this); } -#endif -}; - -/** - * Structure holding the data of a quaternion. - * - *To keep these networking classes as simple as possible, we do not implement the - * quaternion semantics here. The class just holds four named floats, and you have to interpret them yourself. - * - * If you have Eigen you can just cast this class to an Eigen::Quaternionf and use it. - * - * The quaternion is defined as w+xi+yj+zk - **/ -struct fsQuaternionf { - float x,y,z,w; - - fsQuaternionf() {} -#ifdef HAVE_EIGEN - explicit fsQuaternionf(const Eigen::Quaternionf &q) : x(q.x()), y(q.y()), z(q.z()), w(q.w()) {} - Eigen::Quaternionf eigen() const { return Eigen::Quaternionf(w,x,y,z); } -#endif -}; - -/** - * A structure containing the data tracked for a single frame. - **/ -class fsTrackingData { - public: - //! time stamp in ms - double m_timestamp; - - //! flag whether tracking was successful [0,1] - bool m_trackingSuccessful; - - //! head pose - fsQuaternionf m_headRotation; - fsVector3f m_headTranslation; - - //! eye gaze in degrees - float m_eyeGazeLeftPitch; - float m_eyeGazeLeftYaw; - float m_eyeGazeRightPitch; - float m_eyeGazeRightYaw; - - //! blendshape coefficients - std::vector m_coeffs; - - //! marker positions - format specified in faceshift - std::vector< fsVector3f > m_markers; -}; - -/** - * A structure containing vertex information - */ -class fsVertexData { -public: - //! vertex data - std::vector m_vertices; - -#ifdef HAVE_EIGEN - Eigen::Map > eigen() { return Eigen::Map >((float*)m_vertices.data(),3,m_vertices.size()); } -#endif -}; - -/** - * A strucutre containing mesh information - */ -class fsMeshData { -public: - //! topology (quads) - std::vector m_quads; - - //! topology (triangles) - std::vector m_tris; - - //! vertex data - fsVertexData m_vertex_data; - -#ifdef HAVE_EIGEN - Eigen::Map > quads_eigen() { return Eigen::Map >((int32_t*)m_quads.data(),4,m_quads.size()); } - Eigen::Map > tris_eigen() { return Eigen::Map >((int32_t*)m_tris.data(),3,m_tris.size()); } - Eigen::Map > vertices_eigen() { return m_vertex_data.eigen(); } -#endif - -}; - -/******************************************************************************************* - * Now follows a definition of datastructures encapsulating the network messages - ******************************************************************************************/ - -/** Predeclaration of the message types available in faceshift **/ - -// Inbound -class fsMsgStartCapturing; -class fsMsgStopCapturing; -class fsMsgCalibrateNeutral; -class fsMsgSendMarkerNames; -class fsMsgSendBlendshapeNames; -class fsMsgSendRig; - -// Outbound -class fsMsgTrackingState; -class fsMsgMarkerNames; -class fsMsgBlendshapeNames; -class fsMsgRig; - -/** - * Base class of all message that faceshift is sending. - * A class can be queried for its type, using the id() function for use in a switch statement, or by using a dynamic_cast. - **/ -class fsMsg { -public: - virtual ~fsMsg() {} - - enum MessageType { - // Messages to control faceshift via the network - // These are sent from the client to faceshift - MSG_IN_START_TRACKING = 44344, - MSG_IN_STOP_TRACKING = 44444, - MSG_IN_CALIBRATE_NEUTRAL = 44544, - MSG_IN_SEND_MARKER_NAMES = 44644, - MSG_IN_SEND_BLENDSHAPE_NAMES = 44744, - MSG_IN_SEND_RIG = 44844, - MSG_IN_HEADPOSE_RELATIVE = 44944, - MSG_IN_HEADPOSE_ABSOLUTE = 44945, - - // Messages containing tracking information - // These are sent form faceshift to the client application - MSG_OUT_TRACKING_STATE = 33433, - MSG_OUT_MARKER_NAMES = 33533, - MSG_OUT_BLENDSHAPE_NAMES = 33633, - MSG_OUT_RIG = 33733 - }; - - virtual MessageType id() const = 0; -}; -typedef std::tr1::shared_ptr fsMsgPtr; - - -/************* - * Inbound - ***********/ -class fsMsgStartCapturing : public fsMsg { -public: - virtual ~fsMsgStartCapturing() {} - virtual MessageType id() const { return MSG_IN_START_TRACKING; } -}; -class fsMsgStopCapturing : public fsMsg { -public: - virtual ~fsMsgStopCapturing() {} - virtual MessageType id() const { return MSG_IN_STOP_TRACKING; } -}; -class fsMsgCalibrateNeutral : public fsMsg { -public: - virtual ~fsMsgCalibrateNeutral() {} - virtual MessageType id() const { return MSG_IN_CALIBRATE_NEUTRAL; } -}; -class fsMsgSendMarkerNames : public fsMsg { -public: - virtual ~fsMsgSendMarkerNames() {} - virtual MessageType id() const { return MSG_IN_SEND_MARKER_NAMES; } -}; -class fsMsgSendBlendshapeNames : public fsMsg { -public: - virtual ~fsMsgSendBlendshapeNames() {} - virtual MessageType id() const { return MSG_IN_SEND_BLENDSHAPE_NAMES; } -}; -class fsMsgSendRig : public fsMsg { -public: - virtual ~fsMsgSendRig() {} - virtual MessageType id() const { return MSG_IN_SEND_RIG; } -}; -class fsMsgHeadPoseRelative : public fsMsg { -public: - virtual ~fsMsgHeadPoseRelative() {} - virtual MessageType id() const { return MSG_IN_HEADPOSE_RELATIVE; } -}; -class fsMsgHeadPoseAbsolute : public fsMsg { -public: - virtual ~fsMsgHeadPoseAbsolute() {} - virtual MessageType id() const { return MSG_IN_HEADPOSE_ABSOLUTE; } -}; - -/************* - * Outbound - ***********/ -class fsMsgTrackingState : public fsMsg { -public: - virtual ~fsMsgTrackingState() {} - - /* */ fsTrackingData & tracking_data() /* */ { return m_tracking_data; } - const fsTrackingData & tracking_data() const { return m_tracking_data; } - - virtual MessageType id() const { return MSG_OUT_TRACKING_STATE; } - -private: - fsTrackingData m_tracking_data; -}; -class fsMsgMarkerNames : public fsMsg { -public: - virtual ~fsMsgMarkerNames() {} - - /* */ std::vector & marker_names() /* */ { return m_marker_names; } - const std::vector & marker_names() const { return m_marker_names; } - - virtual MessageType id() const { return MSG_OUT_MARKER_NAMES; } -private: - std::vector m_marker_names; -}; -class fsMsgBlendshapeNames : public fsMsg { -public: - virtual ~fsMsgBlendshapeNames() {} - - /* */ std::vector & blendshape_names() /* */ { return m_blendshape_names; } - const std::vector & blendshape_names() const { return m_blendshape_names; } - - virtual MessageType id() const { return MSG_OUT_BLENDSHAPE_NAMES; } -private: - std::vector m_blendshape_names; -}; -class fsMsgRig : public fsMsg { -public: - virtual ~fsMsgRig() {} - - virtual MessageType id() const { return MSG_OUT_RIG; } - - /* */ fsMeshData & mesh() /* */ { return m_mesh; } - const fsMeshData & mesh() const { return m_mesh; } - - /* */ std::vector & blendshape_names() /* */ { return m_blendshape_names; } - const std::vector & blendshape_names() const { return m_blendshape_names; } - - /* */ std::vector & blendshapes() /* */ { return m_blendshapes; } - const std::vector & blendshapes() const { return m_blendshapes; } - -private: - //! neutral mesh - fsMeshData m_mesh; - //! blendshape names - std::vector m_blendshape_names; - //! blendshapes - std::vector m_blendshapes; -}; -class fsMsgSignal : public fsMsg { - MessageType m_id; -public: - explicit fsMsgSignal(MessageType id) : m_id(id) {} - virtual ~fsMsgSignal() {} - virtual MessageType id() const { return m_id; } -}; - -/** - * Class to parse a faceshift data stream, and to create message to write into such a stream - * - * This needs to be connected with your networking methods by calling - * - * void received(int, const char *); - * - * whenever new data is available. After adding received data to the parser you can parse faceshift messages using the - * - * std::tr1::shared_ptr get_message(); - * - * to get the next message, if a full block of data has been received. This should be iterated until no more messages are in the buffer. - * - * You can also use this to encode messages to send back to faceshift. This works by calling the - * - * void encode_message(std::string &msg_out, const fsMsg &msg); - * - * methods (actually the specializations existing for each of our message types). This will encode the message into a - * binary string in msg_out. You then only need to push the resulting string over the network to faceshift. - * - * This class does not handle differences in endianness or other strange things that can happen when pushing data over the network. - * Should you have to adapt this to such a system, then it should be possible to do this by changing only the write_... and read_... - * functions in the accompanying cpp file, but so far there was no need for it. - **/ -class fsBinaryStream { -public: - fsBinaryStream(); - - /** - * Use to push data into the parser. Typically called inside of your network receiver routine - **/ - void received(long int, const char *); - /** - * After pushing data, you can try to extract messages from the stream. Process messages until a null pointer is returned. - **/ - fsMsgPtr get_message(); - /** - * When an invalid message is received, the valid field is set to false. No attempt is made to recover from the problem, you will have to disconnect. - **/ - bool valid() const { return m_valid; } - void clear() { m_start = 0; m_end = 0; m_valid=true; } - - // Inbound - static void encode_message(std::string &msg_out, const fsMsgTrackingState &msg); - static void encode_message(std::string &msg_out, const fsMsgStartCapturing &msg); - static void encode_message(std::string &msg_out, const fsMsgStopCapturing &msg); - static void encode_message(std::string &msg_out, const fsMsgCalibrateNeutral &msg); - static void encode_message(std::string &msg_out, const fsMsgSendMarkerNames &msg); - static void encode_message(std::string &msg_out, const fsMsgSendBlendshapeNames &msg); - static void encode_message(std::string &msg_out, const fsMsgSendRig &msg); - static void encode_message(std::string &msg_out, const fsMsgHeadPoseRelative &msg); - static void encode_message(std::string &msg_out, const fsMsgHeadPoseAbsolute &msg); - - // Outbound - static void encode_message(std::string &msg_out, const fsTrackingData &msg); - static void encode_message(std::string &msg_out, const fsMsgMarkerNames &msg); - static void encode_message(std::string &msg_out, const fsMsgBlendshapeNames &msg); - static void encode_message(std::string &msg_out, const fsMsgRig &msg); - static void encode_message(std::string &msg_out, const fsMsgSignal &msg); // Generic Signal - -private: - std::string m_buffer; - long int m_start; - long int m_end; - bool m_valid; - -}; - -} - - -#endif // FSBINARYSTREAM_H diff --git a/interface/external/faceshift/lib/MacOS/libfaceshift.a b/interface/external/faceshift/lib/MacOS/libfaceshift.a deleted file mode 100644 index bd9dffa6fb941b71b312a7436c36e536d6078c22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 370592 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%0D)oMN3>9fDVZUHoPb+;oJf;Tv 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 diff --git a/interface/external/faceshift/lib/UNIX/libfaceshift.a b/interface/external/faceshift/lib/UNIX/libfaceshift.a deleted file mode 100644 index 4d53de7ab5602a22650db8fb82253c60028eb323..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 523750 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 NKk -#include -#include - -#define FSNETWORKVERSION 1 - -#ifdef FS_INTERNAL -#include -#else -#define LOG_RELEASE_ERROR(...) { printf("ERROR: %20s:%6d", __FILE__, __LINE__); printf(__VA_ARGS__); } -#define LOG_RELEASE_WARNING(...) { printf("WARNING: %20s:%6d", __FILE__, __LINE__); printf(__VA_ARGS__); } -#define LOG_RELEASE_INFO(...) { printf("INFO: %20s:%6d", __FILE__, __LINE__); printf(__VA_ARGS__); } -#endif - - -namespace fs { - -// Ids of the submessages for the tracking state -enum BlockId { - BLOCKID_INFO = 101, - BLOCKID_POSE = 102, - BLOCKID_BLENDSHAPES = 103, - BLOCKID_EYES = 104, - BLOCKID_MARKERS = 105 -}; - - -typedef long int Size; - -struct BlockHeader { - uint16_t id; - uint16_t version; - uint32_t size; - BlockHeader(uint16_t _id=0, - uint32_t _size=0, - uint16_t _version=FSNETWORKVERSION - ) : id(_id), version(_version), size(_size) {} -}; - -// Interprets the data at the position start in buffer as a T and increments start by sizeof(T) -// It should be sufficient to change/overload this function when you are on a wierd endian system -template bool read_pod(T &value, const std::string &buffer, Size &start) { - if(start+sizeof(T) > buffer.size()) return false; - value = *(const T*)(&buffer[start]); - start += sizeof(T); - return true; -} -bool read_pod(std::string &value, const std::string &buffer, Size &start) { - uint16_t len = 0; - if(!read_pod(len, buffer, start)) return false; - if(start+len>Size(buffer.size())) return false; // check whether we have enough data available - value.resize(len); - memcpy(&(value[0]), &buffer[start], len); - start+=len; - return true; -} -template bool read_vector(std::vector & values, const std::string & buffer, Size & start) { - uint32_t len = 0; - if( !read_pod(len, buffer, start)) return false; - if( start+len*sizeof(T) > buffer.size() ) return false; - values.resize(len); - for(uint32_t i = 0; i < len; ++i) { - read_pod(values[i],buffer,start); - } - return true; -} -template bool read_small_vector(std::vector & values, const std::string & buffer, Size & start) { - uint16_t len = 0; - if( !read_pod(len, buffer, start)) return false; - if( start+len*sizeof(T) > buffer.size() ) return false; - values.resize(len); - bool success = true; - for(uint16_t i = 0; i < len; ++i) { - success &= read_pod(values[i],buffer,start); - } - return success; -} - -// Adds the bitpattern of the data to the end of the buffer. -// It should be sufficient to change/overload this function when you are on a wierd endian system -template -void write_pod(std::string &buffer, const T &value) { - Size start = buffer.size(); - buffer.resize(start + sizeof(T)); - *(T*)(&buffer[start]) = value; - start += sizeof(T); -} -// special write function for strings -void write_pod(std::string &buffer, const std::string &value) { - uint16_t len = uint16_t(value.size()); write_pod(buffer, len); - buffer.append(value); -} -template void write_vector(std::string & buffer, const std::vector & values) { - uint32_t len = values.size(); - write_pod(buffer,len); - for(uint32_t i = 0; i < len; ++i) - write_pod(buffer,values[i]); -} -template void write_small_vector(std::string & buffer, const std::vector & values) { - uint16_t len = values.size(); - write_pod(buffer,len); - for(uint16_t i = 0; i < len; ++i) - write_pod(buffer,values[i]); -} -void update_msg_size(std::string &buffer, Size start) { - *(uint32_t*)(&buffer[start+4]) = buffer.size() - sizeof(BlockHeader) - start; -} -void update_msg_size(std::string &buffer) { - *(uint32_t*)(&buffer[4]) = buffer.size() - sizeof(BlockHeader); -} - -static void skipHeader( Size &start) { - start += sizeof(BlockHeader); -} - -//! returns whether @param data contains enough data to read the block header -static bool headerAvailable(BlockHeader &header, const std::string &buffer, Size &start, const Size &end) { - if (end-start >= Size(sizeof(BlockHeader))) { - header = *(BlockHeader*)(&buffer[start]); - return true; - } else { - return false; - } -} - -//! returns whether @param data contains data for a full block -static bool blockAvailable(const std::string &buffer, Size &start, const Size &end) { - BlockHeader header; - if (!headerAvailable(header, buffer, start, end)) return false; - return end-start >= Size(sizeof(header)+header.size); -} - -fsBinaryStream::fsBinaryStream() : m_buffer(), m_start(0), m_end(0), m_valid(true) { m_buffer.resize(64*1024); } // Use a 64kb buffer by default - -void fsBinaryStream::received(long int sz, const char *data) { - - long int new_end = m_end + sz; - if (new_end > Size(m_buffer.size()) && m_start>0) { - // If newly received block is too large to fit into the buffer, but we already have processed data from the start of the buffer, then - // move memory to the front of the buffer - // The buffer only grows, such that it is always large enough to contain the largest message seen so far. - if (m_end>m_start) memmove(&m_buffer[0], &m_buffer[0] + m_start, m_end - m_start); - m_end = m_end - m_start; - m_start = 0; - new_end = m_end + sz; - } - - if (new_end > Size(m_buffer.size())) m_buffer.resize((int)(1.5f * (float)new_end)); // HIFI: to get 1.5 without warnings - - memcpy(&m_buffer[0] + m_end, data, sz); - m_end += sz; - -} - -static bool decodeInfo(fsTrackingData & _trackingData, const std::string &buffer, Size &start) { - bool success = true; - success &= read_pod(_trackingData.m_timestamp, buffer, start); - unsigned char tracking_successfull = 0; - success &= read_pod( tracking_successfull, buffer, start ); - _trackingData.m_trackingSuccessful = bool(tracking_successfull != 0); // HIFI: get rid of windows warning - return success; -} - -static bool decodePose(fsTrackingData & _trackingData, const std::string &buffer, Size &start) { - bool success = true; - success &= read_pod(_trackingData.m_headRotation.x, buffer, start); - success &= read_pod(_trackingData.m_headRotation.y, buffer, start); - success &= read_pod(_trackingData.m_headRotation.z, buffer, start); - success &= read_pod(_trackingData.m_headRotation.w, buffer, start); - success &= read_pod(_trackingData.m_headTranslation.x, buffer, start); - success &= read_pod(_trackingData.m_headTranslation.y, buffer, start); - success &= read_pod(_trackingData.m_headTranslation.z, buffer, start); - return success; -} - -static bool decodeBlendshapes(fsTrackingData & _trackingData, const std::string &buffer, Size &start) { - return read_vector(_trackingData.m_coeffs, buffer, start); -} - -static bool decodeEyeGaze(fsTrackingData & _trackingData, const std::string &buffer, Size &start) { - bool success = true; - success &= read_pod(_trackingData.m_eyeGazeLeftPitch , buffer, start); - success &= read_pod(_trackingData.m_eyeGazeLeftYaw , buffer, start); - success &= read_pod(_trackingData.m_eyeGazeRightPitch, buffer, start); - success &= read_pod(_trackingData.m_eyeGazeRightYaw , buffer, start); - return success; -} - -static bool decodeMarkers(fsTrackingData & _trackingData, const std::string &buffer, Size &start) { - return read_small_vector( _trackingData.m_markers, buffer, start ); -} - -static bool decodeMarkerNames(fsMsgMarkerNames &_msg, const std::string &buffer, Size &start) { - return read_small_vector(_msg.marker_names(), buffer, start); -} -static bool decodeBlendshapeNames(fsMsgBlendshapeNames &_msg, const std::string &buffer, Size &start) { - return read_small_vector(_msg.blendshape_names(), buffer, start); -} -static bool decodeRig(fsMsgRig &_msg, const std::string &buffer, Size &start) { - bool success = true; - success &= read_vector(_msg.mesh().m_quads,buffer,start); // read quads - success &= read_vector(_msg.mesh().m_tris,buffer,start); // read triangles - success &= read_vector(_msg.mesh().m_vertex_data.m_vertices,buffer,start);// read neutral vertices - success &= read_small_vector(_msg.blendshape_names(),buffer,start); // read names - uint16_t bsize = 0; - success &= read_pod(bsize,buffer,start); - _msg.blendshapes().resize(bsize); - for(uint16_t i = 0;i < bsize; i++) - success &= read_vector(_msg.blendshapes()[i].m_vertices,buffer,start); // read blendshapes - return success; -} - -bool is_valid_msg(int id) { - switch(id) { - case fsMsg::MSG_IN_START_TRACKING : - case fsMsg::MSG_IN_STOP_TRACKING : - case fsMsg::MSG_IN_CALIBRATE_NEUTRAL : - case fsMsg::MSG_IN_SEND_MARKER_NAMES : - case fsMsg::MSG_IN_SEND_BLENDSHAPE_NAMES: - case fsMsg::MSG_IN_SEND_RIG : - case fsMsg::MSG_IN_HEADPOSE_RELATIVE : - case fsMsg::MSG_IN_HEADPOSE_ABSOLUTE : - case fsMsg::MSG_OUT_TRACKING_STATE : - case fsMsg::MSG_OUT_MARKER_NAMES : - case fsMsg::MSG_OUT_BLENDSHAPE_NAMES : - case fsMsg::MSG_OUT_RIG : return true; - default: - LOG_RELEASE_ERROR("Invalid Message ID %d", id); - return false; - } -} - -fsMsgPtr fsBinaryStream::get_message() { - BlockHeader super_block; - if( !headerAvailable(super_block, m_buffer, m_start, m_end) ) return fsMsgPtr(); - if (!is_valid_msg(super_block.id)) { LOG_RELEASE_ERROR("Invalid superblock id"); m_valid = false; return fsMsgPtr(); } - if( !blockAvailable( m_buffer, m_start, m_end) ) return fsMsgPtr(); - skipHeader(m_start); - long super_block_data_start = m_start; - switch (super_block.id) { - case fsMsg::MSG_IN_START_TRACKING: { - if (super_block.size > 0) { LOG_RELEASE_ERROR("Expected Size to be 0, not %d", super_block.size); m_valid = false; return fsMsgPtr(); } - return fsMsgPtr(new fsMsgStartCapturing() ); - }; break; - case fsMsg::MSG_IN_STOP_TRACKING: { - if (super_block.size > 0) { LOG_RELEASE_ERROR("Expected Size to be 0, not %d", super_block.size); m_valid = false; return fsMsgPtr(); } - return fsMsgPtr(new fsMsgStopCapturing() ); - }; break; - case fsMsg::MSG_IN_CALIBRATE_NEUTRAL: { - if (super_block.size > 0) { LOG_RELEASE_ERROR("Expected Size to be 0, not %d", super_block.size); m_valid = false; return fsMsgPtr(); } - return fsMsgPtr(new fsMsgCalibrateNeutral() ); - }; break; - case fsMsg::MSG_IN_SEND_MARKER_NAMES: { - if (super_block.size > 0) { LOG_RELEASE_ERROR("Expected Size to be 0, not %d", super_block.size); m_valid = false; return fsMsgPtr(); } - return fsMsgPtr(new fsMsgSendMarkerNames() ); - }; break; - case fsMsg::MSG_IN_SEND_BLENDSHAPE_NAMES: { - if (super_block.size > 0) { LOG_RELEASE_ERROR("Expected Size to be 0, not %d", super_block.size); m_valid = false; return fsMsgPtr(); } - return fsMsgPtr(new fsMsgSendBlendshapeNames() ); - }; break; - case fsMsg::MSG_IN_SEND_RIG: { - if (super_block.size > 0) { LOG_RELEASE_ERROR("Expected Size to be 0, not %d", super_block.size); m_valid = false; return fsMsgPtr(); } - return fsMsgPtr(new fsMsgSendRig() ); - }; break; - case fsMsg::MSG_IN_HEADPOSE_RELATIVE: { - if (super_block.size > 0) { LOG_RELEASE_ERROR("Expected Size to be 0, not %d", super_block.size); m_valid = false; return fsMsgPtr(); } - return fsMsgPtr(new fsMsgHeadPoseRelative() ); - }; break; - case fsMsg::MSG_IN_HEADPOSE_ABSOLUTE: { - if (super_block.size > 0) { LOG_RELEASE_ERROR("Expected Size to be 0, not %d", super_block.size); m_valid = false; return fsMsgPtr(); } - return fsMsgPtr(new fsMsgHeadPoseAbsolute() ); - }; break; - case fsMsg::MSG_OUT_MARKER_NAMES: { - std::tr1::shared_ptr< fsMsgMarkerNames > msg(new fsMsgMarkerNames()); - if( !decodeMarkerNames(*msg, m_buffer, m_start )) { LOG_RELEASE_ERROR("Could not decode marker names"); m_valid = false; return fsMsgPtr(); } - uint64_t actual_size = m_start-super_block_data_start; - if( actual_size != super_block.size ) { LOG_RELEASE_ERROR("Block was promised to be of size %d, not %d", super_block.size, actual_size); m_valid = false; return fsMsgPtr(); } - return msg; - }; break; - case fsMsg::MSG_OUT_BLENDSHAPE_NAMES: { - std::tr1::shared_ptr< fsMsgBlendshapeNames > msg(new fsMsgBlendshapeNames() ); - if( !decodeBlendshapeNames(*msg, m_buffer, m_start) ) { LOG_RELEASE_ERROR("Could not decode blendshape names"); m_valid = false; return fsMsgPtr(); } - uint64_t actual_size = m_start-super_block_data_start; - if( actual_size != super_block.size ) { LOG_RELEASE_ERROR("Block was promised to be of size %d, not %d", super_block.size, actual_size); m_valid = false; return fsMsgPtr(); } - return msg; - }; break; - case fsMsg::MSG_OUT_TRACKING_STATE: { - BlockHeader sub_block; - uint16_t num_blocks = 0; - if( !read_pod(num_blocks, m_buffer, m_start) ) { LOG_RELEASE_ERROR("Could not read num_blocks"); m_valid = false; return fsMsgPtr(); } - std::tr1::shared_ptr msg = std::tr1::shared_ptr(new fsMsgTrackingState()); - for(int i = 0; i < num_blocks; i++) { - if( !headerAvailable(sub_block, m_buffer, m_start, m_end) ) { LOG_RELEASE_ERROR("could not read sub-header %d", i); m_valid = false; return fsMsgPtr(); } - if( !blockAvailable( m_buffer, m_start, m_end) ) { LOG_RELEASE_ERROR("could not read sub-block %d", i); m_valid = false; return fsMsgPtr(); } - skipHeader(m_start); - long sub_block_data_start = m_start; - bool success = true; - switch(sub_block.id) { - case BLOCKID_INFO: success &= decodeInfo( msg->tracking_data(), m_buffer, m_start); break; - case BLOCKID_POSE: success &= decodePose( msg->tracking_data(), m_buffer, m_start); break; - case BLOCKID_BLENDSHAPES: success &= decodeBlendshapes(msg->tracking_data(), m_buffer, m_start); break; - case BLOCKID_EYES: success &= decodeEyeGaze( msg->tracking_data(), m_buffer, m_start); break; - case BLOCKID_MARKERS: success &= decodeMarkers( msg->tracking_data(), m_buffer, m_start); break; - default: - LOG_RELEASE_ERROR("Unexpected subblock id %d", sub_block.id); - m_valid = false; return msg; - break; - } - if(!success) { - LOG_RELEASE_ERROR("Could not decode subblock with id %d", sub_block.id); - m_valid = false; return fsMsgPtr(); - } - uint64_t actual_size = m_start-sub_block_data_start; - if( actual_size != sub_block.size ) { - LOG_RELEASE_ERROR("Unexpected number of bytes consumed %d instead of %d for subblock %d id:%d", actual_size, sub_block.size, i, sub_block.id); - m_valid = false; return fsMsgPtr(); - } - } - uint64_t actual_size = m_start-super_block_data_start; - if( actual_size != super_block.size ) { - LOG_RELEASE_ERROR("Unexpected number of bytes consumed %d instead of %d", actual_size, super_block.size); - m_valid = false; return fsMsgPtr(); - } - return msg; - }; break; - case fsMsg::MSG_OUT_RIG: { - std::tr1::shared_ptr< fsMsgRig > msg(new fsMsgRig() ); - if( !decodeRig(*msg, m_buffer, m_start) ) { LOG_RELEASE_ERROR("Could not decode rig"); m_valid = false; return fsMsgPtr(); } - if( m_start-super_block_data_start != super_block.size ) { LOG_RELEASE_ERROR("Could not decode rig unexpected size"); m_valid = false; return fsMsgPtr(); } - return msg; - }; break; - default: { - LOG_RELEASE_ERROR("Unexpected superblock id %d", super_block.id); - m_valid = false; return fsMsgPtr(); - }; break; - } - return fsMsgPtr(); -} - -static void encodeInfo(std::string &buffer, const fsTrackingData & _trackingData) { - BlockHeader header(BLOCKID_INFO, sizeof(double) + 1); - write_pod(buffer, header); - - write_pod(buffer, _trackingData.m_timestamp); - unsigned char tracking_successfull = _trackingData.m_trackingSuccessful; - write_pod( buffer, tracking_successfull ); -} - -static void encodePose(std::string &buffer, const fsTrackingData & _trackingData) { - BlockHeader header(BLOCKID_POSE, sizeof(float)*7); - write_pod(buffer, header); - - write_pod(buffer, _trackingData.m_headRotation.x); - write_pod(buffer, _trackingData.m_headRotation.y); - write_pod(buffer, _trackingData.m_headRotation.z); - write_pod(buffer, _trackingData.m_headRotation.w); - write_pod(buffer, _trackingData.m_headTranslation.x); - write_pod(buffer, _trackingData.m_headTranslation.y); - write_pod(buffer, _trackingData.m_headTranslation.z); -} - -static void encodeBlendshapes(std::string &buffer, const fsTrackingData & _trackingData) { - uint32_t num_parameters = _trackingData.m_coeffs.size(); - BlockHeader header(BLOCKID_BLENDSHAPES, sizeof(uint32_t) + sizeof(float)*num_parameters); - write_pod(buffer, header); - write_pod(buffer, num_parameters); - for(uint32_t i = 0; i < num_parameters; i++) - write_pod(buffer, _trackingData.m_coeffs[i]); -} - -static void encodeEyeGaze(std::string &buffer, const fsTrackingData & _trackingData) { - BlockHeader header(BLOCKID_EYES, sizeof(float)*4); - write_pod(buffer, header); - write_pod(buffer, _trackingData.m_eyeGazeLeftPitch ); - write_pod(buffer, _trackingData.m_eyeGazeLeftYaw ); - write_pod(buffer, _trackingData.m_eyeGazeRightPitch); - write_pod(buffer, _trackingData.m_eyeGazeRightYaw ); -} - -static void encodeMarkers(std::string &buffer, const fsTrackingData & _trackingData) { - uint16_t numMarkers = _trackingData.m_markers.size(); - BlockHeader header(BLOCKID_MARKERS, sizeof(uint16_t) + sizeof(float)*3*numMarkers); - write_pod(buffer, header); - write_pod(buffer, numMarkers); - for(int i = 0; i < numMarkers; i++) { - write_pod(buffer, _trackingData.m_markers[i].x); - write_pod(buffer, _trackingData.m_markers[i].y); - write_pod(buffer, _trackingData.m_markers[i].z); - } -} - -// Inbound -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgTrackingState &msg) { - encode_message(msg_out, msg.tracking_data()); -} - -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgStartCapturing &msg) { - BlockHeader header(msg.id()); - write_pod(msg_out, header); -} -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgStopCapturing &msg) { - BlockHeader header(msg.id()); - write_pod(msg_out, header); -} -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgCalibrateNeutral &msg) { - BlockHeader header(msg.id()); - write_pod(msg_out, header); -} -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgSendMarkerNames &msg) { - BlockHeader header(msg.id()); - write_pod(msg_out, header); -} -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgSendBlendshapeNames &msg) { - BlockHeader header(msg.id()); - write_pod(msg_out, header); -} -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgSendRig &msg) { - BlockHeader header(msg.id()); - write_pod(msg_out, header); -} -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgHeadPoseRelative &msg) { - BlockHeader header(msg.id()); - write_pod(msg_out, header); -} -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgHeadPoseAbsolute &msg) { - BlockHeader header(msg.id()); - write_pod(msg_out, header); -} - -// Outbound -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgSignal &msg) { - BlockHeader header(msg.id()); - write_pod(msg_out, header); -} -void fsBinaryStream::encode_message(std::string &msg_out, const fsTrackingData &tracking_data) { - Size start = msg_out.size(); - - BlockHeader header(fsMsg::MSG_OUT_TRACKING_STATE); - write_pod(msg_out, header); - - uint16_t N_blocks = 5; - write_pod(msg_out, N_blocks); - encodeInfo( msg_out, tracking_data); - encodePose( msg_out, tracking_data); - encodeBlendshapes(msg_out, tracking_data); - encodeEyeGaze( msg_out, tracking_data); - encodeMarkers( msg_out, tracking_data); - - update_msg_size(msg_out, start); -} -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgMarkerNames &msg) { - Size start = msg_out.size(); - - BlockHeader header(msg.id()); - write_pod(msg_out, header); - - write_small_vector(msg_out,msg.marker_names()); - - update_msg_size(msg_out, start); -} -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgBlendshapeNames &msg) { - Size start = msg_out.size(); - - BlockHeader header(msg.id()); - write_pod(msg_out, header); - - write_small_vector(msg_out,msg.blendshape_names()); - - update_msg_size(msg_out, start); -} -void fsBinaryStream::encode_message(std::string &msg_out, const fsMsgRig &msg) { - Size start = msg_out.size(); - - BlockHeader header(msg.id()); - write_pod(msg_out, header); - - write_vector(msg_out, msg.mesh().m_quads); // write quads - write_vector(msg_out, msg.mesh().m_tris);// write triangles - write_vector(msg_out, msg.mesh().m_vertex_data.m_vertices);// write neutral vertices - write_small_vector(msg_out, msg.blendshape_names());// write names - write_pod(msg_out,uint16_t(msg.blendshapes().size())); - for(uint16_t i = 0;i < uint16_t(msg.blendshapes().size()); i++) - write_vector(msg_out, msg.blendshapes()[i].m_vertices); // write blendshapes - - update_msg_size(msg_out, start); -} -} From b2a99edb2e76b68eb2c55c40dc3e391cf99e3d7c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 14:27:20 -0700 Subject: [PATCH 32/57] fix conditional builds without Faceshift --- interface/src/devices/Faceshift.cpp | 25 +++++++++++++++++++++++++ interface/src/devices/Faceshift.h | 9 ++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp index a7d50814e2..07e394f63f 100644 --- a/interface/src/devices/Faceshift.cpp +++ b/interface/src/devices/Faceshift.cpp @@ -19,11 +19,16 @@ #include "Menu.h" #include "Util.h" +#ifdef HAVE_FACESHIFT using namespace fs; +#endif + using namespace std; const quint16 FACESHIFT_PORT = 33433; +#ifdef HAVE_FACESHIFT + Faceshift::Faceshift() : _tcpEnabled(true), _tcpRetryCount(0), @@ -72,6 +77,24 @@ bool Faceshift::isActive() const { return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS; } +#else + +Faceshift::Faceshift() { + +} + +bool Faceshift::isConnectedOrConnecting() const { + return false; +} + +bool Faceshift::isActive() const { + return false; +} + +#endif + +#ifdef HAVE_FACESHIFT + void Faceshift::update() { if (!isActive()) { return; @@ -260,3 +283,5 @@ void Faceshift::receive(const QByteArray& buffer) { } } } + +#endif diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h index 2fc1aaddb1..fd46be7567 100644 --- a/interface/src/devices/Faceshift.h +++ b/interface/src/devices/Faceshift.h @@ -15,8 +15,12 @@ #include #include +#ifdef HAVE_FACESHIFT + #include +#endif + #include "FaceTracker.h" /// Handles interaction with the Faceshift software, which provides head position/orientation and facial features. @@ -24,7 +28,6 @@ class Faceshift : public FaceTracker { Q_OBJECT public: - Faceshift(); void init(); @@ -32,6 +35,8 @@ public: bool isConnectedOrConnecting() const; bool isActive() const; + +#ifdef HAVE_FACESHIFT const glm::vec3& getHeadAngularVelocity() const { return _headAngularVelocity; } @@ -123,6 +128,8 @@ private: float _longTermAverageEyePitch; float _longTermAverageEyeYaw; bool _longTermAverageInitialized; + +#endif // HAVE_FACESHIFT }; #endif // hifi_Faceshift_h From 10ed33cb3811d7204df3a0b8d2072d51c04d6cc3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 14:34:15 -0700 Subject: [PATCH 33/57] further patching to conditionally build without Faceshift --- interface/src/devices/Faceshift.cpp | 42 ++++++++++++++--------------- interface/src/devices/Faceshift.h | 7 ++--- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp index 07e394f63f..9b2fc7e821 100644 --- a/interface/src/devices/Faceshift.cpp +++ b/interface/src/devices/Faceshift.cpp @@ -63,37 +63,31 @@ Faceshift::Faceshift() : _udpSocket.bind(FACESHIFT_PORT); } -void Faceshift::init() { - setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift)); -} - -bool Faceshift::isConnectedOrConnecting() const { - return _tcpSocket.state() == QAbstractSocket::ConnectedState || - (_tcpRetryCount == 0 && _tcpSocket.state() != QAbstractSocket::UnconnectedState); -} - -bool Faceshift::isActive() const { - const quint64 ACTIVE_TIMEOUT_USECS = 1000000; - return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS; -} - #else Faceshift::Faceshift() { } +#endif + +void Faceshift::init() { + setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift)); +} + bool Faceshift::isConnectedOrConnecting() const { - return false; + return _tcpSocket.state() == QAbstractSocket::ConnectedState || + (_tcpRetryCount == 0 && _tcpSocket.state() != QAbstractSocket::UnconnectedState); } bool Faceshift::isActive() const { - return false; -} - -#endif - #ifdef HAVE_FACESHIFT + const quint64 ACTIVE_TIMEOUT_USECS = 1000000; + return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS; +#else + return false; +#endif +} void Faceshift::update() { if (!isActive()) { @@ -120,12 +114,14 @@ void Faceshift::update() { } void Faceshift::reset() { +#ifdef HAVE_FACESHIFT if (_tcpSocket.state() == QAbstractSocket::ConnectedState) { string message; fsBinaryStream::encode_message(message, fsMsgCalibrateNeutral()); send(message); } _longTermAverageInitialized = false; +#endif } void Faceshift::updateFakeCoefficients(float leftBlink, float rightBlink, float browUp, @@ -161,11 +157,13 @@ void Faceshift::connectSocket() { } void Faceshift::noteConnected() { +#ifdef HAVE_FACESHIFT qDebug("Faceshift: Connected."); // request the list of blendshape names string message; fsBinaryStream::encode_message(message, fsMsgSendBlendshapeNames()); send(message); +#endif } void Faceshift::noteError(QAbstractSocket::SocketError error) { @@ -202,6 +200,7 @@ void Faceshift::send(const std::string& message) { } void Faceshift::receive(const QByteArray& buffer) { +#ifdef HAVE_FACESHIFT _stream.received(buffer.size(), buffer.constData()); fsMsgPtr msg; for (fsMsgPtr msg; (msg = _stream.get_message()); ) { @@ -282,6 +281,5 @@ void Faceshift::receive(const QByteArray& buffer) { break; } } -} - #endif +} diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h index fd46be7567..c14bbed600 100644 --- a/interface/src/devices/Faceshift.h +++ b/interface/src/devices/Faceshift.h @@ -35,8 +35,6 @@ public: bool isConnectedOrConnecting() const; bool isActive() const; - -#ifdef HAVE_FACESHIFT const glm::vec3& getHeadAngularVelocity() const { return _headAngularVelocity; } @@ -93,7 +91,11 @@ private: QTcpSocket _tcpSocket; QUdpSocket _udpSocket; + +#ifdef HAVE_FACESHIFT fs::fsBinaryStream _stream; +#endif + bool _tcpEnabled; int _tcpRetryCount; bool _tracking; @@ -129,7 +131,6 @@ private: float _longTermAverageEyeYaw; bool _longTermAverageInitialized; -#endif // HAVE_FACESHIFT }; #endif // hifi_Faceshift_h From c6bda4bf16972d803e55ced7039d28c91bdab098 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 18 Jul 2014 15:26:18 -0700 Subject: [PATCH 34/57] Moved sixense laser stuff to MyAvatar --- interface/src/Application.cpp | 5 ++- interface/src/avatar/MyAvatar.cpp | 40 ++++++++++++++++++++ interface/src/avatar/MyAvatar.h | 3 ++ interface/src/devices/OculusManager.cpp | 50 +------------------------ interface/src/devices/OculusManager.h | 4 -- interface/src/ui/ApplicationOverlay.cpp | 7 ++-- 6 files changed, 52 insertions(+), 57 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1d5cbabd32..da86e7b54f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2805,8 +2805,11 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { bool mirrorMode = (whichCamera.getInterpolatedMode() == CAMERA_MODE_MIRROR); { PerformanceTimer perfTimer("avatars"); - + _avatarManager.renderAvatars(mirrorMode ? Avatar::MIRROR_RENDER_MODE : Avatar::NORMAL_RENDER_MODE, selfAvatarOnly); + + //Render the sixense lasers + _myAvatar->renderLaserPointers(); } if (!selfAvatarOnly) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a58266fa71..45d6635226 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1849,3 +1849,43 @@ void MyAvatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& pe } } +//Renders sixense laser pointers for UI selection with controllers +void MyAvatar::renderLaserPointers() { + const float PALM_TIP_ROD_RADIUS = 0.002f; + + //If the Oculus is enabled, we will draw a blue cursor ray + + for (size_t i = 0; i < getHand()->getNumPalms(); ++i) { + PalmData& palm = getHand()->getPalms()[i]; + if (palm.isActive()) { + glColor4f(0, 1, 1, 1); + glm::vec3 tip = getLaserPointerTipPosition(&palm); + glm::vec3 root = palm.getPosition(); + + //Scale the root vector with the avatar scale + scaleVectorRelativeToPosition(root); + + Avatar::renderJointConnectingCone(root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS); + } + } +} + +//Gets the tip position for the laser pointer +glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { + const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); + const float PALM_TIP_ROD_LENGTH_MULT = 40.0f; + + glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); + + glm::vec3 position = palm->getPosition(); + //scale the position with the avatar + scaleVectorRelativeToPosition(position); + + + glm::vec3 result; + if (applicationOverlay.calculateRayUICollisionPoint(position, direction, result)) { + return result; + } + + return palm->getPosition(); +} diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0b2254bafa..8f99308530 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -127,6 +127,9 @@ public: void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration); + /// Renders a laser pointer for UI picking + void renderLaserPointers(); + glm::vec3 getLaserPointerTipPosition(const PalmData* palm); public slots: void goHome(); void increaseSize(); diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index beb18bfc72..e6e958d89a 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -462,52 +462,4 @@ QSize OculusManager::getRenderTargetSize() { #else return QSize(100, 100); #endif -} - -//Renders sixense laser pointers for UI selection in the oculus -void OculusManager::renderLaserPointers() { -#ifdef HAVE_LIBOVR - const float PALM_TIP_ROD_RADIUS = 0.002f; - - MyAvatar* myAvatar = Application::getInstance()->getAvatar(); - - //If the Oculus is enabled, we will draw a blue cursor ray - - for (size_t i = 0; i < myAvatar->getHand()->getNumPalms(); ++i) { - PalmData& palm = myAvatar->getHand()->getPalms()[i]; - if (palm.isActive()) { - glColor4f(0, 1, 1, 1); - glm::vec3 tip = getLaserPointerTipPosition(&palm); - glm::vec3 root = palm.getPosition(); - - //Scale the root vector with the avatar scale - myAvatar->scaleVectorRelativeToPosition(root); - - Avatar::renderJointConnectingCone(root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS); - } - } -#endif -} - -//Gets the tip position for the laser pointer -glm::vec3 OculusManager::getLaserPointerTipPosition(const PalmData* palm) { -#ifdef HAVE_LIBOVR - const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); - const float PALM_TIP_ROD_LENGTH_MULT = 40.0f; - - glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); - - glm::vec3 position = palm->getPosition(); - //scale the position with the avatar - Application::getInstance()->getAvatar()->scaleVectorRelativeToPosition(position); - - - glm::vec3 result; - if (applicationOverlay.calculateRayUICollisionPoint(position, direction, result)) { - return result; - } - - return palm->getPosition(); -#endif - return glm::vec3(0.0f); -} +} \ No newline at end of file diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 871e5d649a..db7a6e3f59 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -42,10 +42,6 @@ public: /// param \roll[out] roll in radians static void getEulerAngles(float& yaw, float& pitch, float& roll); static QSize getRenderTargetSize(); - - /// Renders a laser pointer for UI picking - static void renderLaserPointers(); - static glm::vec3 getLaserPointerTipPosition(const PalmData* palm); private: #ifdef HAVE_LIBOVR diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 7b3db26d8d..b6c08f6f4f 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -64,6 +64,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { _textureFov = Menu::getInstance()->getOculusUIAngularSize() * RADIANS_PER_DEGREE; + Application* application = Application::getInstance(); Overlays& overlays = application->getOverlays(); @@ -272,7 +273,7 @@ QPoint ApplicationOverlay::getOculusPalmClickLocation(const PalmData *palm) cons QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); - glm::vec3 tip = OculusManager::getLaserPointerTipPosition(palm); + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); glm::vec3 eyePos = myAvatar->getHead()->calculateAverageEyePosition(); glm::quat orientation = glm::inverse(myAvatar->getOrientation()); glm::vec3 dir = orientation * glm::normalize(application->getCamera()->getPosition() - tip); //direction of ray goes towards camera @@ -340,7 +341,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); //Render the sixense lasers - OculusManager::renderLaserPointers(); + myAvatar->renderLaserPointers(); glActiveTexture(GL_TEXTURE0); @@ -700,7 +701,7 @@ void ApplicationOverlay::renderPointersOculus(const glm::vec3& eyePos) { PalmData& palm = myAvatar->getHand()->getPalms()[i]; if (palm.isActive()) { - glm::vec3 tip = OculusManager::getLaserPointerTipPosition(&palm); + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); glm::vec3 tipPos = (tip - eyePos); float length = glm::length(eyePos - tip); From a370156720174627dc1ff72ed2d41c6871173d19 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 15:39:21 -0700 Subject: [PATCH 35/57] add a note for OS X users to use homebrew --- interface/external/faceshift/readme.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/external/faceshift/readme.txt b/interface/external/faceshift/readme.txt index 2277eb2de3..5a7333a9f6 100644 --- a/interface/external/faceshift/readme.txt +++ b/interface/external/faceshift/readme.txt @@ -2,6 +2,9 @@ Instructions for adding the Faceshift library to Interface Stephen Birarda, July 18th, 2014 +OS X users: You can also use homebrew to get the Faceshift library by tapping our repo - highfidelity/homebrew-formulas +and then calling 'brew install highfidelity/formulas/faceshift'. + You can download the Faceshift SDK from http://download.faceshift.com/faceshift-network.zip. Create a ‘faceshift’ folder under interface/externals. From f5d0241f1be13336663bcc08fa32f0679da49004 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 15:55:48 -0700 Subject: [PATCH 36/57] clarify in BUILD guide that qxmpp is an optional dependency --- BUILD.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/BUILD.md b/BUILD.md index 43c3ca7a09..6305362d49 100644 --- a/BUILD.md +++ b/BUILD.md @@ -4,7 +4,6 @@ Dependencies * [Qt](http://qt-project.org/downloads) ~> 5.2.0 * [zLib](http://www.zlib.net/) ~> 1.2.8 * [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.2 -* [qxmpp](https://github.com/qxmpp-project/qxmpp/) ~> 0.7.6 * [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1g * IMPORTANT: OpenSSL 1.0.1g is critical to avoid a security vulnerability. @@ -15,6 +14,10 @@ Dependencies * [GLEW](http://glew.sourceforge.net/) ~> 1.10.0 * [freeglut MSVC](http://www.transmissionzero.co.uk/software/freeglut-devel/) ~> 2.8.1 +####Optional +* [qxmpp](https://github.com/qxmpp-project/qxmpp/) ~> 0.7.6 + * enables text chat + CMake === Hifi uses CMake to generate build files and project files for your platform. @@ -150,13 +153,10 @@ The recommended route for CMake to find the external dependencies is to place al -> glm -> glm -> glm.hpp - -> gnutls + -> openssl -> bin -> include -> lib - -> qxmpp - -> include - -> lib -> zlib -> include -> lib From 1cde11d3e548f1451b57577232f86577c7f11a23 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 16:02:58 -0700 Subject: [PATCH 37/57] more build guide clarifications --- BUILD.md | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/BUILD.md b/BUILD.md index 6305362d49..3acf34d6f1 100644 --- a/BUILD.md +++ b/BUILD.md @@ -14,10 +14,6 @@ Dependencies * [GLEW](http://glew.sourceforge.net/) ~> 1.10.0 * [freeglut MSVC](http://www.transmissionzero.co.uk/software/freeglut-devel/) ~> 2.8.1 -####Optional -* [qxmpp](https://github.com/qxmpp-project/qxmpp/) ~> 0.7.6 - * enables text chat - CMake === Hifi uses CMake to generate build files and project files for your platform. @@ -218,21 +214,6 @@ This package contains only headers, so there's nothing to add to the PATH. Be careful with glm. For the folder other libraries would normally call 'include', the folder containing the headers, glm opts to use 'glm'. You will have a glm folder nested inside the top-level glm folder. -#### qxmpp - -Download a source-code release from the [qxmpp GitHub page](https://github.com/qxmpp-project/qxmpp/releases). - -Start a Visual Studio Command Prompt. - - mkdir %HIFI_LIB_DIR%\build - tar xfz qxmpp-0.7.6.tar.gz -C %HIFI_LIB_DIR%\build - cd %HIFI_LIB_DIR%\build\qxmpp-0.7.6 - qmake PREFIX=%HIFI_LIB_DIR%\qxmpp # This creates "Makefile" - nmake - nmake install - -Add to the PATH: `%HIFI_LIB_DIR%\qxmpp\lib` - #### Build High Fidelity using Visual Studio Follow the same build steps from the CMake section, but pass a different generator to CMake. @@ -250,7 +231,14 @@ If you need to debug Interface, you can run interface from within Visual Studio * Set the "Working Directory" for the Interface debugging sessions to the Debug output directory so that your application can load resources. Do this: right click interface and click Properties, choose Debugging from Configuration Properties, set Working Directory to .\Debug * Now you can run and debug interface through Visual Studio +Optional Components +=== + +####QXmpp + +You can find QXmpp [here](https://github.com/qxmpp-project/qxmpp). The inclusion of the QXmpp enables text chat in the Interface client. + #### Devices -You can support external input/output devices such as Leap Motion, Faceplus, Faceshift PrioVR, RTmidi, SixSense and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in /hifi/interface/external/ for the detailed explanation of the requirements to use the device. +You can support external input/output devices such as Leap Motion, Faceplus, Faceshift PrioVR, RTmidi, SixSense and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in (interface/external/)[interface/external] for the detailed explanation of the requirements to use the device. From c8cfcaf4adb6259947f98f411bb7687db0d8883d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 16:03:50 -0700 Subject: [PATCH 38/57] fix a poorly ordered link in devices section --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 3acf34d6f1..6cd75eaf7c 100644 --- a/BUILD.md +++ b/BUILD.md @@ -240,5 +240,5 @@ You can find QXmpp [here](https://github.com/qxmpp-project/qxmpp). The inclusion #### Devices -You can support external input/output devices such as Leap Motion, Faceplus, Faceshift PrioVR, RTmidi, SixSense and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in (interface/external/)[interface/external] for the detailed explanation of the requirements to use the device. +You can support external input/output devices such as Leap Motion, Faceplus, Faceshift PrioVR, RTmidi, SixSense and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device. From 880331de92c14cbecb3a85727868215ec6ddb910 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 16:04:34 -0700 Subject: [PATCH 39/57] some typo cleanup in BUILD guide --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 6cd75eaf7c..a32ef2c5af 100644 --- a/BUILD.md +++ b/BUILD.md @@ -240,5 +240,5 @@ You can find QXmpp [here](https://github.com/qxmpp-project/qxmpp). The inclusion #### Devices -You can support external input/output devices such as Leap Motion, Faceplus, Faceshift PrioVR, RTmidi, SixSense and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device. +You can support external input/output devices such as Leap Motion, Faceplus, Faceshift, PrioVR, MIDI, Razr Hydra and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device. From b8bdb2531380c6b40e6428838f39f98f38b4173c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 16:06:10 -0700 Subject: [PATCH 40/57] move zLib to Linux and Windows specific sections --- BUILD.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index a32ef2c5af..160cbce1c0 100644 --- a/BUILD.md +++ b/BUILD.md @@ -2,17 +2,18 @@ Dependencies === * [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.12.2 * [Qt](http://qt-project.org/downloads) ~> 5.2.0 -* [zLib](http://www.zlib.net/) ~> 1.2.8 * [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.2 * [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1g * IMPORTANT: OpenSSL 1.0.1g is critical to avoid a security vulnerability. #####Linux only * [freeglut](http://freeglut.sourceforge.net/) ~> 2.8.0 +* [zLib](http://www.zlib.net/) ~> 1.2.8 #####Windows only * [GLEW](http://glew.sourceforge.net/) ~> 1.10.0 * [freeglut MSVC](http://www.transmissionzero.co.uk/software/freeglut-devel/) ~> 2.8.1 +* [zLib](http://www.zlib.net/) ~> 1.2.8 CMake === From 944e3219444b1a35265e3db9f945f4aad2993e84 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 16:06:34 -0700 Subject: [PATCH 41/57] fix a typo in Qt section --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 160cbce1c0..3b737961ad 100644 --- a/BUILD.md +++ b/BUILD.md @@ -20,7 +20,7 @@ CMake Hifi uses CMake to generate build files and project files for your platform. ####Qt -In order for CMake to find the Qt5 find modules, you will need to set an ENV variable pointing to your CMake installation. +In order for CMake to find the Qt5 find modules, you will need to set an ENV variable pointing to your Qt installation. For example, a Qt5 5.2.0 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). From f581c17c9e72b2fb3f7c47848c5027616dd9571d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 16:09:12 -0700 Subject: [PATCH 42/57] note that QXmpp is an optional dependency --- BUILD.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/BUILD.md b/BUILD.md index 3b737961ad..159d9101fa 100644 --- a/BUILD.md +++ b/BUILD.md @@ -70,9 +70,8 @@ Should you choose not to install Qt5 via a package manager that handles dependen brew install cmake glm openssl brew install highfidelity/formulas/qt5 brew link qt5 --force - brew install highfidelity/formulas/qxmpp -We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. In the code block above qt5 and qxmpp are installed from formulas in this repository. +We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. In the code block above qt5 is installed from a formula in this repository. *Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.2.0 stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.* @@ -239,6 +238,8 @@ Optional Components You can find QXmpp [here](https://github.com/qxmpp-project/qxmpp). The inclusion of the QXmpp enables text chat in the Interface client. +OS X users who tap our [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) can install QXmpp via homebrew - `brew install highfidelity/formulas/qxmpp`. + #### Devices You can support external input/output devices such as Leap Motion, Faceplus, Faceshift, PrioVR, MIDI, Razr Hydra and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device. From f67fd3e48b9bc0b203e339c650115b8e799491c0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 17:51:10 -0700 Subject: [PATCH 43/57] modify gitignore to wildcard handle interface externals --- .gitignore | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index dcef7b5b69..6e6b69cb66 100644 --- a/.gitignore +++ b/.gitignore @@ -28,35 +28,9 @@ DerivedData *.hmap -# ignore oculus -interface/external/oculus/* -!interface/external/oculus/readme.txt - -# Ignore Sixense -interface/external/sixense/* -!interface/external/sixense/readme.txt - -# Ignore Visage -interface/external/visage/* -!interface/external/visage/readme.txt -interface/resources/visage/* -!interface/resources/visage/tracker.cfg - -# Ignore Faceplus -interface/external/faceplus/* -!interface/external/faceplus/readme.txt - -# Ignore Faceshift -interface/external/faceshift/* -!interface/external/faceshift/readme.txt - -# Ignore PrioVR -interface/external/priovr/* -!interface/external/priovr/readme.txt - -# Ignore RtMidi -interface/external/rtmidi/* -!interface/external/rtmidi/readme.txt +# ignore interface optional externals +interface/external/*/* +!interface/external/*/readme.txt # Ignore interfaceCache for Linux users interface/interfaceCache/ From 16fd8f224c3b89139502b3d3eb9ef88571996d8b Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 18 Jul 2014 21:42:20 -0700 Subject: [PATCH 44/57] Imrpoved repeat texture method, its almost finished --- interface/src/devices/OculusManager.cpp | 1 - interface/src/devices/SixenseManager.cpp | 5 +- interface/src/ui/ApplicationOverlay.cpp | 91 +++++++++++++----------- interface/src/ui/ApplicationOverlay.h | 2 +- 4 files changed, 54 insertions(+), 45 deletions(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index e6e958d89a..49164cc8dd 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -310,7 +310,6 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p Matrix4f proj = ovrMatrix4f_Projection(_eyeRenderDesc[eye].Fov, whichCamera.getNearClip(), whichCamera.getFarClip(), true); proj.Transpose(); - glMatrixMode(GL_PROJECTION); glLoadIdentity(); glLoadMatrixf((GLfloat *)proj.M); diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index f145b8d5be..69ffd7ecf4 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -381,8 +381,9 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { triggerButton = Qt::LeftButton; } - if (OculusManager::isConnected()) { - pos = application->getApplicationOverlay().getOculusPalmClickLocation(palm); + const bool useLasers = true; + if (useLasers) { + pos = application->getApplicationOverlay().getPalmClickLocation(palm); } else { // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection(); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index b6c08f6f4f..bf2dbb8404 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -64,7 +64,6 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { _textureFov = Menu::getInstance()->getOculusUIAngularSize() * RADIANS_PER_DEGREE; - Application* application = Application::getInstance(); Overlays& overlays = application->getOverlays(); @@ -139,6 +138,7 @@ void ApplicationOverlay::displayOverlayTexture() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); + glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); @@ -267,12 +267,13 @@ bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, } //Caculate the click location using one of the sixense controllers. Scale is not applied -QPoint ApplicationOverlay::getOculusPalmClickLocation(const PalmData *palm) const { +QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { + Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); - + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); glm::vec3 eyePos = myAvatar->getHead()->calculateAverageEyePosition(); glm::quat orientation = glm::inverse(myAvatar->getOrientation()); @@ -281,31 +282,44 @@ QPoint ApplicationOverlay::getOculusPalmClickLocation(const PalmData *palm) cons QPoint rv; - float t; + if (OculusManager::isConnected()) { + float t; - //We back the ray up by dir to ensure that it will not start inside the UI. - glm::vec3 adjustedPos = tipPos - dir; - //Find intersection of crosshair ray. - if (raySphereIntersect(dir, adjustedPos, _oculusuiRadius * myAvatar->getScale(), &t)){ - glm::vec3 collisionPos = adjustedPos + dir * t; - //Normalize it in case its not a radius of 1 - collisionPos = glm::normalize(collisionPos); - //If we hit the back hemisphere, mark it as not a collision - if (collisionPos.z > 0) { + //We back the ray up by dir to ensure that it will not start inside the UI. + glm::vec3 adjustedPos = tipPos - dir; + //Find intersection of crosshair ray. + if (raySphereIntersect(dir, adjustedPos, _oculusuiRadius * myAvatar->getScale(), &t)){ + glm::vec3 collisionPos = adjustedPos + dir * t; + //Normalize it in case its not a radius of 1 + collisionPos = glm::normalize(collisionPos); + //If we hit the back hemisphere, mark it as not a collision + if (collisionPos.z > 0) { + rv.setX(INT_MAX); + rv.setY(INT_MAX); + } else { + + float u = asin(collisionPos.x) / (_textureFov)+0.5f; + float v = 1.0 - (asin(collisionPos.y) / (_textureFov)+0.5f); + + rv.setX(u * glWidget->width()); + rv.setY(v * glWidget->height()); + } + } else { + //if they did not click on the overlay, just set the coords to INT_MAX rv.setX(INT_MAX); rv.setY(INT_MAX); - } else { - - float u = asin(collisionPos.x) / (_textureFov)+0.5f; - float v = 1.0 - (asin(collisionPos.y) / (_textureFov)+0.5f); - - rv.setX(u * glWidget->width()); - rv.setY(v * glWidget->height()); } } else { - //if they did not click on the overlay, just set the coords to INT_MAX - rv.setX(INT_MAX); - rv.setY(INT_MAX); + + float xTheta = atan2(dir.z, dir.x); + float yTheta = atan2(dir.y, dir.x); + + float fov = application->getCamera()->getFieldOfView() * RADIANS_PER_DEGREE; + float aspectRatio = glWidget->width() / glWidget->height(); + + rv.setX(xTheta / fov * aspectRatio * glWidget->width()); + rv.setY(yTheta / fov * glWidget->height()); + glMatrixMode(GL_PROJECTION); } return rv; } @@ -587,7 +601,7 @@ void ApplicationOverlay::renderControllerPointers() { stateWhenPressed[index] = _magActive[index]; } } else if (isPressed[index]) { - isPressed[index] = false; + isPressed[index] = false; //If the button was only pressed for < 250 ms //then disable it. @@ -601,8 +615,8 @@ void ApplicationOverlay::renderControllerPointers() { if (palmData->getTrigger() == 1.0f) { if (!triggerPressed[index]) { if (bumperPressed[index]) { - Menu::getInstance()->setIsOptionChecked(MenuOption::UserInterface, - !Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)); + Menu::getInstance()->setIsOptionChecked(MenuOption::UserInterface, + !Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)); } triggerPressed[index] = true; } @@ -612,21 +626,21 @@ void ApplicationOverlay::renderControllerPointers() { if ((controllerButtons & BUTTON_FWD)) { if (!bumperPressed[index]) { if (triggerPressed[index]) { - Menu::getInstance()->setIsOptionChecked(MenuOption::UserInterface, - !Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)); + Menu::getInstance()->setIsOptionChecked(MenuOption::UserInterface, + !Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)); } bumperPressed[index] = true; } } else { bumperPressed[index] = false; } - + //if we have the oculus, we should make the cursor smaller since it will be //magnified if (OculusManager::isConnected()) { - QPoint point = getOculusPalmClickLocation(palmData); - + QPoint point = getPalmClickLocation(palmData); + _mouseX[index] = point.x(); _mouseY[index] = point.y(); @@ -641,18 +655,13 @@ void ApplicationOverlay::renderControllerPointers() { continue; } - // Get directon relative to avatar orientation - glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); + int mouseX, mouseY; - // Get the angles, scaled between (-0.5,0.5) - float xAngle = (atan2(direction.z, direction.x) + M_PI_2); - float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); + QPoint res = getPalmClickLocation(palmData); + mouseX = res.x(); + mouseY = res.y(); - // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMult(); - - int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; - int mouseY = glWidget->height() / 2.0f + cursorRange * yAngle; + printf("%d %d\n", mouseX, mouseY); //If the cursor is out of the screen then don't render it if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 33ba818f40..0c2ccc7b21 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -32,7 +32,7 @@ public: void displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov); void computeOculusPickRay(float x, float y, glm::vec3& direction) const; void getClickLocation(int &x, int &y) const; - QPoint getOculusPalmClickLocation(const PalmData *palm) const; + QPoint getPalmClickLocation(const PalmData *palm) const; bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; From 98babe1751ddc53c22a1e9422122f0bc7396fea8 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 21 Jul 2014 12:17:13 -0700 Subject: [PATCH 45/57] Working hydra lazers when not in oculus --- interface/src/ui/ApplicationOverlay.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index bf2dbb8404..eb5bc7bf0f 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -269,7 +269,6 @@ bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, //Caculate the click location using one of the sixense controllers. Scale is not applied QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { - Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); @@ -310,16 +309,17 @@ QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { rv.setY(INT_MAX); } } else { - - float xTheta = atan2(dir.z, dir.x); - float yTheta = atan2(dir.y, dir.x); + glm::dmat4 projection; + application->getProjectionMatrix(&projection); - float fov = application->getCamera()->getFieldOfView() * RADIANS_PER_DEGREE; - float aspectRatio = glWidget->width() / glWidget->height(); + glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0)); + glm::vec3 ndcSpacePos; + if (clipSpacePos.w != 0) { + ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w; + } - rv.setX(xTheta / fov * aspectRatio * glWidget->width()); - rv.setY(yTheta / fov * glWidget->height()); - glMatrixMode(GL_PROJECTION); + rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * glWidget->width()); + rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * glWidget->height()); } return rv; } @@ -661,8 +661,6 @@ void ApplicationOverlay::renderControllerPointers() { mouseX = res.x(); mouseY = res.y(); - printf("%d %d\n", mouseX, mouseY); - //If the cursor is out of the screen then don't render it if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { _reticleActive[index] = false; From 03bc3a9ff85f0d98752b67043f5e5160a89d3b64 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 21 Jul 2014 12:35:46 -0700 Subject: [PATCH 46/57] Added checkable menu option for laser toggle --- interface/src/Application.cpp | 4 +++- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + interface/src/devices/SixenseManager.cpp | 3 +-- interface/src/ui/ApplicationOverlay.cpp | 23 +++++++++++++++++------ 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index da86e7b54f..aad9c4de58 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2809,7 +2809,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { _avatarManager.renderAvatars(mirrorMode ? Avatar::MIRROR_RENDER_MODE : Avatar::NORMAL_RENDER_MODE, selfAvatarOnly); //Render the sixense lasers - _myAvatar->renderLaserPointers(); + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { + _myAvatar->renderLaserPointers(); + } } if (!selfAvatarOnly) { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 38af5d4659..e65f680d4d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -413,6 +413,7 @@ Menu::Menu() : QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options"); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, true); QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 006f6dafc9..d400a60e2e 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -431,6 +431,7 @@ namespace MenuOption { const QString ShowIKConstraints = "Show IK Constraints"; const QString SimpleShadows = "Simple"; const QString SixenseMouseInput = "Enable Sixense Mouse Input"; + const QString SixenseLasers = "Enable Sixense UI Lasers"; const QString StandOnNearbyFloors = "Stand on nearby floors"; const QString Stars = "Stars"; const QString Stats = "Stats"; diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 69ffd7ecf4..a79e2cee74 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -381,8 +381,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { triggerButton = Qt::LeftButton; } - const bool useLasers = true; - if (useLasers) { + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { pos = application->getApplicationOverlay().getPalmClickLocation(palm); } else { // Get directon relative to avatar orientation diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index eb5bc7bf0f..89ae2fbb46 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -353,9 +353,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { Application* application = Application::getInstance(); MyAvatar* myAvatar = application->getAvatar(); - - //Render the sixense lasers - myAvatar->renderLaserPointers(); glActiveTexture(GL_TEXTURE0); @@ -656,10 +653,24 @@ void ApplicationOverlay::renderControllerPointers() { } int mouseX, mouseY; + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { + QPoint res = getPalmClickLocation(palmData); + mouseX = res.x(); + mouseY = res.y(); + } else { + // Get directon relative to avatar orientation + glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); - QPoint res = getPalmClickLocation(palmData); - mouseX = res.x(); - mouseY = res.y(); + // Get the angles, scaled between (-0.5,0.5) + float xAngle = (atan2(direction.z, direction.x) + M_PI_2); + float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); + + // Get the pixel range over which the xAngle and yAngle are scaled + float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMult(); + + mouseX = (glWidget->width() / 2.0f + cursorRange * xAngle); + mouseY = (glWidget->height() / 2.0f + cursorRange * yAngle); + } //If the cursor is out of the screen then don't render it if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { From a70eb1224598d193ec61859b1c2e057361962a7b Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 21 Jul 2014 13:21:31 -0700 Subject: [PATCH 47/57] made Audio::_ringbuffer 10 frames for nonWindows; fixed/separated framesAvailable stats for _ringBuffer and _audioOutput; made QAudioOutput overflow check disabled by default --- interface/src/Audio.cpp | 17 +++++++++++------ interface/src/Audio.h | 4 ++-- interface/src/Menu.cpp | 2 +- interface/src/ui/Stats.cpp | 4 ++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 3fa1d6a082..66f371007f 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -85,8 +85,11 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) : // slower than real time (or at least the desired sample rate). If you increase the size of the ring buffer, then it // this delay will slowly add up and the longer someone runs, they more delayed their audio will be. _inputRingBuffer(0), +#ifdef _WIN32 + _ringBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO, false, 100), +#else _ringBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO), // DO NOT CHANGE THIS UNLESS YOU SOLVE THE AUDIO DEVICE DRIFT PROBLEM!!! - +#endif _isStereoInput(false), _averagedLatency(0.0), _measuredJitter(0), @@ -790,7 +793,7 @@ AudioStreamStats Audio::getDownstreamAudioStreamStats() const { stats._timeGapWindowMax = _interframeTimeGapStats.getWindowMax(); stats._timeGapWindowAverage = _interframeTimeGapStats.getWindowAverage(); - stats._ringBufferFramesAvailable = getFramesAvailableInRingAndAudioOutputBuffers(); + stats._ringBufferFramesAvailable = _ringBuffer.framesAvailable(); stats._ringBufferFramesAvailableAverage = _framesAvailableStats.getWindowAverage(); stats._ringBufferDesiredJitterBufferFrames = getDesiredJitterBufferFrames(); stats._ringBufferStarveCount = _starveCount; @@ -807,7 +810,7 @@ AudioStreamStats Audio::getDownstreamAudioStreamStats() const { void Audio::sendDownstreamAudioStatsPacket() { // since this function is called every second, we'll sample the number of audio frames available here. - _framesAvailableStats.update(getFramesAvailableInRingAndAudioOutputBuffers()); + _framesAvailableStats.update(_ringBuffer.framesAvailable() + getFramesAvailableInAudioOutputBuffer()); // push the current seq number stats into history, which moves the history window forward 1s // (since that's how often pushStatsToHistory() is called) @@ -1613,8 +1616,10 @@ int Audio::calculateNumberOfFrameSamples(int numBytes) { return frameSamples; } -int Audio::getFramesAvailableInRingAndAudioOutputBuffers() const { - int framesInAudioOutputBuffer = (_audioOutput->bufferSize() - _audioOutput->bytesFree()) +int Audio::getFramesAvailableInAudioOutputBuffer() const { + float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float)_outputFormat.sampleRate()) + * (_desiredOutputFormat.channelCount() / (float)_outputFormat.channelCount()); + + return (_audioOutput->bufferSize() - _audioOutput->bytesFree()) * networkOutputToOutputRatio / (sizeof(int16_t) * _ringBuffer.getNumFrameSamples()); - return _ringBuffer.framesAvailable() + framesInAudioOutputBuffer; } diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 0747bdf501..eea5ff26fa 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -79,6 +79,8 @@ public: const SequenceNumberStats& getIncomingMixedAudioSequenceNumberStats() const { return _incomingMixedAudioSequenceNumberStats; } + int getFramesAvailableInAudioOutputBuffer() const; + public slots: void start(); void stop(); @@ -239,8 +241,6 @@ private: void renderGrid(const float* color, int x, int y, int width, int height, int rows, int cols); void renderLineStrip(const float* color, int x, int y, int n, int offset, const QByteArray* byteArray); - int getFramesAvailableInRingAndAudioOutputBuffers() const; - // Audio scope data static const unsigned int NETWORK_SAMPLES_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; static const unsigned int DEFAULT_FRAMES_PER_SCOPE = 5; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 38af5d4659..e21299da11 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -589,7 +589,7 @@ Menu::Menu() : Qt::CTRL | Qt::SHIFT | Qt::Key_U, false); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::DisableQAudioOutputOverflowCheck, 0, false); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::DisableQAudioOutputOverflowCheck, 0, true); addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel, Qt::CTRL | Qt::SHIFT | Qt::Key_V, diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index a751f43059..f37f8c9934 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -339,10 +339,10 @@ void Stats::display( AudioStreamStats downstreamAudioStreamStats = audio->getDownstreamAudioStreamStats(); - sprintf(downstreamAudioStatsString, " mix: %.2f%%/%.2f%%, %u/%u/%u", downstreamAudioStreamStats._packetStreamStats.getLostRate()*100.0f, + sprintf(downstreamAudioStatsString, " mix: %.2f%%/%.2f%%, %u/%u/%u+%u", downstreamAudioStreamStats._packetStreamStats.getLostRate()*100.0f, downstreamAudioStreamStats._packetStreamWindowStats.getLostRate() * 100.0f, downstreamAudioStreamStats._ringBufferDesiredJitterBufferFrames, downstreamAudioStreamStats._ringBufferFramesAvailableAverage, - downstreamAudioStreamStats._ringBufferFramesAvailable); + downstreamAudioStreamStats._ringBufferFramesAvailable, audio->getFramesAvailableInAudioOutputBuffer()); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamAudioStatsString, color); From b21bac25a1c9be52dbbfffe37b21f48b6cb33608 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 21 Jul 2014 13:41:41 -0700 Subject: [PATCH 48/57] bug fix for broken seams in models don't hijack the "weight" variable because it's used elsewhere --- libraries/fbx/src/FBXReader.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 594ad7ebe5..f366b6a5c3 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1743,12 +1743,10 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) if (weight > EXPANSION_WEIGHT_THRESHOLD) { const glm::vec3& vertex = extracted.mesh.vertices.at(it.value()); float proj = glm::dot(boneDirection, boneEnd - vertex); - if (proj < 0.0f || proj > boneLength) { - weight *= 0.5f; - } + float radiusWeight = (proj < 0.0f || proj > boneLength) ? 0.5f * weight : weight; - jointShapeInfo.sumVertexWeights += weight; - jointShapeInfo.sumWeightedRadii += weight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj); + jointShapeInfo.sumVertexWeights += radiusWeight; + jointShapeInfo.sumWeightedRadii += radiusWeight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj); ++jointShapeInfo.numVertexWeights; glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd)); @@ -1796,13 +1794,10 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) glm::vec3 averageVertex(0.f); foreach (const glm::vec3& vertex, extracted.mesh.vertices) { - float weight = 1.0f; float proj = glm::dot(boneDirection, boneEnd - vertex); - if (proj < 0.0f || proj > boneLength) { - weight *= 0.5f; - } - jointShapeInfo.sumVertexWeights += weight; - jointShapeInfo.sumWeightedRadii += weight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj); + float radiusWeight = (proj < 0.0f || proj > boneLength) ? 0.5f : 1.0f; + jointShapeInfo.sumVertexWeights += radiusWeight; + jointShapeInfo.sumWeightedRadii += radiusWeight * radiusScale * glm::distance(vertex, boneEnd - boneDirection * proj); ++jointShapeInfo.numVertexWeights; glm::vec3 vertexInJointFrame = rotateMeshToJoint * (radiusScale * (vertex - boneEnd)); From 9571098b8b40f0f2c3dfcca8a907f260280230af Mon Sep 17 00:00:00 2001 From: palomapalmer97 Date: Mon, 21 Jul 2014 14:10:09 -0700 Subject: [PATCH 49/57] voxel scripts voxelwall is an 8x8 wall made of voxels that transitions from a bright red to dark blue voxelsoundwaves was an array of voxels that was manipulated to resemble an audio scope that responds to loudness and changes its height depending on the loudness of the audio that the microphone picks up --- examples/editVoxels.js | 2 +- examples/voxelsoundwaves.js | 226 ++++++++++++++++++++++++++++++++++++ examples/voxelwall.js | 103 ++++++++++++++++ 3 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 examples/voxelsoundwaves.js create mode 100644 examples/voxelwall.js diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 412612fdad..8d2a516f55 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -65,7 +65,7 @@ colors[4] = { red: 236, green: 174, blue: 0 }; colors[5] = { red: 234, green: 133, blue: 0 }; colors[6] = { red: 211, green: 115, blue: 0 }; colors[7] = { red: 48, green: 116, blue: 119 }; -colors[8] = { red: 31, green: 64, blue: 64 }; +colors[8] = { red: 36, green: 64, blue: 64 }; var numColors = 9; var whichColor = 0; // Starting color is 'Copy' mode diff --git a/examples/voxelsoundwaves.js b/examples/voxelsoundwaves.js new file mode 100644 index 0000000000..6de46e23cf --- /dev/null +++ b/examples/voxelsoundwaves.js @@ -0,0 +1,226 @@ + + + +var wallX = 8000; +var wallY = 8000; +var wallZ = 8000;//location + + +var VOXELSIZE=12;//size of each voxel + +var FACTOR = 0.75; + +var loud=1.0; +var maxLoud=500; +var minLoud=200;//range of loudness + + + +var maxB={color:225,direction:0,speed:1}; +var minB={color:50,direction:1,speed:1}; +var maxG={color:200,direction:0,speed:1}; +var minG={color:30,direction:1,speed:1}; +var maxR={color:255,direction:0,speed:1}; +var minR={color:150,direction:1,speed:1};//color objects +var addVoxArray=[]; +var removeVoxArray=[]; +var numAddVox=0; +var numRemoveVox=0;//array for voxels removed and added + + +var height; +var wallWidth=34; +var wallHeight=25; +var maxHeight=wallHeight; +var minHeight=0;//properties of wall + + +var heightSamplesArray=[]; +var sampleIndex=0;//declare new array of heights + +var direction=1; + + + + +//initiate and fill array of heights +for(var k=0;kgetAverageFramesAvailableInAudioOutputBuffer(), downstreamAudioStreamStats._ringBufferFramesAvailable, audio->getFramesAvailableInAudioOutputBuffer()); verticalOffset += STATS_PELS_PER_LINE; From 05ea5f8d214e44a922d6f217400525a588378e79 Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 21 Jul 2014 14:32:44 -0700 Subject: [PATCH 51/57] changed menu item name for QAudioOutput overflow check --- interface/src/Menu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 006f6dafc9..efec231306 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -348,7 +348,7 @@ namespace MenuOption { const QString DisableActivityLogger = "Disable Activity Logger"; const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD"; const QString DisableNackPackets = "Disable NACK Packets"; - const QString DisableQAudioOutputOverflowCheck = "Disable QAudioOutput Overflow Check"; + const QString DisableQAudioOutputOverflowCheck = "Disable Audio Output Overflow Check"; const QString DisplayFrustum = "Display Frustum"; const QString DisplayHands = "Display Hands"; const QString DisplayHandTargets = "Display Hand Targets"; From 621f0725ccee64908a4c545b9ee79aa3abbb6865 Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 21 Jul 2014 15:39:33 -0700 Subject: [PATCH 52/57] added input ring buffer stats to interface --- interface/src/Audio.cpp | 24 ++++++++++++------- interface/src/Audio.h | 18 +++++++++----- interface/src/ui/Stats.cpp | 18 +++++++++++--- .../audio/src/PositionalAudioRingBuffer.h | 4 ++-- 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index ae4caa76f5..e5e71b8dcb 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -130,7 +130,8 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) : _outgoingAvatarAudioSequenceNumber(0), _incomingMixedAudioSequenceNumberStats(INCOMING_SEQ_STATS_HISTORY_LENGTH), _interframeTimeGapStats(TIME_GAPS_STATS_INTERVAL_SAMPLES, TIME_GAP_STATS_WINDOW_INTERVALS), - _ringBufferFramesAvailableStats(1, FRAMES_AVAILABLE_STATS_WINDOW_SECONDS), + _inputRingBufferFramesAvailableStats(1, FRAMES_AVAILABLE_STATS_WINDOW_SECONDS), + _outputRingBufferFramesAvailableStats(1, FRAMES_AVAILABLE_STATS_WINDOW_SECONDS), _audioOutputBufferFramesAvailableStats(1, FRAMES_AVAILABLE_STATS_WINDOW_SECONDS) { // clear the array of locally injected samples @@ -795,7 +796,7 @@ AudioStreamStats Audio::getDownstreamAudioStreamStats() const { stats._timeGapWindowAverage = _interframeTimeGapStats.getWindowAverage(); stats._ringBufferFramesAvailable = _ringBuffer.framesAvailable(); - stats._ringBufferFramesAvailableAverage = _ringBufferFramesAvailableStats.getWindowAverage(); + stats._ringBufferFramesAvailableAverage = _outputRingBufferFramesAvailableStats.getWindowAverage(); stats._ringBufferDesiredJitterBufferFrames = getDesiredJitterBufferFrames(); stats._ringBufferStarveCount = _starveCount; stats._ringBufferConsecutiveNotMixedCount = _consecutiveNotMixedCount; @@ -810,9 +811,11 @@ AudioStreamStats Audio::getDownstreamAudioStreamStats() const { void Audio::sendDownstreamAudioStatsPacket() { + _inputRingBufferFramesAvailableStats.update(getInputRingBufferFramesAvailable()); + // since this function is called every second, we'll sample the number of audio frames available here. - _ringBufferFramesAvailableStats.update(_ringBuffer.framesAvailable()); - _audioOutputBufferFramesAvailableStats.update(getFramesAvailableInAudioOutputBuffer()); + _outputRingBufferFramesAvailableStats.update(_ringBuffer.framesAvailable()); + _audioOutputBufferFramesAvailableStats.update(getOutputRingBufferFramesAvailable()); // push the current seq number stats into history, which moves the history window forward 1s // (since that's how often pushStatsToHistory() is called) @@ -1596,7 +1599,7 @@ const float Audio::CALLBACK_ACCELERATOR_RATIO = 2.0f; const float Audio::CALLBACK_ACCELERATOR_RATIO = 2.0f; #endif -int Audio::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) { +int Audio::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const { int numInputCallbackBytes = (int)(((NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL * format.channelCount() * (format.sampleRate() / SAMPLE_RATE)) @@ -1605,7 +1608,7 @@ int Audio::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) { return numInputCallbackBytes; } -float Audio::calculateDeviceToNetworkInputRatio(int numBytes) { +float Audio::calculateDeviceToNetworkInputRatio(int numBytes) const { float inputToNetworkInputRatio = (int)((_numInputCallbackBytes * CALLBACK_ACCELERATOR_RATIO / NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL) + 0.5f); @@ -1613,15 +1616,20 @@ float Audio::calculateDeviceToNetworkInputRatio(int numBytes) { return inputToNetworkInputRatio; } -int Audio::calculateNumberOfFrameSamples(int numBytes) { +int Audio::calculateNumberOfFrameSamples(int numBytes) const { int frameSamples = (int)(numBytes * CALLBACK_ACCELERATOR_RATIO + 0.5f) / sizeof(int16_t); return frameSamples; } -int Audio::getFramesAvailableInAudioOutputBuffer() const { +int Audio::getOutputRingBufferFramesAvailable() const { float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float)_outputFormat.sampleRate()) * (_desiredOutputFormat.channelCount() / (float)_outputFormat.channelCount()); return (_audioOutput->bufferSize() - _audioOutput->bytesFree()) * networkOutputToOutputRatio / (sizeof(int16_t) * _ringBuffer.getNumFrameSamples()); } + +int Audio::getInputRingBufferFramesAvailable() const { + float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(_numInputCallbackBytes); + return _inputRingBuffer.samplesAvailable() / inputToNetworkInputRatio / _inputRingBuffer.getNumFrameSamples(); +} \ No newline at end of file diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 9bc0ecef93..ed50815d78 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -79,8 +79,11 @@ public: const SequenceNumberStats& getIncomingMixedAudioSequenceNumberStats() const { return _incomingMixedAudioSequenceNumberStats; } - int getFramesAvailableInAudioOutputBuffer() const; - int getAverageFramesAvailableInAudioOutputBuffer() const { return (int)_audioOutputBufferFramesAvailableStats.getWindowAverage(); } + int getInputRingBufferFramesAvailable() const; + int getInputRingBufferAverageFramesAvailable() const { return (int)_inputRingBufferFramesAvailableStats.getWindowAverage(); } + + int getOutputRingBufferFramesAvailable() const; + int getOutputRingBufferAverageFramesAvailable() const { return (int)_audioOutputBufferFramesAvailableStats.getWindowAverage(); } public slots: void start(); @@ -224,9 +227,9 @@ private: // Callback acceleration dependent calculations static const float CALLBACK_ACCELERATOR_RATIO; - int calculateNumberOfInputCallbackBytes(const QAudioFormat& format); - int calculateNumberOfFrameSamples(int numBytes); - float calculateDeviceToNetworkInputRatio(int numBytes); + int calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const; + int calculateNumberOfFrameSamples(int numBytes) const; + float calculateDeviceToNetworkInputRatio(int numBytes) const; // Audio scope methods for allocation/deallocation void allocateScope(); @@ -269,7 +272,10 @@ private: SequenceNumberStats _incomingMixedAudioSequenceNumberStats; MovingMinMaxAvg _interframeTimeGapStats; - MovingMinMaxAvg _ringBufferFramesAvailableStats; + + MovingMinMaxAvg _inputRingBufferFramesAvailableStats; + + MovingMinMaxAvg _outputRingBufferFramesAvailableStats; MovingMinMaxAvg _audioOutputBufferFramesAvailableStats; }; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index aabfe08869..5d6682e413 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -280,7 +280,7 @@ void Stats::display( Audio* audio = Application::getInstance()->getAudio(); const QHash& audioMixerInjectedStreamAudioStatsMap = audio->getAudioMixerInjectedStreamAudioStatsMap(); - lines = _expanded ? 11 + (audioMixerInjectedStreamAudioStatsMap.size() + 2) * 3 : 3; + lines = _expanded ? 13 + (audioMixerInjectedStreamAudioStatsMap.size() + 2) * 3 : 3; drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; @@ -314,6 +314,18 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing, color); + char inputAudioLabelString[] = "Input: (avail_avg_10s/avail)"; + + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, inputAudioLabelString, color); + + char inputAudioStatsString[512]; + sprintf(inputAudioStatsString, " %d/%d", audio->getInputRingBufferFramesAvailable(), + audio->getInputRingBufferAverageFramesAvailable()); + + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, inputAudioStatsString, color); + char audioMixerStatsLabelString[] = "AudioMixer stats:"; char streamStatsFormatLabelString[] = "lost%/lost_30s%"; char streamStatsFormatLabelString2[] = "desired/avail_avg_10s/avail"; @@ -342,8 +354,8 @@ void Stats::display( sprintf(downstreamAudioStatsString, " mix: %.2f%%/%.2f%%, %u/%u+%d/%u+%d", downstreamAudioStreamStats._packetStreamStats.getLostRate()*100.0f, downstreamAudioStreamStats._packetStreamWindowStats.getLostRate() * 100.0f, downstreamAudioStreamStats._ringBufferDesiredJitterBufferFrames, downstreamAudioStreamStats._ringBufferFramesAvailableAverage, - audio->getAverageFramesAvailableInAudioOutputBuffer(), - downstreamAudioStreamStats._ringBufferFramesAvailable, audio->getFramesAvailableInAudioOutputBuffer()); + audio->getOutputRingBufferAverageFramesAvailable(), + downstreamAudioStreamStats._ringBufferFramesAvailable, audio->getOutputRingBufferFramesAvailable()); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamAudioStatsString, color); diff --git a/libraries/audio/src/PositionalAudioRingBuffer.h b/libraries/audio/src/PositionalAudioRingBuffer.h index c4645c362c..f0d6aff80b 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.h +++ b/libraries/audio/src/PositionalAudioRingBuffer.h @@ -30,9 +30,9 @@ const int TIME_GAPS_FOR_STATS_PACKET_INTERVAL_SAMPLES = USECS_PER_SECOND / BUFFE const int TIME_GAPS_FOR_STATS_PACKET_WINDOW_INTERVALS = 30; // the stats for calculating the average frames available will recalculate every ~1 second -// and will include data for the past ~2 seconds +// and will include data for the past ~10 seconds const int FRAMES_AVAILABLE_STATS_INTERVAL_SAMPLES = USECS_PER_SECOND / BUFFER_SEND_INTERVAL_USECS; -const int FRAMES_AVAILABLE_STATS_WINDOW_INTERVALS = 2; +const int FRAMES_AVAILABLE_STATS_WINDOW_INTERVALS = 10; const int AUDIOMIXER_INBOUND_RING_BUFFER_FRAME_CAPACITY = 100; From 0bcc13aec96c19e84cf0632a8b85218a213c8d22 Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 21 Jul 2014 17:12:03 -0700 Subject: [PATCH 53/57] made Audio always push a whole number of frames to the audio output --- interface/src/Audio.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index e5e71b8dcb..478c0d0c55 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -977,8 +977,9 @@ void Audio::processReceivedAudio(const QByteArray& audioByteArray) { if (Menu::getInstance()->isOptionChecked(MenuOption::DisableQAudioOutputOverflowCheck)) { numNetworkOutputSamples = _ringBuffer.samplesAvailable(); } else { - int numSamplesAudioOutputRoomFor = _audioOutput->bytesFree() / sizeof(int16_t); - numNetworkOutputSamples = std::min(_ringBuffer.samplesAvailable(), (int)(numSamplesAudioOutputRoomFor * networkOutputToOutputRatio)); + // make sure to push a whole number of frames to the audio output + int numFramesAudioOutputRoomFor = _audioOutput->bytesFree() / sizeof(int16_t) * networkOutputToOutputRatio / _ringBuffer.getNumFrameSamples(); + numNetworkOutputSamples = std::min(_ringBuffer.samplesAvailable(), numFramesAudioOutputRoomFor * _ringBuffer.getNumFrameSamples()); } // if there is data in the ring buffer and room in the audio output, decide what to do From f6ef61e76a9ed534e99de8419a238e653a3fd4ec Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 21 Jul 2014 17:15:08 -0700 Subject: [PATCH 54/57] fixed some typos --- interface/src/Audio.cpp | 2 +- interface/src/ui/Stats.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 478c0d0c55..be348ab635 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -1633,4 +1633,4 @@ int Audio::getOutputRingBufferFramesAvailable() const { int Audio::getInputRingBufferFramesAvailable() const { float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(_numInputCallbackBytes); return _inputRingBuffer.samplesAvailable() / inputToNetworkInputRatio / _inputRingBuffer.getNumFrameSamples(); -} \ No newline at end of file +} diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 5d6682e413..412de6e7b0 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -314,14 +314,14 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing, color); - char inputAudioLabelString[] = "Input: (avail_avg_10s/avail)"; + char inputAudioLabelString[] = "Input: avail_avg_10s/avail"; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, inputAudioLabelString, color); char inputAudioStatsString[512]; - sprintf(inputAudioStatsString, " %d/%d", audio->getInputRingBufferFramesAvailable(), - audio->getInputRingBufferAverageFramesAvailable()); + sprintf(inputAudioStatsString, " %d/%d", audio->getInputRingBufferAverageFramesAvailable(), + audio->getInputRingBufferFramesAvailable()); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, inputAudioStatsString, color); From 7bbbde895733c857a92c2bdc82f187a8eb82e530 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 21 Jul 2014 22:18:03 -0700 Subject: [PATCH 55/57] add useDynamicJitterBuffers to the audio mixer stats page --- assignment-client/src/audio/AudioMixer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index b008a20aa7..37d4a7993d 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -352,6 +352,8 @@ void AudioMixer::readPendingDatagrams() { void AudioMixer::sendStatsPacket() { static QJsonObject statsObject; + + statsObject["useDynamicJitterBuffers"] = _useDynamicJitterBuffers; statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100.0f; statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio; @@ -488,6 +490,7 @@ void AudioMixer::run() { _useDynamicJitterBuffers = true; } else { qDebug() << "Dynamic jitter buffers disabled, using old behavior."; + _useDynamicJitterBuffers = false; } } From f325500d1489dcf6b9499e56d7efe7ab04c93707 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 22 Jul 2014 14:38:41 -0700 Subject: [PATCH 56/57] repairs to stubbed faceshft constructor --- interface/src/devices/Faceshift.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp index 9b2fc7e821..b5cba8348c 100644 --- a/interface/src/devices/Faceshift.cpp +++ b/interface/src/devices/Faceshift.cpp @@ -27,8 +27,6 @@ using namespace std; const quint16 FACESHIFT_PORT = 33433; -#ifdef HAVE_FACESHIFT - Faceshift::Faceshift() : _tcpEnabled(true), _tcpRetryCount(0), @@ -53,6 +51,7 @@ Faceshift::Faceshift() : _longTermAverageEyeYaw(0.0f), _longTermAverageInitialized(false) { +#ifdef HAVE_FACESHIFT connect(&_tcpSocket, SIGNAL(connected()), SLOT(noteConnected())); connect(&_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(noteError(QAbstractSocket::SocketError))); connect(&_tcpSocket, SIGNAL(readyRead()), SLOT(readFromSocket())); @@ -61,18 +60,13 @@ Faceshift::Faceshift() : connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams())); _udpSocket.bind(FACESHIFT_PORT); -} - -#else - -Faceshift::Faceshift() { - -} - #endif +} void Faceshift::init() { +#ifdef HAVE_FACESHIFT setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift)); +#endif } bool Faceshift::isConnectedOrConnecting() const { From 19618182a39acc83ac26867c60caed885469e464 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 22 Jul 2014 14:40:53 -0700 Subject: [PATCH 57/57] don't show Faceshift option if not present --- interface/src/Menu.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0d46fbd251..16c3419561 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -391,12 +391,15 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollideAsRagdoll); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false); +#ifdef HAVE_FACESHIFT addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Faceshift, 0, true, appInstance->getFaceshift(), SLOT(setTCPEnabled(bool))); +#endif + #ifdef HAVE_FACEPLUS addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Faceplus, 0, true, appInstance->getFaceplus(), SLOT(updateEnabled()));