From db73d8d2f4d897caf89dcb69f907fad95701f22a Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 14 Jul 2014 12:23:00 -0700 Subject: [PATCH 01/53] add body angular acceleration --- interface/src/Hair.cpp | 12 ++++++++---- interface/src/Hair.h | 2 ++ interface/src/avatar/Avatar.cpp | 6 +++++- interface/src/avatar/Avatar.h | 4 ++++ interface/src/avatar/MyAvatar.cpp | 3 ++- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index e07b624d89..810833d750 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -18,7 +18,8 @@ const float HAIR_DAMPING = 0.99f; const float CONSTRAINT_RELAXATION = 10.0f; const float HAIR_ACCELERATION_COUPLING = 0.025f; -const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; +const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.01f; +const float HAIR_ANGULAR_ACCELERATION_COUPLING = 0.001f; const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; const float HAIR_STIFFNESS = 0.005f; const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); @@ -36,6 +37,7 @@ Hair::Hair(int strands, _radius(radius), _acceleration(0.0f), _angularVelocity(0.0f), + _angularAcceleration(0.0f), _gravity(0.0f) { _hairPosition = new glm::vec3[_strands * _links]; @@ -127,13 +129,15 @@ void Hair::simulate(float deltaTime) { const float ANGULAR_VELOCITY_MIN = 0.001f; if (glm::length(_angularVelocity) > ANGULAR_VELOCITY_MIN) { glm::vec3 yawVector = _hairPosition[vertexIndex]; + glm::vec3 angularVelocity = _angularVelocity * HAIR_ANGULAR_VELOCITY_COUPLING; + glm::vec3 angularAcceleration = _angularAcceleration * HAIR_ANGULAR_ACCELERATION_COUPLING; yawVector.y = 0.f; if (glm::length(yawVector) > EPSILON) { float radius = glm::length(yawVector); yawVector = glm::normalize(yawVector); float angle = atan2f(yawVector.x, -yawVector.z) + PI; glm::vec3 delta = glm::vec3(-1.f, 0.f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 1, 0)); - _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.y * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + _hairPosition[vertexIndex] -= delta * radius * (angularVelocity.y - angularAcceleration.y) * deltaTime; } glm::vec3 pitchVector = _hairPosition[vertexIndex]; pitchVector.x = 0.f; @@ -142,7 +146,7 @@ void Hair::simulate(float deltaTime) { pitchVector = glm::normalize(pitchVector); float angle = atan2f(pitchVector.y, -pitchVector.z) + PI; glm::vec3 delta = glm::vec3(0.0f, 1.0f, 0.f) * glm::angleAxis(angle, glm::vec3(1, 0, 0)); - _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.x * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + _hairPosition[vertexIndex] -= delta * radius * (angularVelocity.x - angularAcceleration.x) * deltaTime; } glm::vec3 rollVector = _hairPosition[vertexIndex]; rollVector.z = 0.f; @@ -151,7 +155,7 @@ void Hair::simulate(float deltaTime) { pitchVector = glm::normalize(rollVector); float angle = atan2f(rollVector.x, rollVector.y) + PI; glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 0, 1)); - _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.z * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + _hairPosition[vertexIndex] -= delta * radius * (angularVelocity.z - angularAcceleration.z) * deltaTime; } } diff --git a/interface/src/Hair.h b/interface/src/Hair.h index 0ca507ee23..12a573db58 100644 --- a/interface/src/Hair.h +++ b/interface/src/Hair.h @@ -40,6 +40,7 @@ public: void render(); void setAcceleration(const glm::vec3& acceleration) { _acceleration = acceleration; } void setAngularVelocity(const glm::vec3& angularVelocity) { _angularVelocity = angularVelocity; } + void setAngularAcceleration(const glm::vec3& angularAcceleration) { _angularAcceleration = angularAcceleration; } void setGravity(const glm::vec3& gravity) { _gravity = gravity; } private: @@ -58,6 +59,7 @@ private: int* _hairConstraints; glm::vec3 _acceleration; glm::vec3 _angularVelocity; + glm::vec3 _angularAcceleration; glm::vec3 _gravity; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index fd8f2d836b..4645dd6363 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -52,6 +52,8 @@ Avatar::Avatar() : _lastVelocity(0.0f, 0.0f, 0.0f), _acceleration(0.0f, 0.0f, 0.0f), _angularVelocity(0.0f, 0.0f, 0.0f), + _lastAngularVelocity(0.0f, 0.0f, 0.0f), + _angularAcceleration(0.0f, 0.0f, 0.0f), _lastOrientation(), _leanScale(0.5f), _scale(1.0f), @@ -168,7 +170,8 @@ void Avatar::simulate(float deltaTime) { if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { PerformanceTimer perfTimer("hair"); _hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame()); - _hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setAngularVelocity((getAngularVelocity() + getHead()->getAngularVelocity()) * getHead()->getFinalOrientationInWorldFrame()); + _hair.setAngularAcceleration(getAngularAcceleration() * getHead()->getFinalOrientationInWorldFrame()); _hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame()); _hair.simulate(deltaTime); } @@ -204,6 +207,7 @@ void Avatar::updateAcceleration(float deltaTime) { glm::quat orientation = getOrientation(); glm::quat delta = glm::inverse(_lastOrientation) * orientation; _angularVelocity = safeEulerAngles(delta) * (1.f / deltaTime); + _angularAcceleration = (_angularVelocity - _lastAngularVelocity) * (1.f / deltaTime); _lastOrientation = getOrientation(); } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 2d85aa70a5..6a6cdb8511 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -152,6 +152,8 @@ public: glm::vec3 getAcceleration() const { return _acceleration; } glm::vec3 getAngularVelocity() const { return _angularVelocity; } + glm::vec3 getAngularAcceleration() const { return _angularAcceleration; } + /// Scales a world space position vector relative to the avatar position and scale /// \param vector position to be scaled. Will store the result @@ -176,6 +178,8 @@ protected: glm::vec3 _lastVelocity; glm::vec3 _acceleration; glm::vec3 _angularVelocity; + glm::vec3 _lastAngularVelocity; + glm::vec3 _angularAcceleration; glm::quat _lastOrientation; float _leanScale; float _scale; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3ec6652889..d4bafc388f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -186,7 +186,8 @@ void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("hair"); if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { _hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame()); - _hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setAngularVelocity((getAngularVelocity() + getHead()->getAngularVelocity()) * getHead()->getFinalOrientationInWorldFrame()); + _hair.setAngularAcceleration(getAngularAcceleration() * getHead()->getFinalOrientationInWorldFrame()); _hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame()); _hair.simulate(deltaTime); } From 074d7f40bba8be14d567a866c3a08bf4040293c0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 17 Jul 2014 15:10:32 -0700 Subject: [PATCH 02/53] 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 03/53] 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 04/53] 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 05/53] 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 06/53] 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 07/53] 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 08/53] 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 09/53] 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 10/53] 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 11/53] 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 12/53] 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 13/53] 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 14/53] 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 15/53] 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 16/53] 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 17/53] 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 18/53] 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 19/53] 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 20/53] 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 21/53] 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 22/53] 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 23/53] 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 24/53] 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 25/53] 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 26/53] 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 27/53] 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 28/53] 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 29/53] 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 30/53] 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 31/53] 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 32/53] 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 33/53] 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 34/53] 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 a370156720174627dc1ff72ed2d41c6871173d19 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 18 Jul 2014 15:39:21 -0700 Subject: [PATCH 35/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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/53] 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 be028c2b01100acc8b5460d42b188c19b3a7f28c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 22 Jul 2014 14:13:51 -0700 Subject: [PATCH 44/53] Fixes for local lights. --- examples/avatarLocalLight.js | 89 +++++-------------- interface/resources/shaders/model.frag | 29 +++--- .../shaders/model_cascaded_shadow_map.frag | 13 ++- .../model_cascaded_shadow_normal_map.frag | 15 +++- ...l_cascaded_shadow_normal_specular_map.frag | 13 ++- .../model_cascaded_shadow_specular_map.frag | 13 ++- .../resources/shaders/model_normal_map.frag | 20 +++-- .../shaders/model_normal_specular_map.frag | 13 ++- .../resources/shaders/model_shadow_map.frag | 13 ++- .../shaders/model_shadow_normal_map.frag | 13 ++- .../model_shadow_normal_specular_map.frag | 13 ++- .../shaders/model_shadow_specular_map.frag | 13 ++- .../resources/shaders/model_specular_map.frag | 27 +++--- interface/src/Application.cpp | 3 + interface/src/avatar/Avatar.cpp | 16 +--- interface/src/avatar/AvatarManager.cpp | 61 +++---------- interface/src/avatar/AvatarManager.h | 13 +-- interface/src/renderer/Model.cpp | 52 +++++++---- interface/src/renderer/Model.h | 24 +++-- 19 files changed, 225 insertions(+), 228 deletions(-) diff --git a/examples/avatarLocalLight.js b/examples/avatarLocalLight.js index 5768388b45..40f1b9a8ca 100644 --- a/examples/avatarLocalLight.js +++ b/examples/avatarLocalLight.js @@ -10,14 +10,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var localLightDirections = [ {x: 1.0, y:1.0, z: 0.0}, {x: 0.0, y:1.0, z: 1.0} ]; -var localLightColors = [ {x: 0.0, y:1.0, z: 0.0}, {x: 1.0, y:0.0, z: 0.0} ]; +var localLightDirections = [ {x: 1.0, y:0.0, z: 0.0}, {x: 0.0, y:0.0, z: 1.0} ]; +var localLightColors = [ {x: 1.0, y:1.0, z: 1.0}, {x: 1.0, y:1.0, z: 1.0} ]; var currentSelection = 0; -var currentNumLights = 1; +var currentNumLights = 2; var maxNumLights = 2; var currentNumAvatars = 0; var changeDelta = 0.1; +var lightsDirty = true; function keyPressEvent(event) { @@ -45,7 +46,7 @@ function keyPressEvent(event) { localLightColors[currentSelection].x = 0.0; } - setAllLightColors(); + lightsDirty = true; print("CHANGE RED light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); } else if (event.text == "6" ) { @@ -54,7 +55,7 @@ function keyPressEvent(event) { localLightColors[currentSelection].y = 0.0; } - setAllLightColors(); + lightsDirty = true; print("CHANGE GREEN light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); } else if (event.text == "7" ) { @@ -63,7 +64,7 @@ function keyPressEvent(event) { localLightColors[currentSelection].z = 0.0; } - setAllLightColors(); + lightsDirty = true; print("CHANGE BLUE light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); } else if (event.text == "8" ) { @@ -72,7 +73,7 @@ function keyPressEvent(event) { localLightDirections[currentSelection].x = -1.0; } - setAllLightDirections(); + lightsDirty = true; print("PLUS X light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } else if (event.text == "9" ) { @@ -81,7 +82,7 @@ function keyPressEvent(event) { localLightDirections[currentSelection].x = 1.0; } - setAllLightDirections(); + lightsDirty = true; print("MINUS X light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } else if (event.text == "0" ) { @@ -90,7 +91,7 @@ function keyPressEvent(event) { localLightDirections[currentSelection].y = -1.0; } - setAllLightDirections(); + lightsDirty = true; print("PLUS Y light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } else if (event.text == "-" ) { @@ -99,21 +100,13 @@ function keyPressEvent(event) { localLightDirections[currentSelection].y = 1.0; } - setAllLightDirections(); + lightsDirty = true; print("MINUS Y light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } else if (event.text == "," ) { if (currentNumLights + 1 <= maxNumLights) { ++currentNumLights; - - for (var i = 0; i < currentNumAvatars; i++) { - AvatarManager.addAvatarLocalLight(i); - - for (var j = 0; j < currentNumLights; j++) { - AvatarManager.setAvatarLightColor(localLightColors[j], j, i); - AvatarManager.setAvatarLightDirection(localLightDirections[j], j, i); - } - } + lightsDirty = true; } print("ADD LIGHT, number of lights " + currentNumLights); @@ -121,16 +114,7 @@ function keyPressEvent(event) { else if (event.text == "." ) { if (currentNumLights - 1 >= 0 ) { --currentNumLights; - - for (var i = 0; i < currentNumAvatars; i++) { - AvatarManager.removeAvatarLocalLight(i); - - for (var j = 0; j < currentNumLights; j++) { - AvatarManager.setAvatarLightColor(localLightColors[j], j, i); - AvatarManager.setAvatarLightDirection(localLightDirections[j], j, i); - } - } - + lightsDirty = true; } print("REMOVE LIGHT, number of lights " + currentNumLights); @@ -139,45 +123,14 @@ function keyPressEvent(event) { function updateLocalLights() { - // new avatars, so add lights - var numAvatars = AvatarManager.getNumAvatars(); - if (numAvatars != currentNumAvatars) { - - for (var i = 0; i < numAvatars; i++) { - var numLights = AvatarManager.getNumLightsInAvatar(i); - - // check if new avatar has lights - if (numLights <= 0) { - AvatarManager.addAvatarLocalLight(i); - - // set color and direction for new avatar - for (var j = 0; j < maxNumLights; j++) { - AvatarManager.setAvatarLightColor(localLightColors[j], j, i); - AvatarManager.setAvatarLightDirection(localLightDirections[j], j, i); - } - } - } - - currentNumAvatars = numAvatars; - } -} - -function setAllLightColors() -{ - for (var i = 0; i < currentNumAvatars; i++) { - for (var j = 0; j < maxNumLights; j++) { - AvatarManager.setAvatarLightColor(localLightColors[j], j, i); - } - } -} - -function setAllLightDirections() -{ - for (var i = 0; i < currentNumAvatars; i++) { - for (var j = 0; j < maxNumLights; j++) { - AvatarManager.setAvatarLightDirection(localLightDirections[j], j, i); - } - } + if (lightsDirty) { + var localLights = []; + for (var i = 0; i < currentNumLights; i++) { + localLights.push({ direction: localLightDirections[i], color: localLightColors[i] }); + } + AvatarManager.setLocalLights(localLights); + lightsDirty = false; + } } // main diff --git a/interface/resources/shaders/model.frag b/interface/resources/shaders/model.frag index 468a892686..27f6510477 100644 --- a/interface/resources/shaders/model.frag +++ b/interface/resources/shaders/model.frag @@ -11,16 +11,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; // the diffuse texture uniform sampler2D diffuseMap; -// local lights -const int MAX_LOCAL_LIGHTS = 2; // 2 lights for now, will probably need more later on -uniform int numLocalLights; -uniform vec3 localLightDirections[MAX_LOCAL_LIGHTS]; -uniform vec3 localLightColors[MAX_LOCAL_LIGHTS]; - // the interpolated position varying vec4 position; @@ -28,23 +24,18 @@ varying vec4 position; varying vec4 normal; void main(void) { - // compute the base color based on OpenGL lighting model + // add up the local lights vec4 normalizedNormal = normalize(normal); - float diffuse = dot(normalizedNormal, gl_LightSource[0].position); - float facingLight = step(0.0, diffuse); - - // the local light that is always present - vec4 totalLocalLight = vec4(0.0, 0.0, 0.0, 1.0); - for (int i = 0; i < numLocalLights; i++) { - float localDiffuse = dot(normalizedNormal, vec4(localLightDirections[i], 1.0)); - float localLight = step(0.0, localDiffuse); - float localLightVal = localDiffuse * localLight; - - totalLocalLight += (localLightVal * vec4( localLightColors[i], 0.0)); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); } + // compute the base color based on OpenGL lighting model + float diffuse = dot(normalizedNormal, gl_LightSource[0].position); + float facingLight = step(0.0, diffuse); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + totalLocalLight); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))), diff --git a/interface/resources/shaders/model_cascaded_shadow_map.frag b/interface/resources/shaders/model_cascaded_shadow_map.frag index 720c43b656..35026cb3b2 100644 --- a/interface/resources/shaders/model_cascaded_shadow_map.frag +++ b/interface/resources/shaders/model_cascaded_shadow_map.frag @@ -11,6 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; + // the diffuse texture uniform sampler2D diffuseMap; @@ -34,9 +37,15 @@ void main(void) { int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0))); vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position), dot(gl_EyePlaneR[shadowIndex], position)); + + // add up the local lights + vec4 normalizedNormal = normalize(normal); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); + } // compute the base color based on OpenGL lighting model - vec4 normalizedNormal = normalize(normal); float diffuse = dot(normalizedNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse) * 0.25 * (shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r + @@ -44,7 +53,7 @@ void main(void) { shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r + shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))), diff --git a/interface/resources/shaders/model_cascaded_shadow_normal_map.frag b/interface/resources/shaders/model_cascaded_shadow_normal_map.frag index 5758333392..0e474c173a 100644 --- a/interface/resources/shaders/model_cascaded_shadow_normal_map.frag +++ b/interface/resources/shaders/model_cascaded_shadow_normal_map.frag @@ -11,6 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; + // the diffuse texture uniform sampler2D diffuseMap; @@ -46,10 +49,16 @@ void main(void) { vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], interpolatedPosition), dot(gl_EyePlaneT[shadowIndex], interpolatedPosition), dot(gl_EyePlaneR[shadowIndex], interpolatedPosition)); - - // compute the base color based on OpenGL lighting model + + // add up the local lights vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + } + + // compute the base color based on OpenGL lighting model float diffuse = dot(viewNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse) * 0.25 * (shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r + @@ -57,7 +66,7 @@ void main(void) { shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r + shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - diff --git a/interface/resources/shaders/model_cascaded_shadow_normal_specular_map.frag b/interface/resources/shaders/model_cascaded_shadow_normal_specular_map.frag index 2b949710f3..33c025925e 100644 --- a/interface/resources/shaders/model_cascaded_shadow_normal_specular_map.frag +++ b/interface/resources/shaders/model_cascaded_shadow_normal_specular_map.frag @@ -11,6 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; + // the diffuse texture uniform sampler2D diffuseMap; @@ -50,9 +53,15 @@ void main(void) { dot(gl_EyePlaneT[shadowIndex], interpolatedPosition), dot(gl_EyePlaneR[shadowIndex], interpolatedPosition)); - // compute the base color based on OpenGL lighting model + // add up the local lights vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + } + + // compute the base color based on OpenGL lighting model float diffuse = dot(viewNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse) * 0.25 * (shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r + @@ -60,7 +69,7 @@ void main(void) { shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r + shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - diff --git a/interface/resources/shaders/model_cascaded_shadow_specular_map.frag b/interface/resources/shaders/model_cascaded_shadow_specular_map.frag index ba8ba6b85b..9190654431 100644 --- a/interface/resources/shaders/model_cascaded_shadow_specular_map.frag +++ b/interface/resources/shaders/model_cascaded_shadow_specular_map.frag @@ -11,6 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; + // the diffuse texture uniform sampler2D diffuseMap; @@ -38,8 +41,14 @@ void main(void) { vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position), dot(gl_EyePlaneR[shadowIndex], position)); - // compute the base color based on OpenGL lighting model + // add up the local lights vec4 normalizedNormal = normalize(normal); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); + } + + // compute the base color based on OpenGL lighting model float diffuse = dot(normalizedNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse) * 0.25 * (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + @@ -47,7 +56,7 @@ void main(void) { shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))), diff --git a/interface/resources/shaders/model_normal_map.frag b/interface/resources/shaders/model_normal_map.frag index ca0201f6ab..acd049dc47 100644 --- a/interface/resources/shaders/model_normal_map.frag +++ b/interface/resources/shaders/model_normal_map.frag @@ -11,6 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; + // the diffuse texture uniform sampler2D diffuseMap; @@ -32,19 +35,20 @@ void main(void) { vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0); - // compute the base color based on OpenGL lighting model + // add up the local lights vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + } + + // compute the base color based on OpenGL lighting model float diffuse = dot(viewNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse); - float localDiffuse = dot(viewNormal, gl_LightSource[1].position); - float localLight = step(0.0, localDiffuse); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + gl_FrontLightProduct[1].diffuse * (localDiffuse * localLight)); - - - - + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); + // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(vec3(interpolatedPosition), 0.0))), viewNormal)); diff --git a/interface/resources/shaders/model_normal_specular_map.frag b/interface/resources/shaders/model_normal_specular_map.frag index 357677d82a..fa997bff7f 100644 --- a/interface/resources/shaders/model_normal_specular_map.frag +++ b/interface/resources/shaders/model_normal_specular_map.frag @@ -11,6 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; + // the diffuse texture uniform sampler2D diffuseMap; @@ -35,13 +38,19 @@ void main(void) { vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0); - // compute the base color based on OpenGL lighting model + // add up the local lights vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + } + + // compute the base color based on OpenGL lighting model float diffuse = dot(viewNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - diff --git a/interface/resources/shaders/model_shadow_map.frag b/interface/resources/shaders/model_shadow_map.frag index aa1df03b95..c2bf95fd27 100644 --- a/interface/resources/shaders/model_shadow_map.frag +++ b/interface/resources/shaders/model_shadow_map.frag @@ -11,6 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; + // the diffuse texture uniform sampler2D diffuseMap; @@ -27,8 +30,14 @@ varying vec4 position; varying vec4 normal; void main(void) { - // compute the base color based on OpenGL lighting model + // add up the local lights vec4 normalizedNormal = normalize(normal); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); + } + + // compute the base color based on OpenGL lighting model float diffuse = dot(normalizedNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse) * 0.25 * (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + @@ -36,7 +45,7 @@ void main(void) { shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))), diff --git a/interface/resources/shaders/model_shadow_normal_map.frag b/interface/resources/shaders/model_shadow_normal_map.frag index 3461c1b5f3..058c3783be 100644 --- a/interface/resources/shaders/model_shadow_normal_map.frag +++ b/interface/resources/shaders/model_shadow_normal_map.frag @@ -11,6 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; + // the diffuse texture uniform sampler2D diffuseMap; @@ -38,9 +41,15 @@ void main(void) { vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0); - // compute the base color based on OpenGL lighting model + // add up the local lights vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + } + + // compute the base color based on OpenGL lighting model float diffuse = dot(viewNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse) * 0.25 * (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + @@ -48,7 +57,7 @@ void main(void) { shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - diff --git a/interface/resources/shaders/model_shadow_normal_specular_map.frag b/interface/resources/shaders/model_shadow_normal_specular_map.frag index 273d197fca..5b8d7c87a4 100644 --- a/interface/resources/shaders/model_shadow_normal_specular_map.frag +++ b/interface/resources/shaders/model_shadow_normal_specular_map.frag @@ -11,6 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; + // the diffuse texture uniform sampler2D diffuseMap; @@ -41,9 +44,15 @@ void main(void) { vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0); - // compute the base color based on OpenGL lighting model + // add up the local lights vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + } + + // compute the base color based on OpenGL lighting model float diffuse = dot(viewNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse) * 0.25 * (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + @@ -51,7 +60,7 @@ void main(void) { shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - diff --git a/interface/resources/shaders/model_shadow_specular_map.frag b/interface/resources/shaders/model_shadow_specular_map.frag index 77cff1e04e..9f413f63a4 100644 --- a/interface/resources/shaders/model_shadow_specular_map.frag +++ b/interface/resources/shaders/model_shadow_specular_map.frag @@ -11,6 +11,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; + // the diffuse texture uniform sampler2D diffuseMap; @@ -30,8 +33,14 @@ varying vec4 position; varying vec4 normal; void main(void) { - // compute the base color based on OpenGL lighting model + // add up the local lights vec4 normalizedNormal = normalize(normal); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); + } + + // compute the base color based on OpenGL lighting model float diffuse = dot(normalizedNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse) * 0.25 * (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + @@ -39,7 +48,7 @@ void main(void) { shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))), diff --git a/interface/resources/shaders/model_specular_map.frag b/interface/resources/shaders/model_specular_map.frag index 329da65e9e..54790a36fb 100644 --- a/interface/resources/shaders/model_specular_map.frag +++ b/interface/resources/shaders/model_specular_map.frag @@ -10,11 +10,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -const int MAX_LOCAL_LIGHTS = 2; -uniform int numLocalLights; -uniform vec3 localLightDirections[MAX_LOCAL_LIGHTS]; -uniform vec3 localLightColors[MAX_LOCAL_LIGHTS]; +// the maximum number of local lights to apply +const int MAX_LOCAL_LIGHTS = 2; // the diffuse texture uniform sampler2D diffuseMap; @@ -29,23 +27,18 @@ varying vec4 position; varying vec4 normal; void main(void) { - // compute the base color based on OpenGL lighting model + // add up the local lights vec4 normalizedNormal = normalize(normal); - float diffuse = dot(normalizedNormal, gl_LightSource[0].position); - float facingLight = step(0.0, diffuse); - - // the local light that is always present - vec4 totalLocalLight = vec4(0.0, 0.0, 0.0, 1.0); - for (int i = 0; i < numLocalLights; i++) { - float localDiffuse = dot(normalizedNormal, vec4(localLightDirections[i], 1.0)); - float localLight = step(0.0, localDiffuse); - float localLightVal = localDiffuse * localLight; - - totalLocalLight += (localLightVal * vec4( localLightColors[i], 0.0)); + vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { + localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); } + // compute the base color based on OpenGL lighting model + float diffuse = dot(normalizedNormal, gl_LightSource[0].position); + float facingLight = step(0.0, diffuse); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + totalLocalLight); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + localLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))), diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index aad9c4de58..1d0bf8d6a3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3628,6 +3628,9 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine->getModelsScriptingInterface()->setPacketSender(&_modelEditSender); scriptEngine->getModelsScriptingInterface()->setModelTree(_models.getTree()); + // model has some custom types + Model::registerMetaTypes(scriptEngine->getEngine()); + // hook our avatar object into this script engine scriptEngine->setAvatarData(_myAvatar, "MyAvatar"); // leave it as a MyAvatar class to expose thrust features diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b5590dec09..cddd85964c 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -245,19 +245,9 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { // local lights directions and colors - getSkeletonModel().setNumLocalLights(_numLocalLights); - getHead()->getFaceModel().setNumLocalLights(_numLocalLights); - for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { - glm::vec3 normalized = glm::normalize(_localLightDirections[i]); - - // body - getSkeletonModel().setLocalLightColor(_localLightColors[i], i); - getSkeletonModel().setLocalLightDirection(normalized, i); - - // head - getHead()->getFaceModel().setLocalLightColor(_localLightColors[i], i); - getHead()->getFaceModel().setLocalLightDirection(_localLightDirections[i], i); - } + const QVector& localLights = Application::getInstance()->getAvatarManager().getLocalLights(); + _skeletonModel.setLocalLights(localLights); + getHead()->getFaceModel().setLocalLights(localLights); // render body if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c172df5de6..0db12276ad 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -159,58 +159,21 @@ void AvatarManager::clearOtherAvatars() { _myAvatar->clearLookAtTargetAvatar(); } -Avatar* AvatarManager::getAvatarFromIndex(int avatarIndex) { - Avatar* avatar = NULL; - int numAvatars = _avatarHash.count(); - if (avatarIndex < numAvatars) { - QUuid key = (_avatarHash.keys())[avatarIndex]; - - const AvatarSharedPointer& avatarPointer = _avatarHash.value(key); - avatar = static_cast(avatarPointer.data()); +void AvatarManager::setLocalLights(const QVector& localLights) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setLocalLights", Q_ARG(const QVector&, localLights)); + return; } - - return avatar; + _localLights = localLights; } -void AvatarManager::addAvatarLocalLight(int avatarIndex) { - Avatar* avatar = getAvatarFromIndex(avatarIndex); - if (avatar) { - avatar->addLocalLight(); +QVector AvatarManager::getLocalLights() const { + if (QThread::currentThread() != thread()) { + QVector result; + QMetaObject::invokeMethod(const_cast(this), "getLocalLights", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QVector, result)); + return result; } + return _localLights; } -void AvatarManager::removeAvatarLocalLight(int avatarIndex) { - Avatar* avatar = getAvatarFromIndex(avatarIndex); - if (avatar) { - avatar->removeLocalLight(); - } -} - -void AvatarManager::setAvatarLightDirection(const glm::vec3& direction, int lightIndex, int avatarIndex) { - Avatar* avatar = getAvatarFromIndex(avatarIndex); - if (avatar) { - avatar->setLocalLightDirection(direction, lightIndex); - } -} - -void AvatarManager::setAvatarLightColor(const glm::vec3& color, int lightIndex, int avatarIndex) { - Avatar* avatar = getAvatarFromIndex(avatarIndex); - if (avatar) { - avatar->setLocalLightColor(color, lightIndex); - } -} - -int AvatarManager::getNumLightsInAvatar(int avatarIndex) { - int numLights = 0; - - Avatar* avatar = getAvatarFromIndex(avatarIndex); - if (avatar) { - numLights = avatar->getNumLocalLights(); - } - - return numLights; -} - -int AvatarManager::getNumAvatars() { - return _avatarHash.count(); -} diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index e6f058c0ab..ccb7459217 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -37,13 +37,8 @@ public: void clearOtherAvatars(); -public slots: - void setAvatarLightColor(const glm::vec3& color, int lightIndex, int avatarIndex); - void setAvatarLightDirection(const glm::vec3& direction, int lightIndex, int avatarIndex); - void removeAvatarLocalLight(int avatarIndex); - void addAvatarLocalLight(int avatarIndex); - int getNumLightsInAvatar(int avatarIndex); - int getNumAvatars(); + Q_INVOKABLE void setLocalLights(const QVector& localLights); + Q_INVOKABLE QVector getLocalLights() const; private: AvatarManager(const AvatarManager& other); @@ -53,13 +48,13 @@ private: AvatarSharedPointer newSharedAvatar(); - Avatar* getAvatarFromIndex(int avatarIndex); - // virtual override AvatarHash::iterator erase(const AvatarHash::iterator& iterator); QVector _avatarFades; QSharedPointer _myAvatar; + + QVector _localLights; }; #endif // hifi_AvatarManager_h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index d65cb014c9..c5c83aa01d 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -31,6 +33,23 @@ static int modelPointerTypeId = qRegisterMetaType >(); static int weakNetworkGeometryPointerTypeId = qRegisterMetaType >(); static int vec3VectorTypeId = qRegisterMetaType >(); +static QScriptValue localLightToScriptValue(QScriptEngine* engine, const Model::LocalLight& light) { + QScriptValue object = engine->newObject(); + object.setProperty("direction", vec3toScriptValue(engine, light.direction)); + object.setProperty("color", vec3toScriptValue(engine, light.color)); + return object; +} + +static void localLightFromScriptValue(const QScriptValue& value, Model::LocalLight& light) { + vec3FromScriptValue(value.property("direction"), light.direction); + vec3FromScriptValue(value.property("color"), light.color); +} + +void Model::registerMetaTypes(QScriptEngine* engine) { + qScriptRegisterMetaType(engine, localLightToScriptValue, localLightFromScriptValue); + qScriptRegisterSequenceMetaType >(engine); +} + Model::Model(QObject* parent) : QObject(parent), _scale(1.0f, 1.0f, 1.0f), @@ -609,6 +628,20 @@ bool Model::render(float alpha, RenderMode mode, bool receiveShadows) { glEnable(GL_CULL_FACE); if (mode == SHADOW_RENDER_MODE) { glCullFace(GL_FRONT); + + } else if (mode == DEFAULT_RENDER_MODE) { + // set up the local lights + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + glm::vec4 color; + GLenum lightName = GL_LIGHT0 + i + 1; + if (i < _localLights.size()) { + const LocalLight& light = _localLights.at(i); + color = glm::vec4(light.color, 1.0f); + glm::vec4 position = glm::vec4(_rotation * light.direction, 0.0f); + glLightfv(lightName, GL_POSITION, (GLfloat*)&position); + } + glLightfv(lightName, GL_DIFFUSE, (GLfloat*)&color); + } } } @@ -1493,11 +1526,6 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re if (cascadedShadows) { program->setUniform(skinLocations->shadowDistances, Application::getInstance()->getShadowDistances()); } - - // local light uniforms - skinProgram->setUniformValue("numLocalLights", _numLocalLights); - skinProgram->setUniformArray("localLightDirections", _localLightDirections, MAX_LOCAL_LIGHTS); - skinProgram->setUniformArray("localLightColors", _localLightColors, MAX_LOCAL_LIGHTS); } else { glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); program->bind(); @@ -1632,20 +1660,6 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re } } -void Model::setLocalLightDirection(const glm::vec3& direction, int lightIndex) { - assert(lightIndex >= 0 && lightIndex < MAX_LOCAL_LIGHTS); - _localLightDirections[lightIndex] = direction; -} - -void Model::setLocalLightColor(const glm::vec3& color, int lightIndex) { - assert(lightIndex >= 0 && lightIndex < MAX_LOCAL_LIGHTS); - _localLightColors[lightIndex] = color; -} - -void Model::setNumLocalLights(int numLocalLights) { - _numLocalLights = numLocalLights; -} - void AnimationHandle::setURL(const QUrl& url) { if (_url != url) { _animation = Application::getInstance()->getAnimationCache()->getAnimation(_url = url); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 078685345b..fe09710d0a 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -26,6 +26,8 @@ #include "ProgramObject.h" #include "TextureCache.h" +class QScriptEngine; + class AnimationHandle; class Shape; @@ -40,6 +42,9 @@ class Model : public QObject, public PhysicsEntity { public: + /// Registers the script types associated with models. + static void registerMetaTypes(QScriptEngine* engine); + Model(QObject* parent = NULL); virtual ~Model(); @@ -145,9 +150,14 @@ public: /// Sets blended vertices computed in a separate thread. void setBlendedVertices(const QVector& vertices, const QVector& normals); - void setLocalLightDirection(const glm::vec3& direction, int lightIndex); - void setLocalLightColor(const glm::vec3& color, int lightIndex); - void setNumLocalLights(int numLocalLights); + class LocalLight { + public: + glm::vec3 color; + glm::vec3 direction; + }; + + void setLocalLights(const QVector& localLights) { _localLights = localLights; } + const QVector& getLocalLights() const { return _localLights; } void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; } @@ -165,10 +175,8 @@ protected: bool _snappedToCenter; /// are we currently snapped to center bool _showTrueJointTransforms; - glm::vec3 _localLightDirections[MAX_LOCAL_LIGHTS]; - glm::vec3 _localLightColors[MAX_LOCAL_LIGHTS]; - int _numLocalLights; - + QVector _localLights; + QVector _jointStates; class MeshState { @@ -326,6 +334,8 @@ private: Q_DECLARE_METATYPE(QPointer) Q_DECLARE_METATYPE(QWeakPointer) Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(Model::LocalLight) +Q_DECLARE_METATYPE(QVector) /// Represents a handle to a model animation. class AnimationHandle : public QObject { From 7faf64c54ac5aebcf89cf4f4d6bfd2feb33ed9cb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 22 Jul 2014 14:24:25 -0700 Subject: [PATCH 45/53] Tab fixes. --- examples/avatarLocalLight.js | 150 +++++++++++++++++------------------ 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/examples/avatarLocalLight.js b/examples/avatarLocalLight.js index 40f1b9a8ca..040289af1f 100644 --- a/examples/avatarLocalLight.js +++ b/examples/avatarLocalLight.js @@ -25,110 +25,110 @@ function keyPressEvent(event) { var choice = parseInt(event.text); if (event.text == "1") { - currentSelection = 0; - print("light election = " + currentSelection); + currentSelection = 0; + print("light election = " + currentSelection); } else if (event.text == "2" ) { - currentSelection = 1; - print("light selection = " + currentSelection); + currentSelection = 1; + print("light selection = " + currentSelection); } else if (event.text == "3" ) { - currentSelection = 2; - print("light selection = " + currentSelection); + currentSelection = 2; + print("light selection = " + currentSelection); } else if (event.text == "4" ) { - currentSelection = 3; - print("light selection = " + currentSelection); + currentSelection = 3; + print("light selection = " + currentSelection); } else if (event.text == "5" ) { - localLightColors[currentSelection].x += changeDelta; - if ( localLightColors[currentSelection].x > 1.0) { - localLightColors[currentSelection].x = 0.0; - } - - lightsDirty = true; - print("CHANGE RED light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); + localLightColors[currentSelection].x += changeDelta; + if ( localLightColors[currentSelection].x > 1.0) { + localLightColors[currentSelection].x = 0.0; + } + + lightsDirty = true; + print("CHANGE RED light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); } else if (event.text == "6" ) { - localLightColors[currentSelection].y += changeDelta; - if ( localLightColors[currentSelection].y > 1.0) { - localLightColors[currentSelection].y = 0.0; - } - - lightsDirty = true; - print("CHANGE GREEN light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); + localLightColors[currentSelection].y += changeDelta; + if ( localLightColors[currentSelection].y > 1.0) { + localLightColors[currentSelection].y = 0.0; + } + + lightsDirty = true; + print("CHANGE GREEN light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); } else if (event.text == "7" ) { - localLightColors[currentSelection].z += changeDelta; - if ( localLightColors[currentSelection].z > 1.0) { - localLightColors[currentSelection].z = 0.0; - } - - lightsDirty = true; - print("CHANGE BLUE light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); + localLightColors[currentSelection].z += changeDelta; + if ( localLightColors[currentSelection].z > 1.0) { + localLightColors[currentSelection].z = 0.0; + } + + lightsDirty = true; + print("CHANGE BLUE light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); } else if (event.text == "8" ) { - localLightDirections[currentSelection].x += changeDelta; - if (localLightDirections[currentSelection].x > 1.0) { - localLightDirections[currentSelection].x = -1.0; - } - - lightsDirty = true; - print("PLUS X light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); + localLightDirections[currentSelection].x += changeDelta; + if (localLightDirections[currentSelection].x > 1.0) { + localLightDirections[currentSelection].x = -1.0; + } + + lightsDirty = true; + print("PLUS X light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } else if (event.text == "9" ) { - localLightDirections[currentSelection].x -= changeDelta; - if (localLightDirections[currentSelection].x < -1.0) { - localLightDirections[currentSelection].x = 1.0; - } - - lightsDirty = true; - print("MINUS X light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); + localLightDirections[currentSelection].x -= changeDelta; + if (localLightDirections[currentSelection].x < -1.0) { + localLightDirections[currentSelection].x = 1.0; + } + + lightsDirty = true; + print("MINUS X light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } else if (event.text == "0" ) { - localLightDirections[currentSelection].y += changeDelta; - if (localLightDirections[currentSelection].y > 1.0) { - localLightDirections[currentSelection].y = -1.0; - } - - lightsDirty = true; - print("PLUS Y light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); + localLightDirections[currentSelection].y += changeDelta; + if (localLightDirections[currentSelection].y > 1.0) { + localLightDirections[currentSelection].y = -1.0; + } + + lightsDirty = true; + print("PLUS Y light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } else if (event.text == "-" ) { - localLightDirections[currentSelection].y -= changeDelta; - if (localLightDirections[currentSelection].y < -1.0) { - localLightDirections[currentSelection].y = 1.0; - } - - lightsDirty = true; - print("MINUS Y light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); + localLightDirections[currentSelection].y -= changeDelta; + if (localLightDirections[currentSelection].y < -1.0) { + localLightDirections[currentSelection].y = 1.0; + } + + lightsDirty = true; + print("MINUS Y light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } else if (event.text == "," ) { - if (currentNumLights + 1 <= maxNumLights) { - ++currentNumLights; - lightsDirty = true; - } - - print("ADD LIGHT, number of lights " + currentNumLights); + if (currentNumLights + 1 <= maxNumLights) { + ++currentNumLights; + lightsDirty = true; + } + + print("ADD LIGHT, number of lights " + currentNumLights); } else if (event.text == "." ) { - if (currentNumLights - 1 >= 0 ) { - --currentNumLights; - lightsDirty = true; - } - - print("REMOVE LIGHT, number of lights " + currentNumLights); + if (currentNumLights - 1 >= 0 ) { + --currentNumLights; + lightsDirty = true; + } + + print("REMOVE LIGHT, number of lights " + currentNumLights); } } function updateLocalLights() { - if (lightsDirty) { - var localLights = []; - for (var i = 0; i < currentNumLights; i++) { - localLights.push({ direction: localLightDirections[i], color: localLightColors[i] }); - } - AvatarManager.setLocalLights(localLights); + if (lightsDirty) { + var localLights = []; + for (var i = 0; i < currentNumLights; i++) { + localLights.push({ direction: localLightDirections[i], color: localLightColors[i] }); + } + AvatarManager.setLocalLights(localLights); lightsDirty = false; } } From f325500d1489dcf6b9499e56d7efe7ab04c93707 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 22 Jul 2014 14:38:41 -0700 Subject: [PATCH 46/53] 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 47/53] 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())); From 5c955ca6568c2b979eea45d49a7d12da8b4949f9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 22 Jul 2014 14:48:51 -0700 Subject: [PATCH 48/53] fix faceshift definition --- interface/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 5461bc2556..e1d93394c7 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -229,7 +229,7 @@ endif() # and with Faceshift for depth camera face tracking if (FACESHIFT_FOUND AND NOT DISABLE_FACESHIFT) - add_definitions(-DAHAVE_FACESHIFT) + add_definitions(-DHAVE_FACESHIFT) include_directories(SYSTEM "${FACESHIFT_INCLUDE_DIRS}") target_link_libraries(${TARGET_NAME} ${FACESHIFT_LIBRARIES}) endif() From d4904f976ae23eb698ac834d2766f033b0b7af04 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 22 Jul 2014 15:50:08 -0700 Subject: [PATCH 49/53] Missed the local light stats. --- interface/src/avatar/Avatar.cpp | 34 --------------------------------- interface/src/avatar/Avatar.h | 14 +------------- interface/src/ui/Stats.cpp | 8 ++++---- 3 files changed, 5 insertions(+), 51 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index cddd85964c..ef0fae1c13 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -60,7 +60,6 @@ Avatar::Avatar() : _mouseRayDirection(0.0f, 0.0f, 0.0f), _moving(false), _collisionGroups(0), - _numLocalLights(0), _initialized(false), _shouldRenderBillboard(true) { @@ -916,36 +915,3 @@ void Avatar::setShowDisplayName(bool showDisplayName) { } -void Avatar::setLocalLightDirection(const glm::vec3& direction, int lightIndex) { - _localLightDirections[lightIndex] = direction; -} - -void Avatar::setLocalLightColor(const glm::vec3& color, int lightIndex) { - _localLightColors[lightIndex] = color; -} - -void Avatar::addLocalLight() { - if (_numLocalLights + 1 <= MAX_LOCAL_LIGHTS) { - ++_numLocalLights; - } -} - -void Avatar::removeLocalLight() { - if (_numLocalLights - 1 >= 0) { - --_numLocalLights; - } -} - -int Avatar::getNumLocalLights() { - return _numLocalLights; -} - -glm::vec3 Avatar::getLocalLightDirection(int lightIndex) { - return _localLightDirections[lightIndex]; -} - -glm::vec3 Avatar::getLocalLightColor(int lightIndex) { - return _localLightColors[lightIndex]; -} - - diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index d2f3ee6ab4..02bc2f227a 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -159,14 +159,7 @@ public: public slots: void updateCollisionGroups(); - void setLocalLightDirection(const glm::vec3& direction, int lightIndex); - void setLocalLightColor(const glm::vec3& color, int lightIndex); - void addLocalLight(); - void removeLocalLight(); - int getNumLocalLights(); - glm::vec3 getLocalLightDirection(int lightIndex); - glm::vec3 getLocalLightColor(int lightIndex); - + signals: void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); @@ -189,11 +182,6 @@ protected: bool _moving; ///< set when position is changing quint32 _collisionGroups; - - // always-present local lighting for the avatar - glm::vec3 _localLightDirections[MAX_LOCAL_LIGHTS]; - glm::vec3 _localLightColors[MAX_LOCAL_LIGHTS]; - int _numLocalLights; // protected methods... glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 412de6e7b0..f82bc7ba17 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -828,19 +828,19 @@ void Stats::display( } // draw local light stats - int numLocalLights = myAvatar->getNumLocalLights(); + QVector localLights = Application::getInstance()->getAvatarManager().getLocalLights(); verticalOffset = 400; horizontalOffset = 20; char buffer[128]; - for (int i = 0; i < numLocalLights; i++) { - glm::vec3 lightDirection = myAvatar->getLocalLightDirection(i); + for (int i = 0; i < localLights.size(); i++) { + glm::vec3 lightDirection = localLights.at(i).direction; snprintf(buffer, sizeof(buffer), "Light %d direction (%.2f, %.2f, %.2f)", i, lightDirection.x, lightDirection.y, lightDirection.z); drawText(horizontalOffset, verticalOffset, scale, rotation, font, buffer, color); verticalOffset += STATS_PELS_PER_LINE; - glm::vec3 lightColor = myAvatar->getLocalLightColor(i); + glm::vec3 lightColor = localLights.at(i).color; snprintf(buffer, sizeof(buffer), "Light %d color (%.2f, %.2f, %.2f)", i, lightColor.x, lightColor.y, lightColor.z); drawText(horizontalOffset, verticalOffset, scale, rotation, font, buffer, color); From 56efa46812e95566a702b4a7590da87f45a3c34a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 22 Jul 2014 15:56:22 -0700 Subject: [PATCH 50/53] repair for Visage find module base includes --- cmake/modules/FindVisage.cmake | 8 ++++---- interface/src/devices/Faceshift.h | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cmake/modules/FindVisage.cmake b/cmake/modules/FindVisage.cmake index 4151d3bec0..7e1d6e60b6 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 PATH_SUFFIXES include HINTS ${VISAGE_SEARCH_DIRS}) +find_path(VISAGE_BASE_INCLUDE_DIR VisageTracker2.h PATH_SUFFIXES include HINTS ${VISAGE_SEARCH_DIRS}) if (APPLE) find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h HINTS /usr/include/libxml2 ${VISAGE_SEARCH_DIRS}) @@ -43,15 +43,15 @@ 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_BASE_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_INCLUDE_DIRS "${VISAGE_XML_INCLUDE_DIR}" "${VISAGE_OPENCV_INCLUDE_DIR}" "${VISAGE_OPENCV2_INCLUDE_DIR}" "${VISAGE_BASE_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_BASE_INCLUDE_DIR VISAGE_XML_INCLUDE_DIR VISAGE_OPENCV_INCLUDE_DIR VISAGE_OPENCV2_INCLUDE_DIR VISAGE_CORE_LIBRARY VISAGE_VISION_LIBRARY VISAGE_OPENCV_LIBRARY ) diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h index c14bbed600..25abd8c0eb 100644 --- a/interface/src/devices/Faceshift.h +++ b/interface/src/devices/Faceshift.h @@ -16,9 +16,7 @@ #include #ifdef HAVE_FACESHIFT - #include - #endif #include "FaceTracker.h" From 4f52098da1c4707d420881e2c18046465ca9e8a3 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 22 Jul 2014 20:23:23 -0400 Subject: [PATCH 51/53] tuning tweaks to world and avatar lights --- examples/avatarLocalLight.js | 2 +- interface/src/Application.cpp | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/examples/avatarLocalLight.js b/examples/avatarLocalLight.js index 040289af1f..b1f31888d8 100644 --- a/examples/avatarLocalLight.js +++ b/examples/avatarLocalLight.js @@ -11,7 +11,7 @@ // var localLightDirections = [ {x: 1.0, y:0.0, z: 0.0}, {x: 0.0, y:0.0, z: 1.0} ]; -var localLightColors = [ {x: 1.0, y:1.0, z: 1.0}, {x: 1.0, y:1.0, z: 1.0} ]; +var localLightColors = [ {x: 0.4, y:0.335, z: 0.266}, {x: 0.4, y:0.335, z: 0.266} ]; var currentSelection = 0; var currentNumLights = 2; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1d0bf8d6a3..8b00bface7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2600,7 +2600,9 @@ void Application::updateShadowMap() { glViewport(0, 0, _glWidget->width(), _glWidget->height()); } -const GLfloat WHITE_SPECULAR_COLOR[] = { 1.0f, 1.0f, 1.0f, 1.0f }; +const GLfloat WORLD_AMBIENT_COLOR[] = { 0.525f, 0.525f, 0.6f }; +const GLfloat WORLD_DIFFUSE_COLOR[] = { 0.6f, 0.525f, 0.525f }; +const GLfloat WORLD_SPECULAR_COLOR[] = { 0.94f, 0.94f, 0.737f, 1.0f }; const GLfloat NO_SPECULAR_COLOR[] = { 0.0f, 0.0f, 0.0f, 1.0f }; void Application::setupWorldLight() { @@ -2612,13 +2614,10 @@ void Application::setupWorldLight() { glm::vec3 sunDirection = getSunDirection(); GLfloat light_position0[] = { sunDirection.x, sunDirection.y, sunDirection.z, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, light_position0); - GLfloat ambient_color[] = { 0.7f, 0.7f, 0.8f }; - glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color); - GLfloat diffuse_color[] = { 0.8f, 0.7f, 0.7f }; - glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color); - - glLightfv(GL_LIGHT0, GL_SPECULAR, WHITE_SPECULAR_COLOR); - glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR); + glLightfv(GL_LIGHT0, GL_AMBIENT, WORLD_AMBIENT_COLOR); + glLightfv(GL_LIGHT0, GL_DIFFUSE, WORLD_DIFFUSE_COLOR); + glLightfv(GL_LIGHT0, GL_SPECULAR, WORLD_SPECULAR_COLOR); + glMaterialfv(GL_FRONT, GL_SPECULAR, WORLD_SPECULAR_COLOR); glMateriali(GL_FRONT, GL_SHININESS, 96); } @@ -2796,7 +2795,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } // restore default, white specular - glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR); + glMaterialfv(GL_FRONT, GL_SPECULAR, WORLD_SPECULAR_COLOR); _nodeBoundsDisplay.draw(); From 4f875248216d369ab83128b0f79d408b1796dcc5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 22 Jul 2014 18:03:13 -0700 Subject: [PATCH 52/53] Switched back to using uniforms for light parameters, as using the fixed function state killed my frame rate. Also cleaned up some of the location tracking. --- interface/resources/shaders/model.frag | 10 +- .../shaders/model_cascaded_shadow_map.frag | 10 +- .../model_cascaded_shadow_normal_map.frag | 10 +- ...l_cascaded_shadow_normal_specular_map.frag | 10 +- .../model_cascaded_shadow_specular_map.frag | 10 +- .../resources/shaders/model_normal_map.frag | 10 +- .../shaders/model_normal_specular_map.frag | 10 +- .../resources/shaders/model_shadow_map.frag | 10 +- .../shaders/model_shadow_normal_map.frag | 10 +- .../model_shadow_normal_specular_map.frag | 10 +- .../shaders/model_shadow_specular_map.frag | 10 +- .../resources/shaders/model_specular_map.frag | 10 +- interface/src/Application.h | 2 + interface/src/renderer/Model.cpp | 185 +++++++----------- interface/src/renderer/Model.h | 33 +++- 15 files changed, 199 insertions(+), 141 deletions(-) diff --git a/interface/resources/shaders/model.frag b/interface/resources/shaders/model.frag index 27f6510477..d8e1efab14 100644 --- a/interface/resources/shaders/model.frag +++ b/interface/resources/shaders/model.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -27,8 +33,8 @@ void main(void) { // add up the local lights vec4 normalizedNormal = normalize(normal); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(normalizedNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/resources/shaders/model_cascaded_shadow_map.frag b/interface/resources/shaders/model_cascaded_shadow_map.frag index 35026cb3b2..63500e7e2a 100644 --- a/interface/resources/shaders/model_cascaded_shadow_map.frag +++ b/interface/resources/shaders/model_cascaded_shadow_map.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -41,8 +47,8 @@ void main(void) { // add up the local lights vec4 normalizedNormal = normalize(normal); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(normalizedNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/resources/shaders/model_cascaded_shadow_normal_map.frag b/interface/resources/shaders/model_cascaded_shadow_normal_map.frag index 0e474c173a..1fef1ef82a 100644 --- a/interface/resources/shaders/model_cascaded_shadow_normal_map.frag +++ b/interface/resources/shaders/model_cascaded_shadow_normal_map.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -54,8 +60,8 @@ void main(void) { vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(viewNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/resources/shaders/model_cascaded_shadow_normal_specular_map.frag b/interface/resources/shaders/model_cascaded_shadow_normal_specular_map.frag index 33c025925e..f0751bcca9 100644 --- a/interface/resources/shaders/model_cascaded_shadow_normal_specular_map.frag +++ b/interface/resources/shaders/model_cascaded_shadow_normal_specular_map.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -57,8 +63,8 @@ void main(void) { vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(viewNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/resources/shaders/model_cascaded_shadow_specular_map.frag b/interface/resources/shaders/model_cascaded_shadow_specular_map.frag index 9190654431..b2881e6c13 100644 --- a/interface/resources/shaders/model_cascaded_shadow_specular_map.frag +++ b/interface/resources/shaders/model_cascaded_shadow_specular_map.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -44,8 +50,8 @@ void main(void) { // add up the local lights vec4 normalizedNormal = normalize(normal); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(normalizedNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/resources/shaders/model_normal_map.frag b/interface/resources/shaders/model_normal_map.frag index acd049dc47..73d2fc0ef6 100644 --- a/interface/resources/shaders/model_normal_map.frag +++ b/interface/resources/shaders/model_normal_map.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -39,8 +45,8 @@ void main(void) { vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(viewNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/resources/shaders/model_normal_specular_map.frag b/interface/resources/shaders/model_normal_specular_map.frag index fa997bff7f..5eb804292c 100644 --- a/interface/resources/shaders/model_normal_specular_map.frag +++ b/interface/resources/shaders/model_normal_specular_map.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -42,8 +48,8 @@ void main(void) { vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(viewNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/resources/shaders/model_shadow_map.frag b/interface/resources/shaders/model_shadow_map.frag index c2bf95fd27..807a9d162f 100644 --- a/interface/resources/shaders/model_shadow_map.frag +++ b/interface/resources/shaders/model_shadow_map.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -33,8 +39,8 @@ void main(void) { // add up the local lights vec4 normalizedNormal = normalize(normal); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(normalizedNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/resources/shaders/model_shadow_normal_map.frag b/interface/resources/shaders/model_shadow_normal_map.frag index 058c3783be..454b88dd23 100644 --- a/interface/resources/shaders/model_shadow_normal_map.frag +++ b/interface/resources/shaders/model_shadow_normal_map.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -45,8 +51,8 @@ void main(void) { vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(viewNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/resources/shaders/model_shadow_normal_specular_map.frag b/interface/resources/shaders/model_shadow_normal_specular_map.frag index 5b8d7c87a4..8283d10b93 100644 --- a/interface/resources/shaders/model_shadow_normal_specular_map.frag +++ b/interface/resources/shaders/model_shadow_normal_specular_map.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -48,8 +54,8 @@ void main(void) { vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(viewNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(viewNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/resources/shaders/model_shadow_specular_map.frag b/interface/resources/shaders/model_shadow_specular_map.frag index 9f413f63a4..f30f3d7b06 100644 --- a/interface/resources/shaders/model_shadow_specular_map.frag +++ b/interface/resources/shaders/model_shadow_specular_map.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -36,8 +42,8 @@ void main(void) { // add up the local lights vec4 normalizedNormal = normalize(normal); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(normalizedNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/resources/shaders/model_specular_map.frag b/interface/resources/shaders/model_specular_map.frag index 54790a36fb..824746d575 100644 --- a/interface/resources/shaders/model_specular_map.frag +++ b/interface/resources/shaders/model_specular_map.frag @@ -14,6 +14,12 @@ // the maximum number of local lights to apply const int MAX_LOCAL_LIGHTS = 2; +// the color of each local light +uniform vec4 localLightColors[MAX_LOCAL_LIGHTS]; + +// the direction of each local light +uniform vec4 localLightDirections[MAX_LOCAL_LIGHTS]; + // the diffuse texture uniform sampler2D diffuseMap; @@ -30,8 +36,8 @@ void main(void) { // add up the local lights vec4 normalizedNormal = normalize(normal); vec4 localLight = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 1; i <= MAX_LOCAL_LIGHTS; i++) { - localLight += gl_FrontLightProduct[i].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[i].position)); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + localLight += localLightColors[i] * max(0.0, dot(normalizedNormal, localLightDirections[i])); } // compute the base color based on OpenGL lighting model diff --git a/interface/src/Application.h b/interface/src/Application.h index d956a949ac..97457a00c8 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -258,6 +258,8 @@ public: /// the view matrix translation. void updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation = glm::vec3()); + const glm::mat4& getUntranslatedViewMatrix() const { return _untranslatedViewMatrix; } + /// Loads a view matrix that incorporates the specified model translation without the precision issues that can /// result from matrix multiplication at high translation magnitudes. void loadTranslatedViewMatrix(const glm::vec3& translation); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index c5c83aa01d..14bf3fb684 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -104,17 +104,18 @@ ProgramObject Model::_skinCascadedShadowNormalSpecularMapProgram; ProgramObject Model::_skinShadowProgram; -int Model::_normalMapTangentLocation; -int Model::_normalSpecularMapTangentLocation; -int Model::_shadowNormalMapTangentLocation; -int Model::_shadowNormalSpecularMapTangentLocation; -int Model::_cascadedShadowNormalMapTangentLocation; -int Model::_cascadedShadowNormalSpecularMapTangentLocation; - -int Model::_cascadedShadowMapDistancesLocation; -int Model::_cascadedShadowNormalMapDistancesLocation; -int Model::_cascadedShadowSpecularMapDistancesLocation; -int Model::_cascadedShadowNormalSpecularMapDistancesLocation; +Model::Locations Model::_locations; +Model::Locations Model::_normalMapLocations; +Model::Locations Model::_specularMapLocations; +Model::Locations Model::_normalSpecularMapLocations; +Model::Locations Model::_shadowMapLocations; +Model::Locations Model::_shadowNormalMapLocations; +Model::Locations Model::_shadowSpecularMapLocations; +Model::Locations Model::_shadowNormalSpecularMapLocations; +Model::Locations Model::_cascadedShadowMapLocations; +Model::Locations Model::_cascadedShadowNormalMapLocations; +Model::Locations Model::_cascadedShadowSpecularMapLocations; +Model::Locations Model::_cascadedShadowNormalSpecularMapLocations; Model::SkinLocations Model::_skinLocations; Model::SkinLocations Model::_skinNormalMapLocations; @@ -160,15 +161,13 @@ void Model::setOffset(const glm::vec3& offset) { _snappedToCenter = false; } - -void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations, +void Model::initProgram(ProgramObject& program, Model::Locations& locations, int specularTextureUnit, int shadowTextureUnit) { program.bind(); - locations.clusterMatrices = program.uniformLocation("clusterMatrices"); - locations.clusterIndices = program.attributeLocation("clusterIndices"); - locations.clusterWeights = program.attributeLocation("clusterWeights"); locations.tangent = program.attributeLocation("tangent"); locations.shadowDistances = program.uniformLocation("shadowDistances"); + locations.localLightColors = program.uniformLocation("localLightColors"); + locations.localLightDirections = program.uniformLocation("localLightDirections"); program.setUniformValue("diffuseMap", 0); program.setUniformValue("normalMap", 1); program.setUniformValue("specularMap", specularTextureUnit); @@ -176,6 +175,17 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati program.release(); } +void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations, + int specularTextureUnit, int shadowTextureUnit) { + initProgram(program, locations, specularTextureUnit, shadowTextureUnit); + + program.bind(); + locations.clusterMatrices = program.uniformLocation("clusterMatrices"); + locations.clusterIndices = program.attributeLocation("clusterIndices"); + locations.clusterWeights = program.attributeLocation("clusterWeights"); + program.release(); +} + QVector Model::createJointStates(const FBXGeometry& geometry) { QVector jointStates; foreach (const FBXJoint& joint, geometry.joints) { @@ -212,9 +222,7 @@ void Model::init() { _program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model.frag"); _program.link(); - _program.bind(); - _program.setUniformValue("diffuseMap", 0); - _program.release(); + initProgram(_program, _locations); _normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_normal_map.vert"); @@ -222,11 +230,7 @@ void Model::init() { Application::resourcesPath() + "shaders/model_normal_map.frag"); _normalMapProgram.link(); - _normalMapProgram.bind(); - _normalMapProgram.setUniformValue("diffuseMap", 0); - _normalMapProgram.setUniformValue("normalMap", 1); - _normalMapTangentLocation = _normalMapProgram.attributeLocation("tangent"); - _normalMapProgram.release(); + initProgram(_normalMapProgram, _normalMapLocations); _specularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model.vert"); @@ -234,10 +238,7 @@ void Model::init() { Application::resourcesPath() + "shaders/model_specular_map.frag"); _specularMapProgram.link(); - _specularMapProgram.bind(); - _specularMapProgram.setUniformValue("diffuseMap", 0); - _specularMapProgram.setUniformValue("specularMap", 1); - _specularMapProgram.release(); + initProgram(_specularMapProgram, _specularMapLocations); _normalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_normal_map.vert"); @@ -245,12 +246,7 @@ void Model::init() { Application::resourcesPath() + "shaders/model_normal_specular_map.frag"); _normalSpecularMapProgram.link(); - _normalSpecularMapProgram.bind(); - _normalSpecularMapProgram.setUniformValue("diffuseMap", 0); - _normalSpecularMapProgram.setUniformValue("normalMap", 1); - _normalSpecularMapProgram.setUniformValue("specularMap", 2); - _normalSpecularMapTangentLocation = _normalSpecularMapProgram.attributeLocation("tangent"); - _normalSpecularMapProgram.release(); + initProgram(_normalSpecularMapProgram, _normalSpecularMapLocations, 2); _shadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model.vert"); @@ -258,10 +254,7 @@ void Model::init() { "shaders/model_shadow_map.frag"); _shadowMapProgram.link(); - _shadowMapProgram.bind(); - _shadowMapProgram.setUniformValue("diffuseMap", 0); - _shadowMapProgram.setUniformValue("shadowMap", 1); - _shadowMapProgram.release(); + initProgram(_shadowMapProgram, _shadowMapLocations); _shadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_normal_map.vert"); @@ -269,12 +262,7 @@ void Model::init() { Application::resourcesPath() + "shaders/model_shadow_normal_map.frag"); _shadowNormalMapProgram.link(); - _shadowNormalMapProgram.bind(); - _shadowNormalMapProgram.setUniformValue("diffuseMap", 0); - _shadowNormalMapProgram.setUniformValue("normalMap", 1); - _shadowNormalMapProgram.setUniformValue("shadowMap", 2); - _shadowNormalMapTangentLocation = _shadowNormalMapProgram.attributeLocation("tangent"); - _shadowNormalMapProgram.release(); + initProgram(_shadowNormalMapProgram, _shadowNormalMapLocations, 1, 2); _shadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model.vert"); @@ -282,11 +270,7 @@ void Model::init() { Application::resourcesPath() + "shaders/model_shadow_specular_map.frag"); _shadowSpecularMapProgram.link(); - _shadowSpecularMapProgram.bind(); - _shadowSpecularMapProgram.setUniformValue("diffuseMap", 0); - _shadowSpecularMapProgram.setUniformValue("specularMap", 1); - _shadowSpecularMapProgram.setUniformValue("shadowMap", 2); - _shadowSpecularMapProgram.release(); + initProgram(_shadowSpecularMapProgram, _shadowSpecularMapLocations, 1, 2); _shadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_normal_map.vert"); @@ -294,13 +278,7 @@ void Model::init() { Application::resourcesPath() + "shaders/model_shadow_normal_specular_map.frag"); _shadowNormalSpecularMapProgram.link(); - _shadowNormalSpecularMapProgram.bind(); - _shadowNormalSpecularMapProgram.setUniformValue("diffuseMap", 0); - _shadowNormalSpecularMapProgram.setUniformValue("normalMap", 1); - _shadowNormalSpecularMapProgram.setUniformValue("specularMap", 2); - _shadowNormalSpecularMapProgram.setUniformValue("shadowMap", 3); - _shadowNormalSpecularMapTangentLocation = _shadowNormalSpecularMapProgram.attributeLocation("tangent"); - _shadowNormalSpecularMapProgram.release(); + initProgram(_shadowNormalSpecularMapProgram, _shadowNormalSpecularMapLocations, 2, 3); _cascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + @@ -309,11 +287,7 @@ void Model::init() { "shaders/model_cascaded_shadow_map.frag"); _cascadedShadowMapProgram.link(); - _cascadedShadowMapProgram.bind(); - _cascadedShadowMapProgram.setUniformValue("diffuseMap", 0); - _cascadedShadowMapProgram.setUniformValue("shadowMap", 1); - _cascadedShadowMapDistancesLocation = _cascadedShadowMapProgram.uniformLocation("shadowDistances"); - _cascadedShadowMapProgram.release(); + initProgram(_cascadedShadowMapProgram, _cascadedShadowMapLocations); _cascadedShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_normal_map.vert"); @@ -321,13 +295,7 @@ void Model::init() { Application::resourcesPath() + "shaders/model_cascaded_shadow_normal_map.frag"); _cascadedShadowNormalMapProgram.link(); - _cascadedShadowNormalMapProgram.bind(); - _cascadedShadowNormalMapProgram.setUniformValue("diffuseMap", 0); - _cascadedShadowNormalMapProgram.setUniformValue("normalMap", 1); - _cascadedShadowNormalMapProgram.setUniformValue("shadowMap", 2); - _cascadedShadowNormalMapDistancesLocation = _cascadedShadowNormalMapProgram.uniformLocation("shadowDistances"); - _cascadedShadowNormalMapTangentLocation = _cascadedShadowNormalMapProgram.attributeLocation("tangent"); - _cascadedShadowNormalMapProgram.release(); + initProgram(_cascadedShadowNormalMapProgram, _cascadedShadowNormalMapLocations, 1, 2); _cascadedShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model.vert"); @@ -335,12 +303,7 @@ void Model::init() { Application::resourcesPath() + "shaders/model_cascaded_shadow_specular_map.frag"); _cascadedShadowSpecularMapProgram.link(); - _cascadedShadowSpecularMapProgram.bind(); - _cascadedShadowSpecularMapProgram.setUniformValue("diffuseMap", 0); - _cascadedShadowSpecularMapProgram.setUniformValue("specularMap", 1); - _cascadedShadowSpecularMapProgram.setUniformValue("shadowMap", 2); - _cascadedShadowSpecularMapDistancesLocation = _cascadedShadowSpecularMapProgram.uniformLocation("shadowDistances"); - _cascadedShadowSpecularMapProgram.release(); + initProgram(_cascadedShadowSpecularMapProgram, _cascadedShadowSpecularMapLocations, 1, 2); _cascadedShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_normal_map.vert"); @@ -348,15 +311,7 @@ void Model::init() { Application::resourcesPath() + "shaders/model_cascaded_shadow_normal_specular_map.frag"); _cascadedShadowNormalSpecularMapProgram.link(); - _cascadedShadowNormalSpecularMapProgram.bind(); - _cascadedShadowNormalSpecularMapProgram.setUniformValue("diffuseMap", 0); - _cascadedShadowNormalSpecularMapProgram.setUniformValue("normalMap", 1); - _cascadedShadowNormalSpecularMapProgram.setUniformValue("specularMap", 2); - _cascadedShadowNormalSpecularMapProgram.setUniformValue("shadowMap", 3); - _cascadedShadowNormalSpecularMapDistancesLocation = - _cascadedShadowNormalSpecularMapProgram.uniformLocation("shadowDistances"); - _cascadedShadowNormalSpecularMapTangentLocation = _cascadedShadowNormalSpecularMapProgram.attributeLocation("tangent"); - _cascadedShadowNormalSpecularMapProgram.release(); + initProgram(_cascadedShadowNormalSpecularMapProgram, _cascadedShadowNormalSpecularMapLocations, 2, 3); _shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert"); @@ -630,17 +585,16 @@ bool Model::render(float alpha, RenderMode mode, bool receiveShadows) { glCullFace(GL_FRONT); } else if (mode == DEFAULT_RENDER_MODE) { - // set up the local lights + // update the local lights for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { - glm::vec4 color; - GLenum lightName = GL_LIGHT0 + i + 1; if (i < _localLights.size()) { const LocalLight& light = _localLights.at(i); - color = glm::vec4(light.color, 1.0f); - glm::vec4 position = glm::vec4(_rotation * light.direction, 0.0f); - glLightfv(lightName, GL_POSITION, (GLfloat*)&position); + _localLightColors[i] = glm::vec4(light.color, 1.0f); + _localLightDirections[i] = glm::normalize(Application::getInstance()->getUntranslatedViewMatrix() * + glm::vec4(_rotation * light.direction, 0.0f)); + } else { + _localLightColors[i] = glm::vec4(); } - glLightfv(lightName, GL_DIFFUSE, (GLfloat*)&color); } } } @@ -1417,10 +1371,9 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re const_cast(networkMesh.vertexBuffer).bind(); ProgramObject* program = &_program; + Locations* locations = &_locations; ProgramObject* skinProgram = &_skinProgram; SkinLocations* skinLocations = &_skinLocations; - int tangentLocation = _normalMapTangentLocation; - int shadowDistancesLocation = _cascadedShadowMapDistancesLocation; GLenum specularTextureUnit = 0; GLenum shadowTextureUnit = 0; if (mode == SHADOW_RENDER_MODE) { @@ -1433,41 +1386,40 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re if (receiveShadows) { if (cascadedShadows) { program = &_cascadedShadowNormalSpecularMapProgram; + locations = &_cascadedShadowNormalSpecularMapLocations; skinProgram = &_skinCascadedShadowNormalSpecularMapProgram; skinLocations = &_skinCascadedShadowNormalSpecularMapLocations; - tangentLocation = _cascadedShadowNormalSpecularMapTangentLocation; - shadowDistancesLocation = _cascadedShadowNormalSpecularMapDistancesLocation; } else { program = &_shadowNormalSpecularMapProgram; + locations = &_shadowNormalSpecularMapLocations; skinProgram = &_skinShadowNormalSpecularMapProgram; skinLocations = &_skinShadowNormalSpecularMapLocations; - tangentLocation = _shadowNormalSpecularMapTangentLocation; } shadowTextureUnit = GL_TEXTURE3; } else { program = &_normalSpecularMapProgram; + locations = &_normalSpecularMapLocations; skinProgram = &_skinNormalSpecularMapProgram; skinLocations = &_skinNormalSpecularMapLocations; - tangentLocation = _normalSpecularMapTangentLocation; } specularTextureUnit = GL_TEXTURE2; } else if (receiveShadows) { if (cascadedShadows) { program = &_cascadedShadowNormalMapProgram; + locations = &_cascadedShadowNormalMapLocations; skinProgram = &_skinCascadedShadowNormalMapProgram; skinLocations = &_skinCascadedShadowNormalMapLocations; - tangentLocation = _cascadedShadowNormalMapTangentLocation; - shadowDistancesLocation = _cascadedShadowNormalMapDistancesLocation; } else { program = &_shadowNormalMapProgram; + locations = &_shadowNormalMapLocations; skinProgram = &_skinShadowNormalMapProgram; skinLocations = &_skinShadowNormalMapLocations; - tangentLocation = _shadowNormalMapTangentLocation; } shadowTextureUnit = GL_TEXTURE2; } else { program = &_normalMapProgram; + locations = &_normalMapLocations; skinProgram = &_skinNormalMapProgram; skinLocations = &_skinNormalMapLocations; } @@ -1475,17 +1427,19 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re if (receiveShadows) { if (cascadedShadows) { program = &_cascadedShadowSpecularMapProgram; + locations = &_cascadedShadowSpecularMapLocations; skinProgram = &_skinCascadedShadowSpecularMapProgram; skinLocations = &_skinCascadedShadowSpecularMapLocations; - shadowDistancesLocation = _cascadedShadowSpecularMapDistancesLocation; } else { program = &_shadowSpecularMapProgram; + locations = &_shadowSpecularMapLocations; skinProgram = &_skinShadowSpecularMapProgram; skinLocations = &_skinShadowSpecularMapLocations; } shadowTextureUnit = GL_TEXTURE2; } else { program = &_specularMapProgram; + locations = &_specularMapLocations; skinProgram = &_skinSpecularMapProgram; skinLocations = &_skinSpecularMapLocations; } @@ -1494,10 +1448,12 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re } else if (receiveShadows) { if (cascadedShadows) { program = &_cascadedShadowMapProgram; + locations = &_cascadedShadowMapLocations; skinProgram = &_skinCascadedShadowMapProgram; skinLocations = &_skinCascadedShadowMapLocations; } else { program = &_shadowMapProgram; + locations = &_shadowMapLocations; skinProgram = &_skinShadowMapProgram; skinLocations = &_skinShadowMapLocations; } @@ -1506,6 +1462,7 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re const MeshState& state = _meshStates.at(i); ProgramObject* activeProgram = program; + Locations* activeLocations = locations; glPushMatrix(); Application::getInstance()->loadTranslatedViewMatrix(_translation); @@ -1522,22 +1479,24 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re skinProgram->enableAttributeArray(skinLocations->clusterIndices); skinProgram->enableAttributeArray(skinLocations->clusterWeights); activeProgram = skinProgram; - tangentLocation = skinLocations->tangent; - if (cascadedShadows) { - program->setUniform(skinLocations->shadowDistances, Application::getInstance()->getShadowDistances()); - } + activeLocations = skinLocations; + } else { glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); program->bind(); - if (cascadedShadows) { - program->setUniform(shadowDistancesLocation, Application::getInstance()->getShadowDistances()); - } } - + if (cascadedShadows) { + activeProgram->setUniform(activeLocations->shadowDistances, Application::getInstance()->getShadowDistances()); + } + activeProgram->setUniformValueArray(activeLocations->localLightColors, + (const GLfloat*)_localLightColors, MAX_LOCAL_LIGHTS, 4); + activeProgram->setUniformValueArray(activeLocations->localLightDirections, + (const GLfloat*)_localLightDirections, MAX_LOCAL_LIGHTS, 4); + if (mesh.blendshapes.isEmpty()) { if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) { - activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); - activeProgram->enableAttributeArray(tangentLocation); + activeProgram->setAttributeBuffer(activeLocations->tangent, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); + activeProgram->enableAttributeArray(activeLocations->tangent); } glColorPointer(3, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + mesh.tangents.size() * sizeof(glm::vec3))); @@ -1546,8 +1505,8 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re } else { if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) { - activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, 0, 3); - activeProgram->enableAttributeArray(tangentLocation); + activeProgram->setAttributeBuffer(activeLocations->tangent, GL_FLOAT, 0, 3); + activeProgram->enableAttributeArray(activeLocations->tangent); } glColorPointer(3, GL_FLOAT, 0, (void*)(mesh.tangents.size() * sizeof(glm::vec3))); glTexCoordPointer(2, GL_FLOAT, 0, (void*)((mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); @@ -1635,7 +1594,7 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); - activeProgram->disableAttributeArray(tangentLocation); + activeProgram->disableAttributeArray(activeLocations->tangent); } if (specularTextureUnit) { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index fe09710d0a..a4eae8fd9a 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -258,6 +258,9 @@ private: QList _runningAnimations; + glm::vec4 _localLightColors[MAX_LOCAL_LIGHTS]; + glm::vec4 _localLightDirections[MAX_LOCAL_LIGHTS]; + static ProgramObject _program; static ProgramObject _normalMapProgram; static ProgramObject _specularMapProgram; @@ -304,13 +307,35 @@ private: static int _cascadedShadowSpecularMapDistancesLocation; static int _cascadedShadowNormalSpecularMapDistancesLocation; - class SkinLocations { + class Locations { + public: + int localLightColors; + int localLightDirections; + int tangent; + int shadowDistances; + }; + + static Locations _locations; + static Locations _normalMapLocations; + static Locations _specularMapLocations; + static Locations _normalSpecularMapLocations; + static Locations _shadowMapLocations; + static Locations _shadowNormalMapLocations; + static Locations _shadowSpecularMapLocations; + static Locations _shadowNormalSpecularMapLocations; + static Locations _cascadedShadowMapLocations; + static Locations _cascadedShadowNormalMapLocations; + static Locations _cascadedShadowSpecularMapLocations; + static Locations _cascadedShadowNormalSpecularMapLocations; + + static void initProgram(ProgramObject& program, Locations& locations, + int specularTextureUnit = 1, int shadowTextureUnit = 1); + + class SkinLocations : public Locations { public: int clusterMatrices; int clusterIndices; - int clusterWeights; - int tangent; - int shadowDistances; + int clusterWeights; }; static SkinLocations _skinLocations; From 02bf76240aa7554d58ed16f173ec4084ba31fa8b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 22 Jul 2014 18:30:28 -0700 Subject: [PATCH 53/53] Multiply local light colors by material diffuse reflectance. --- interface/src/renderer/Model.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 14bf3fb684..8fac5d3d03 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -588,10 +588,8 @@ bool Model::render(float alpha, RenderMode mode, bool receiveShadows) { // update the local lights for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { if (i < _localLights.size()) { - const LocalLight& light = _localLights.at(i); - _localLightColors[i] = glm::vec4(light.color, 1.0f); _localLightDirections[i] = glm::normalize(Application::getInstance()->getUntranslatedViewMatrix() * - glm::vec4(_rotation * light.direction, 0.0f)); + glm::vec4(_rotation * _localLights.at(i).direction, 0.0f)); } else { _localLightColors[i] = glm::vec4(); } @@ -1488,8 +1486,6 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re if (cascadedShadows) { activeProgram->setUniform(activeLocations->shadowDistances, Application::getInstance()->getShadowDistances()); } - activeProgram->setUniformValueArray(activeLocations->localLightColors, - (const GLfloat*)_localLightColors, MAX_LOCAL_LIGHTS, 4); activeProgram->setUniformValueArray(activeLocations->localLightDirections, (const GLfloat*)_localLightDirections, MAX_LOCAL_LIGHTS, 4); @@ -1544,6 +1540,12 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); + for (int k = 0; k < qMin(MAX_LOCAL_LIGHTS, _localLights.size()); k++) { + _localLightColors[k] = glm::vec4(_localLights.at(k).color, 1.0f) * diffuse; + } + activeProgram->setUniformValueArray(activeLocations->localLightColors, + (const GLfloat*)_localLightColors, MAX_LOCAL_LIGHTS, 4); + Texture* diffuseMap = networkPart.diffuseTexture.data(); if (mesh.isEye && diffuseMap) { diffuseMap = (_dilatedTextures[i][j] =