Merge branch 'master' into tony/anim-sync-blend

This commit is contained in:
Anthony J. Thibault 2015-11-02 08:37:10 -08:00
commit e3e759248f
284 changed files with 10475 additions and 4835 deletions

View file

@ -5,25 +5,30 @@
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1m
* IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities.
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
* [zlib](http://www.zlib.net/)
####CMake External Project Dependencies
* [boostconfig](https://github.com/boostorg/config) ~> 1.58
* [Bullet Physics Engine](https://code.google.com/p/bullet/downloads/list) ~> 2.82
* [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3
* [Faceshift](http://www.faceshift.com/) ~> 4.3
* [GLEW](http://glew.sourceforge.net/)
* [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.4
* [gverb](https://github.com/highfidelity/gverb)
The following external projects are optional dependencies. You can indicate to CMake that you would like to include them by passing -DGET_$NAME=1 when running a clean CMake build. For example, to get CMake to download and compile SDL2 you would pass -DGET_SDL2=1.
* [Oculus SDK](https://developer.oculus.com/downloads/) ~> 0.6 (Win32) / 0.5 (Mac / Linux)
* [oglplus](http://oglplus.org/) ~> 0.63
* [OpenVR](https://github.com/ValveSoftware/openvr) ~> 0.91 (Win32 only)
* [Polyvox](http://www.volumesoffun.com/) ~> 0.2.1
* [SDL2](https://www.libsdl.org/download-2.0.php) ~> 2.0.3
* Enables game controller support in Interface
* [soxr](http://soxr.sourceforge.net) ~> 0.1.1
* [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3
* [Sixense](http://sixense.com/) ~> 071615
* [zlib](http://www.zlib.net/) ~> 1.28 (Win32 only)
The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build/ext` folder in each of the subfolders for each external project.
These are not placed in your normal build tree when doing an out of source build so that they do not need to be re-downloaded and re-compiled every time the CMake build folder is cleared. Should you want to force a re-download and re-compile of a specific external, you can simply remove that directory from the appropriate subfolder in `build/ext`. Should you want to force a re-download and re-compile of all externals, just remove the `build/ext` folder.
If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DGET_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project.
If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE_LOCAL_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project.
###OS Specific Build Guides
* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.
@ -63,7 +68,7 @@ For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generat
####Finding Dependencies
The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DGET_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies).
The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DUSE_LOCAL_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies).
You can point our [Cmake find modules](cmake/modules/) to the correct version of dependencies by setting one of the three following variables to the location of the correct version of the dependency.
@ -77,5 +82,5 @@ In the examples below the variable $NAME would be replaced by the name of the de
####Devices
You can support external input/output devices such as Oculus Rift, Leap Motion, Faceshift, 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.
You can support external input/output devices such as Leap Motion, MIDI, 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.

View file

@ -75,16 +75,6 @@ To prevent these problems, install OpenSSL yourself. Download the following bina
Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version.
####zlib
Install zlib from
[Zlib for Windows](http://gnuwin32.sourceforge.net/packages/zlib.htm)
and fix a header file, as described here:
[zlib zconf.h bug](http://sourceforge.net/p/gnuwin32/bugs/169/)
###Build High Fidelity using Visual Studio
Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake.

View file

@ -185,22 +185,8 @@ set(EXTERNAL_PROJECT_PREFIX "project")
set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX})
setup_externals_binary_dir()
# setup options to grab external project dependencies
option(GET_BULLET "Get Bullet library automatically as external project" 1)
option(GET_GLM "Get GLM library automatically as external project" 1)
option(GET_GVERB "Get Gverb library automatically as external project" 1)
option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1)
option(GET_LIBOVR "Get LibOVR library automatically as external project" 1)
option(GET_VHACD "Get V-HACD library automatically as external project" 1)
option(GET_POLYVOX "Get polyvox library automatically as external project" 1)
option(GET_OPENVR "Get OpenVR library automatically as external project" 1)
option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1)
option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1)
option(GET_GLEW "Get GLEW library automatically as external project" 1)
option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
option(GET_SDL2 "Get SDL2 library automatically as external project" 0)
if (WIN32)
add_paths_to_fixup_libs("${QT_DIR}/bin")
@ -217,6 +203,7 @@ if (NOT ANDROID)
add_subdirectory(interface)
set_target_properties(interface PROPERTIES FOLDER "Apps")
add_subdirectory(tests)
add_subdirectory(plugins)
add_subdirectory(tools)
endif ()

View file

@ -1,12 +1,12 @@
set(TARGET_NAME assignment-client)
setup_hifi_project(Core Gui Network Script Widgets WebSockets)
setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets)
# link in the shared libraries
link_hifi_libraries(
audio avatars octree environment gpu model fbx entities
networking animation shared script-engine embedded-webserver
physics
controllers physics
)
include_application_version()

View file

@ -74,7 +74,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
// make sure we output process IDs for a child AC otherwise it's insane to parse
LogHandler::getInstance().setShouldOutputPID(true);
LogHandler::getInstance().setShouldOutputProcessID(true);
// setup our _requestAssignment member variable from the passed arguments
_requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool);

View file

@ -44,7 +44,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME);
// make sure we output process IDs for a monitor otherwise it's insane to parse
LogHandler::getInstance().setShouldOutputPID(true);
LogHandler::getInstance().setShouldOutputProcessID(true);
// create a NodeList so we can receive stats from children
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();

View file

@ -254,10 +254,10 @@ void AvatarMixer::broadcastAvatarData() {
// potentially update the max full rate distance for this frame
maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar);
if (distanceToAvatar != 0.0f
if (distanceToAvatar != 0.0f
&& distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) {
return;
}
return;
}
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber();

View file

@ -0,0 +1,30 @@
set(EXTERNAL_NAME faceshift)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://hifi-public.s3.amazonaws.com/dependencies/faceshift.zip
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
# URL_MD5 1bdcb8a0b8d5b1ede434cc41efade41d
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to Faceshift include directory")
if (WIN32)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/faceshift.lib CACHE FILEPATH "Faceshift libraries")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/faceshift.lib CACHE FILEPATH "Faceshift libraries")
elseif (APPLE)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/libfaceshift.a CACHE FILEPATH "Faceshift libraries")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/libfaceshift.a CACHE FILEPATH "Faceshift libraries")
endif()

View file

@ -7,14 +7,15 @@ endif ()
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple.zip
URL_MD5 0507dc08337a82a5e7ecbc5417f92cc1
URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple2.zip
URL_MD5 f05d858e8203c32b689da208ad8b39db
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")

View file

@ -2,6 +2,8 @@ set(EXTERNAL_NAME sdl2)
include(ExternalProject)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
@ -13,15 +15,33 @@ if (WIN32)
LOG_DOWNLOAD 1
)
elseif (APPLE)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-2.0.3-OSX.tar.gz
URL_MD5 64f888886268bdf1656ef1b4b7d7756d
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://hifi-public.s3.amazonaws.com/dependencies/SDL2-2.0.3.zip
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DVIDEO_OPENGL=OFF
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory")
set(${EXTERNAL_NAME_UPPER}_LIBRARY "${INSTALL_DIR}/lib/libSDL2-2.0.dylib" CACHE STRING "Path to SDL2 library")
set(_SDL2_LIB_DIR "${INSTALL_DIR}/lib")
ExternalProject_Add_Step(
${EXTERNAL_NAME}
change-install-name
COMMENT "Calling install_name_tool on SDL2 libraries to fix install name for dylib linking"
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SDL2_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
DEPENDEES install
WORKING_DIRECTORY <INSTALL_DIR>
LOG 1
)
else ()
if (ANDROID)
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
@ -41,31 +61,15 @@ endif ()
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (APPLE)
elseif (WIN32)
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/SDL2.framework/Headers CACHE PATH "Location of SDL2 include directory")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/SDL2.framework/SDL2 CACHE STRING "Path to SDL2 library")
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library")
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL")
else ()
if (WIN32)
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set(_ROOT_DIR ${SOURCE_DIR})
set(_INCLUDE_DIR ${_ROOT_DIR}/include)
set(_LIB_DIR "${SOURCE_DIR}/lib/x86")
set(_LIB_EXT "lib")
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${_LIB_DIR} CACHE PATH "Location of SDL2 DLL")
else ()
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
set(_ROOT_DIR ${INSTALL_DIR})
set(_INCLUDE_DIR ${_ROOT_DIR}/include/SDL2)
set(_LIB_DIR ${INSTALL_DIR}/lib)
set(_LIB_EXT "so")
set(_LIB_PREFIX "lib")
endif ()
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${_INCLUDE_DIR} CACHE PATH "Location of SDL2 include directory")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${_LIB_DIR}/${_LIB_PREFIX}SDL2.${_LIB_EXT} CACHE FILEPATH "Path to SDL2 library")
endif ()
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${INSTALL_DIR}/lib/libSDL2.so CACHE FILEPATH "Path to SDL2 library")
endif ()

View file

@ -1,30 +1,20 @@
include(ExternalProject)
include(SelectLibraryConfigurations)
set(EXTERNAL_NAME Sixense)
set(EXTERNAL_NAME sixense)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
ExternalProject_Add(
${EXTERNAL_NAME}
URL ./SixenseSDK_062612.zip
URL_MD5 10cc8dc470d2ac1244a88cf04bc549cc
URL http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip
URL_MD5 752a3901f334124e9cffc2ba4136ef7d
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
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)
endif ()
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
@ -39,22 +29,41 @@ if (WIN32)
set(ARCH_DIR "Win32")
set(ARCH_SUFFIX "")
endif()
# FIXME need to account for different architectures
set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2013/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
add_paths_to_fixup_libs("${SOURCE_DIR}/bin/${ARCH_DIR}/VS2013/release_dll")
elseif(APPLE)
# FIXME need to account for different architectures
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL)
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${SOURCE_DIR}/lib/osx_x64/debug_dll/libsixensed_x64.dylib CACHE TYPE INTERNAL)
set(_SIXENSE_LIB_DIR "${SOURCE_DIR}/lib/osx_x64")
ExternalProject_Add_Step(
${EXTERNAL_NAME}
change-install-name-release
COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking"
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SIXENSE_LIB_DIR}/release_dll -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
DEPENDEES install
WORKING_DIRECTORY <SOURCE_DIR>
LOG 1
)
set(_SIXENSE_LIB_DIR "${SOURCE_DIR}/lib/osx_x64")
ExternalProject_Add_Step(
${EXTERNAL_NAME}
change-install-name-debug
COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking"
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SIXENSE_LIB_DIR}/debug_dll -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
DEPENDEES install
WORKING_DIRECTORY <SOURCE_DIR>
LOG 1
)
elseif(NOT ANDROID)
# FIXME need to account for different architectures
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL)
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux32)
# FIXME need to account for different architectures
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL)
endif()

View file

@ -1,28 +1,31 @@
set(EXTERNAL_NAME zlib)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (WIN32)
set(EXTERNAL_NAME zlib)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
include(ExternalProject)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://zlib.net/zlib128.zip
URL_MD5 126f8676442ffbd97884eb4d6f32afb4
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://zlib.net/zlib128.zip
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include CACHE PATH "List of zlib include directories")
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${${EXTERNAL_NAME_UPPER}_INCLUDE_DIR} CACHE PATH "List of zlib include directories")
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/bin CACHE FILEPATH "Location of ZLib DLL")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/zlib.lib CACHE FILEPATH "Location of zlib release library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/zlibd.lib CACHE FILEPATH "Location of zlib debug library")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
include(SelectLibraryConfigurations)
select_library_configurations(${EXTERNAL_NAME_UPPER})
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of zlib include directories")
ExternalProject_Get_Property(${EXTERNAL_NAME} BINARY_DIR)
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${BINARY_DIR}/Release CACHE FILEPATH "Location of GLEW DLL")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${BINARY_DIR}/Release/zlib.lib CACHE FILEPATH "Location of ZLib release library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Location of ZLib debug library")
endif ()
# Force selected libraries into the cache
set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE FILEPATH "Location of zlib libraries")
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of zlib libraries")

View file

@ -16,7 +16,7 @@ macro(ADD_DEPENDENCY_EXTERNAL_PROJECTS)
string(TOUPPER ${_PROJ_NAME} _PROJ_NAME_UPPER)
# has the user told us they specific don't want this as an external project?
if (GET_${_PROJ_NAME_UPPER})
if (NOT USE_LOCAL_${_PROJ_NAME_UPPER})
# have we already detected we can't have this as external project on this OS?
if (NOT DEFINED ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT OR ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT)
# have we already setup the target?

View file

@ -0,0 +1,33 @@
#
# Created by Bradley Austin Davis on 2015/10/25
# Copyright 2015 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
#
macro(SETUP_HIFI_PLUGIN)
set(${TARGET_NAME}_SHARED 1)
setup_hifi_library(${ARGV})
add_dependencies(interface ${TARGET_NAME})
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Plugins")
if (APPLE)
set(PLUGIN_PATH "interface.app/Contents/MacOS/plugins")
else()
set(PLUGIN_PATH "plugins")
endif()
# create the destination for the plugin binaries
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E make_directory
"${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${PLUGIN_PATH}/"
)
add_custom_command(TARGET ${DIR} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy
"$<TARGET_FILE:${TARGET_NAME}>"
"${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${PLUGIN_PATH}/"
)
endmacro()

View file

@ -0,0 +1,14 @@
#
# Copyright 2015 High Fidelity, Inc.
# Created by Bradley Austin Davis on 2015/10/10
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_FACESHIFT)
add_dependency_external_projects(faceshift)
find_package(Faceshift REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${FACESHIFT_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${FACESHIFT_LIBRARIES})
add_definitions(-DHAVE_FACESHIFT)
endmacro()

View file

@ -0,0 +1,14 @@
#
# Copyright 2015 High Fidelity, Inc.
# Created by Bradley Austin Davis on 2015/10/10
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_SDL2)
add_dependency_external_projects(sdl2)
find_package(SDL2 REQUIRED)
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SDL2_INCLUDE_DIR})
target_link_libraries(${TARGET_NAME} ${SDL2_LIBRARY})
add_definitions(-DHAVE_SDL2)
endmacro()

View file

@ -0,0 +1,14 @@
#
# Copyright 2015 High Fidelity, Inc.
# Created by Bradley Austin Davis on 2015/10/10
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_SIXENSE)
add_dependency_external_projects(sixense)
find_package(Sixense REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
add_definitions(-DHAVE_SIXENSE)
endmacro()

View file

@ -0,0 +1,22 @@
#
# Copyright 2015 High Fidelity, Inc.
# Created by Bradley Austin Davis on 2015/10/10
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_ZLIB)
if (WIN32)
add_dependency_external_projects(zlib)
endif()
find_package(ZLIB REQUIRED)
if (WIN32)
add_paths_to_fixup_libs(${ZLIB_DLL_PATH})
endif()
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
endmacro()

View file

@ -18,32 +18,9 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("faceshift")
find_path(FACESHIFT_INCLUDE_DIRS fsbinarystream.h PATH_SUFFIXES include HINTS ${FACESHIFT_SEARCH_DIRS})
if (APPLE)
set(ARCH_DIR "MacOS")
elseif (UNIX)
set(ARCH_DIR "UNIX")
elseif (WIN32)
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(ARCH_DIR "x64")
else()
set(ARCH_DIR "Win32")
endif()
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})
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)
mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS)

View file

@ -30,6 +30,4 @@ include(SelectLibraryConfigurations)
select_library_configurations(GLEW)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES)
message(STATUS "Found GLEW - Assuming that GLEW is static and defining GLEW_STATIC")
find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES)

View file

@ -18,49 +18,10 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("sixense")
find_path(SIXENSE_INCLUDE_DIRS sixense.h PATH_SUFFIXES include HINTS ${SIXENSE_SEARCH_DIRS})
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)
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(ARCH_DIR "x64")
set(ARCH_SUFFIX "_x64")
else()
set(ARCH_DIR "Win32")
set(ARCH_SUFFIX "")
endif()
find_library(SIXENSE_LIBRARY_RELEASE "lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" HINTS ${SIXENSE_SEARCH_DIRS})
find_library(SIXENSE_LIBRARY_DEBUG "lib/${ARCH_DIR}/debug_dll/sixensed.lib" HINTS ${SIXENSE_SEARCH_DIRS})
find_path(SIXENSE_DEBUG_DLL_PATH "sixensed${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/debug_dll HINTS ${SIXENSE_SEARCH_DIRS})
find_path(SIXENSE_RELEASE_DLL_PATH "sixense${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/release_dll HINTS ${SIXENSE_SEARCH_DIRS})
find_path(SIXENSE_DEVICE_DLL_PATH DeviceDLL.dll PATH_SUFFIXES samples/${ARCH_DIR}/sixense_simple3d HINTS ${SIXENSE_SEARCH_DIRS})
endif ()
include(SelectLibraryConfigurations)
select_library_configurations(SIXENSE)
set(SIXENSE_REQUIREMENTS SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES)
if (WIN32)
list(APPEND SIXENSE_REQUIREMENTS SIXENSE_DEBUG_DLL_PATH SIXENSE_RELEASE_DLL_PATH SIXENSE_DEVICE_DLL_PATH)
endif ()
set(SIXENSE_LIBRARIES "${SIXENSE_LIBRARY}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Sixense DEFAULT_MSG ${SIXENSE_REQUIREMENTS})
if (WIN32)
add_paths_to_fixup_libs(${SIXENSE_DEBUG_DLL_PATH} ${SIXENSE_RELEASE_DLL_PATH} ${SIXENSE_DEVICE_DLL_PATH})
endif ()
find_package_handle_standard_args(Sixense DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES)
mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS)

View file

@ -21,45 +21,17 @@ if (WIN32)
hifi_library_search_hints("iViewHMD")
find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS})
find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS})
find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS})
find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS})
find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS})
list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_API_DLL_PATH)
set(IVIEWHMD_DLLS
avcodec-53.dll
avformat-53.dll
avutil-51.dll
libboost_filesystem-mgw45-mt-1_49.dll
libboost_system-mgw45-mt-1_49.dll
libboost_thread-mgw45-mt-1_49.dll
libgcc_s_dw2-1.dll
libiViewNG-LibCore.dll
libopencv_calib3d244.dll
libopencv_core244.dll
libopencv_features2d244.dll
libopencv_flann244.dll
libopencv_highgui244.dll
libopencv_imgproc244.dll
libopencv_legacy244.dll
libopencv_ml244.dll
libopencv_video244.dll
libstdc++-6.dll
opencv_core220.dll
opencv_highgui220.dll
opencv_imgproc220.dll
swscale-2.dll
)
foreach(IVIEWHMD_DLL ${IVIEWHMD_DLLS})
find_path(IVIEWHMD_DLL_PATH ${IVIEWHMD_DLL} PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS})
list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH)
list(APPEND IVIEWHMD_DLL_PATHS ${IVIEWHMD_DLL_PATH})
endforeach()
find_path(IVIEWHMD_DLL_PATH_3RD_PARTY libiViewNG.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS})
list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH_3RD_PARTY)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(IVIEWHMD DEFAULT_MSG ${IVIEWHMD_REQUIREMENTS})
add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATHS})
add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATH_3RD_PARTY})
mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS)

View file

@ -10,5 +10,35 @@
#
include(BundleUtilities)
# replace copy_resolved_item_into_bundle
#
# The official version of copy_resolved_item_into_bundle will print out a "warning:" when
# the resolved item matches the resolved embedded item. This not not really an issue that
# should rise to the level of a "warning" so we replace this message with a "status:"
#
function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
if(WIN32)
# ignore case on Windows
string(TOLOWER "${resolved_item}" resolved_item_compare)
string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
else()
set(resolved_item_compare "${resolved_item}")
set(resolved_embedded_item_compare "${resolved_embedded_item}")
endif()
if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
# this is our only change from the original version
message(STATUS "status: resolved_item == resolved_embedded_item - not copying...")
else()
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
if(UNIX AND NOT APPLE)
file(RPATH_REMOVE FILE "${resolved_embedded_item}")
endif()
endif()
endfunction()
message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@")
fixup_bundle("${BUNDLE_EXECUTABLE}" "" "@FIXUP_LIBS@")

View file

@ -71,8 +71,8 @@ function maybePlaySound(deltaTime) {
var palm2Position = MyAvatar.getRightPalmPosition();
var distanceBetween = Vec3.length(Vec3.subtract(palm1Position, palm2Position));
var palm1Velocity = Controller.getSpatialControlVelocity(1);
var palm2Velocity = Controller.getSpatialControlVelocity(3);
var palm1Velocity = Controller.getPoseValue(Controller.Standard.LeftHand).velocity;
var palm2Velocity = Controller.getPoseValue(Controller.Standard.RightHand).velocity;
var closingVelocity = Vec3.length(Vec3.subtract(palm1Velocity, palm2Velocity));
const CLAP_SPEED = 0.7;

View file

@ -0,0 +1,101 @@
//
// controllerScriptingExamples.js
// examples
//
// Created by Sam Gondelman on 6/2/15
// Copyright 2015 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
//
// Assumes you only have the default keyboard connected
/*myFirstMapping = function() {
return {
"name": "example",
"channels": [
{ "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" },
{ "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" },
{ "from": "Keyboard.Left", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Right", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.A", "to": "Actions.YAW_LEFT" },
{ "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" },
{ "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" },
{ "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" },
{
"from": "Standard.LX",
"filters": [ {
"type": "scale",
"params": [2.0],
}
],
"to": "Actions.LATERAL_LEFT",
}, {
"from": "Keyboard.B",
"to": "Actions.Yaw"
}
]
}
}
*/
mySecondMapping = function() {
return {
"name": "example2",
"channels": [
{ "from": "Standard.LY", "to": "Actions.TranslateZ" },
{ "from": "Standard.LX", "to": "Actions.Yaw" },
]
}
}
//Script.include('mapping-test0.json');
/*var myFirstMappingJSON = myFirstMapping();
print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON));
var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON));
Controller.enableMapping("example3");
var mySecondMappingJSON = mySecondMapping();
print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON));
var mapping2 = Controller.parseMapping(JSON.stringify(mySecondMappingJSON));
mapping2.enable();
Controller.enableMapping("example2");
*/
var mapping3 = Controller.loadMapping(Script.resolvePath("example3.json"));
Controller.enableMapping("example3");
/*
Object.keys(Controller.Standard).forEach(function (input) {
print("Controller.Standard." + input + ":" + Controller.Standard[input]);
});
Object.keys(Controller.Hardware).forEach(function (deviceName) {
Object.keys(Controller.Hardware[deviceName]).forEach(function (input) {
print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]);
});
});
Object.keys(Controller.Actions).forEach(function (actionName) {
print("Controller.Actions." + actionName + ":" + Controller.Actions[actionName]);
});
*/
Controller.hardwareChanged.connect(function () {
print("hardwareChanged ---------------------------------------------------");
Object.keys(Controller.Hardware).forEach(function (deviceName) {
Object.keys(Controller.Hardware[deviceName]).forEach(function (input) {
print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]);
});
});
print("-------------------------------------------------------------------");
});

View file

@ -15,13 +15,11 @@
Script.include("../libraries/utils.js");
////////////////////////////////////////////////////////////
//
// add lines where the hand ray picking is happening
//
var WANT_DEBUG = false;
/////////////////////////////////////////////////////////////////
//
// these tune time-averaging and "on" value for analog trigger
//
@ -30,7 +28,6 @@ var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value
var TRIGGER_ON_VALUE = 0.4;
var TRIGGER_OFF_VALUE = 0.15;
/////////////////////////////////////////////////////////////////
//
// distant manipulation
//
@ -44,7 +41,6 @@ var LINE_ENTITY_DIMENSIONS = { x: 1000, y: 1000,z: 1000};
var LINE_LENGTH = 500;
var PICK_MAX_DISTANCE = 500; // max length of pick-ray
/////////////////////////////////////////////////////////////////
//
// near grabbing
//
@ -55,8 +51,8 @@ var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held
var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected
var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things
var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object
var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed
/////////////////////////////////////////////////////////////////
//
// other constants
//
@ -79,6 +75,18 @@ var ACTION_TTL_REFRESH = 5;
var PICKS_PER_SECOND_PER_HAND = 5;
var MSECS_PER_SEC = 1000.0;
var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js
var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js
var DEFAULT_GRABBABLE_DATA = {
grabbable: true,
invertSolidWhileHeld: false
};
var disabledHand ='none';
// states for the state machine
var STATE_OFF = 0;
var STATE_SEARCHING = 1;
@ -92,15 +100,35 @@ var STATE_FAR_GRABBING_NON_COLLIDING = 8;
var STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING = 9;
var STATE_RELEASE = 10;
var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js
var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js
var DEFAULT_GRABBABLE_DATA = {
grabbable: true,
invertSolidWhileHeld: false
};
function stateToName(state) {
switch (state) {
case STATE_OFF:
return "off";
case STATE_SEARCHING:
return "searching";
case STATE_DISTANCE_HOLDING:
return "distance_holding";
case STATE_CONTINUE_DISTANCE_HOLDING:
return "continue_distance_holding";
case STATE_NEAR_GRABBING:
return "near_grabbing";
case STATE_CONTINUE_NEAR_GRABBING:
return "continue_near_grabbing";
case STATE_NEAR_GRABBING_NON_COLLIDING:
return "near_grabbing_non_colliding";
case STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING:
return "continue_near_grabbing_non_colliding";
case STATE_FAR_GRABBING_NON_COLLIDING:
return "far_grabbing_non_colliding";
case STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING:
return "continue_far_grabbing_non_colliding";
case STATE_RELEASE:
return "release";
}
var disabledHand ='none';
return "unknown";
}
function getTag() {
return "grab-" + MyAvatar.sessionUUID;
@ -126,7 +154,7 @@ function entityIsGrabbedByOther(entityID) {
}
function MyController(hand, triggerAction) {
function MyController(hand) {
this.hand = hand;
if (this.hand === RIGHT_HAND) {
this.getHandPosition = MyAvatar.getRightPalmPosition;
@ -138,7 +166,6 @@ function MyController(hand, triggerAction) {
var SPATIAL_CONTROLLERS_PER_PALM = 2;
var TIP_CONTROLLER_OFFSET = 1;
this.triggerAction = triggerAction;
this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand;
this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET;
@ -148,6 +175,7 @@ function MyController(hand, triggerAction) {
this.state = STATE_OFF;
this.pointer = null; // entity-id of line object
this.triggerValue = 0; // rolling average of trigger value
this.rawTriggerValue = 0;
var _this = this;
@ -195,7 +223,7 @@ function MyController(hand, triggerAction) {
this.setState = function(newState) {
if (WANT_DEBUG) {
print("STATE: " + this.state + " --> " + newState);
print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand);
}
this.state = newState;
}
@ -244,12 +272,16 @@ function MyController(hand, triggerAction) {
this.pointer = null;
};
this.updateSmoothedTrigger = function() {
var triggerValue = Controller.getActionValue(this.triggerAction);
this.eitherTrigger = function (value) {
_this.rawTriggerValue = value;
};
this.updateSmoothedTrigger = function () {
var triggerValue = this.rawTriggerValue;
// smooth out trigger value
this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) +
(triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO));
}
};
this.triggerSmoothedSqueezed = function() {
return this.triggerValue > TRIGGER_ON_VALUE;
@ -259,8 +291,8 @@ function MyController(hand, triggerAction) {
return this.triggerValue < TRIGGER_OFF_VALUE;
};
this.triggerSqueezed = function() {
var triggerValue = Controller.getActionValue(this.triggerAction);
this.triggerSqueezed = function() {
var triggerValue = this.rawTriggerValue;
return triggerValue > TRIGGER_ON_VALUE;
};
@ -347,10 +379,11 @@ function MyController(hand, triggerAction) {
}
if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) {
// the hand is very close to the intersected object. go into close-grabbing mode.
if (intersection.properties.collisionsWillMove === 1) {
this.setState(STATE_NEAR_GRABBING);
} else {
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
if (grabbableData.wantsTrigger) {
this.setState(STATE_NEAR_GRABBING_NON_COLLIDING);
} else {
this.setState(STATE_NEAR_GRABBING);
}
} else {
// don't allow two people to distance grab the same object
@ -391,21 +424,22 @@ function MyController(hand, triggerAction) {
}
if (this.grabbedEntity === null) {
return;
} else if (props.locked === 0 && props.collisionsWillMove === 1) {
this.setState(STATE_NEAR_GRABBING);
} else if (props.collisionsWillMove === 0 && grabbableData.wantsTrigger) {
// We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event
}
if (grabbableData.wantsTrigger) {
this.setState(STATE_NEAR_GRABBING_NON_COLLIDING);
} else if (props.locked === 0) {
this.setState(STATE_NEAR_GRABBING);
}
}
};
this.distanceHolding = function() {
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation",
"gravity", "ignoreForCollisions"]);
"gravity", "ignoreForCollisions",
"collisionsWillMove"]);
var now = Date.now();
// add the action and initialize some variables
@ -452,8 +486,9 @@ function MyController(hand, triggerAction) {
}
var handPosition = this.getHandPosition();
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]);
this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);
@ -549,8 +584,14 @@ function MyController(hand, triggerAction) {
this.lineOff();
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity,
["position", "rotation", "gravity", "ignoreForCollisions"]);
["position", "rotation", "gravity",
"ignoreForCollisions", "collisionsWillMove"]);
this.activateEntity(this.grabbedEntity, grabbedProperties);
if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) {
Entities.editEntity(this.grabbedEntity, {
collisionsWillMove: false
});
}
var handRotation = this.getHandRotation();
var handPosition = this.getHandPosition();
@ -579,7 +620,9 @@ function MyController(hand, triggerAction) {
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
relativePosition: this.offsetPosition,
relativeRotation: this.offsetRotation,
ttl: ACTION_TTL
ttl: ACTION_TTL,
kinematic: NEAR_GRABBING_KINEMATIC,
kinematicSetVelocity: true
});
if (this.actionID === NULL_ACTION_ID) {
this.actionID = null;
@ -595,7 +638,7 @@ function MyController(hand, triggerAction) {
}
this.currentHandControllerTipPosition = Controller.getSpatialControlPosition(this.tip);
this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;;
this.currentObjectTime = Date.now();
};
@ -613,7 +656,7 @@ function MyController(hand, triggerAction) {
// of it's actual offset, let's try imparting a velocity which is at a fixed radius
// from the palm.
var handControllerPosition = Controller.getSpatialControlPosition(this.tip);
var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
var now = Date.now();
var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters
@ -631,7 +674,9 @@ function MyController(hand, triggerAction) {
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
relativePosition: this.offsetPosition,
relativeRotation: this.offsetRotation,
ttl: ACTION_TTL
ttl: ACTION_TTL,
kinematic: NEAR_GRABBING_KINEMATIC,
kinematicSetVelocity: true
});
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC);
}
@ -800,12 +845,13 @@ function MyController(hand, triggerAction) {
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
}
this.deactivateEntity(this.grabbedEntity);
// the action will tend to quickly bring an object's velocity to zero. now that
// the action is gone, set the objects velocity to something the holder might expect.
Entities.editEntity(this.grabbedEntity, {
velocity: this.grabbedVelocity
});
this.deactivateEntity(this.grabbedEntity);
this.grabbedVelocity = ZERO_VEC;
this.grabbedEntity = null;
@ -828,6 +874,7 @@ function MyController(hand, triggerAction) {
if (data["refCount"] == 1) {
data["gravity"] = grabbedProperties.gravity;
data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions;
data["collisionsWillMove"] = grabbedProperties.collisionsWillMove;
var whileHeldProperties = {gravity: {x:0, y:0, z:0}};
if (invertSolidWhileHeld) {
whileHeldProperties["ignoreForCollisions"] = ! grabbedProperties.ignoreForCollisions;
@ -845,7 +892,8 @@ function MyController(hand, triggerAction) {
if (data["refCount"] < 1) {
Entities.editEntity(entityID, {
gravity: data["gravity"],
ignoreForCollisions: data["ignoreForCollisions"]
ignoreForCollisions: data["ignoreForCollisions"],
collisionsWillMove: data["collisionsWillMove"]
});
data = null;
}
@ -856,8 +904,16 @@ function MyController(hand, triggerAction) {
};
}
var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK"));
var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK"));
var rightController = new MyController(RIGHT_HAND);
var leftController = new MyController(LEFT_HAND);
var MAPPING_NAME = "com.highfidelity.handControllerGrab";
var mapping = Controller.newMapping(MAPPING_NAME);
mapping.from([Controller.Standard.RB, Controller.Standard.RT]).peek().to(rightController.eitherTrigger);
mapping.from([Controller.Standard.LB, Controller.Standard.LT]).peek().to(leftController.eitherTrigger);
Controller.enableMapping(MAPPING_NAME);
function update() {
rightController.update();
@ -867,6 +923,7 @@ function update() {
function cleanup() {
rightController.cleanup();
leftController.cleanup();
Controller.disableMapping(MAPPING_NAME);
}
Script.scriptEnding.connect(cleanup);

View file

@ -0,0 +1,102 @@
//
// handPosesDebug.js
// examples
//
// Copyright 2015 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
//
function makeSphere(color) {
var SPHERE_SIZE = 0.05;
var sphere = Overlays.addOverlay("sphere", {
position: { x: 0, y: 0, z: 0 },
size: SPHERE_SIZE,
color: color,
alpha: 1.0,
solid: true,
visible: true,
});
return sphere;
}
var NUM_HANDS = 2;
var NUM_SPHERES_PER_HAND = 2;
var LEFT_HAND = 0;
var RIGHT_HAND = 1;
var COLORS = [ { red: 255, green: 0, blue: 0 }, { red: 0, green: 0, blue: 255 } ];
function index(handNum, indexNum) {
return handNum * NUM_HANDS + indexNum;
}
var app = {};
function setup() {
app.spheres = new Array();
for (var h = 0; h < NUM_HANDS; h++) {
for (var s = 0; s < NUM_SPHERES_PER_HAND; s++) {
var i = index(h, s);
app.spheres[i] = makeSphere(COLORS[h]);
print("Added Sphere num " + i + " = " + JSON.stringify(app.spheres[i]));
}
}
}
function updateHand(handNum, deltaTime) {
var pose;
var handName = "right";
if (handNum == LEFT_HAND) {
pose = MyAvatar.getLeftHandPose();
handName = "left";
} else {
pose = MyAvatar.getRightHandPose();
handName = "right";
}
if (pose.valid) {
//print(handName + " hand moving" + JSON.stringify(pose));
Overlays.editOverlay(app.spheres[index(handNum, 0)], {
position: pose.translation,
visible: true,
});
var vpos = Vec3.sum(Vec3.multiply(10 * deltaTime, pose.velocity), pose.translation);
Overlays.editOverlay(app.spheres[index(handNum, 1)], {
position: vpos,
visible: true,
});
} else {
Overlays.editOverlay(app.spheres[index(handNum, 0)], {
visible: false
});
Overlays.editOverlay(app.spheres[index(handNum, 1)], {
visible: false
});
}
}
function update(deltaTime) {
updateHand(LEFT_HAND, deltaTime);
updateHand(RIGHT_HAND, deltaTime);
}
function scriptEnding() {
print("Removing spheres = " + JSON.stringify(app.spheres));
for (var i = 0; i < app.spheres.length; i++) {
Overlays.deleteOverlay(app.spheres[i]);
}
}
setup();
Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding);

View file

@ -32,6 +32,8 @@ var guitarModel = HIFI_PUBLIC_BUCKET + "models/attachments/guitar.fst";
// Load sounds that will be played
var heyManWave = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav");
var chords = new Array();
// Nylon string guitar
chords[1] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw");
@ -56,97 +58,128 @@ var NUM_GUITARS = 3;
var guitarSelector = NUM_CHORDS;
var whichChord = 1;
var leftHanded = true;
var leftHanded = false;
var strumHand, chordHand, strumTrigger, chordTrigger;
if (leftHanded) {
var strumHand = 0;
var chordHand = 1;
strumHand = Controller.Standard.LeftHand;
chordHand = Controller.Standard.RightHand;
strumTrigger = Controller.Standard.LT;
chordTrigger = Controller.Standard.RT;
changeGuitar = Controller.Standard.RB;
chord1 = Controller.Standard.X;
chord2 = Controller.Standard.Y;
chord3 = Controller.Standard.A;
chord4 = Controller.Standard.B;
} else {
var strumHand = 1;
var chordHand = 0;
strumHand = Controller.Standard.RightHand;
chordHand = Controller.Standard.LeftHand;
strumTrigger = Controller.Standard.RT;
chordTrigger = Controller.Standard.LT;
changeGuitar = Controller.Standard.LB;
chord1 = Controller.Standard.DU; // these may not be correct, maybe we should map directly to Hydra??
chord2 = Controller.Standard.DD;
chord3 = Controller.Standard.DL;
chord4 = Controller.Standard.DR;
}
var lastPosition = { x: 0.0,
y: 0.0,
z: 0.0 };
var soundPlaying = false;
var audioInjector = null;
var selectorPressed = false;
var position;
MyAvatar.attach(guitarModel, "Hips", {x: -0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, 90), 1.0);
MyAvatar.attach(guitarModel, "Hips", {x: leftHanded ? -0.2 : 0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, leftHanded ? 75 : -75), 1.0);
function checkHands(deltaTime) {
for (var palm = 0; palm < 2; palm++) {
var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1);
var volume = length(palmVelocity) / 5.0;
var position = Controller.getSpatialControlPosition(palm * 2 + 1);
var myPelvis = MyAvatar.position;
var trigger = Controller.getTriggerValue(strumHand);
var chord = Controller.getTriggerValue(chordHand);
var strumVelocity = Controller.getPoseValue(strumHand).velocity;
var volume = length(strumVelocity) / 5.0;
if (volume > 1.0) volume = 1.0;
if ((chord > 0.1) && soundPlaying.isPlaying) {
// If chord finger trigger pulled, stop current chord
print("stopped sound");
soundPlaying.stop();
if (volume == 0.0) {
volume = 1.0;
}
var strumHandPosition = leftHanded ? MyAvatar.leftHandPosition : MyAvatar.rightHandPosition;
var myPelvis = MyAvatar.position;
var strumming = Controller.getValue(strumTrigger);
var chord = Controller.getValue(chordTrigger);
if (volume > 1.0) volume = 1.0;
if ((chord > 0.1) && audioInjector && audioInjector.isPlaying) {
// If chord finger trigger pulled, stop current chord
print("stopping chord because cord trigger pulled");
audioInjector.stop();
}
// Change guitars if button FWD (5) pressed
if (Controller.getValue(changeGuitar)) {
if (!selectorPressed) {
print("changeGuitar:" + changeGuitar);
guitarSelector += NUM_CHORDS;
if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) {
guitarSelector = 0;
}
print("new guitarBase: " + guitarSelector);
stopAudio(true);
selectorPressed = true;
}
} else {
selectorPressed = false;
}
var BUTTON_COUNT = 6;
if (Controller.getValue(chord1)) {
whichChord = 1;
stopAudio(true);
} else if (Controller.getValue(chord2)) {
whichChord = 2;
stopAudio(true);
} else if (Controller.getValue(chord3)) {
whichChord = 3;
stopAudio(true);
} else if (Controller.getValue(chord4)) {
whichChord = 4;
stopAudio(true);
}
// Change guitars if button FWD (5) pressed
if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 5)) {
if (!selectorPressed) {
guitarSelector += NUM_CHORDS;
if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) {
guitarSelector = 0;
}
selectorPressed = true;
}
} else {
selectorPressed = false;
}
var STRUM_HEIGHT_ABOVE_PELVIS = 0.10;
var strummingHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS;
if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 1)) {
whichChord = 1;
} else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 2)) {
whichChord = 2;
} else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 3)) {
whichChord = 3;
} else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 4)) {
whichChord = 4;
}
var strumNowAbove = strumHandPosition.y > strummingHeight;
var strumNowBelow = strumHandPosition.y <= strummingHeight;
var strumWasAbove = lastPosition.y > strummingHeight;
var strumWasBelow = lastPosition.y <= strummingHeight;
var strumUp = strumNowAbove && strumWasBelow;
var strumDown = strumNowBelow && strumWasAbove;
if (palm == strumHand) {
if ((strumUp || strumDown) && (strumming > 0.1)) {
// If hand passes downward or upward through 'strings', and finger trigger pulled, play
playChord(strumHandPosition, volume);
}
lastPosition = strumHandPosition;
}
var STRUM_HEIGHT_ABOVE_PELVIS = 0.10;
var strumTriggerHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS;
//printVector(position);
if ( ( ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) ||
((position.y > strumTriggerHeight) && (lastPosition.y <= strumTriggerHeight)) ) && (trigger > 0.1) ){
// If hand passes downward or upward through 'strings', and finger trigger pulled, play
playChord(position, volume);
}
lastPosition = Controller.getSpatialControlPosition(palm * 2 + 1);
}
function stopAudio(killInjector) {
if (audioInjector && audioInjector.isPlaying) {
print("stopped sound");
audioInjector.stop();
}
if (killInjector) {
audioInjector = null;
}
}
function playChord(position, volume) {
if (soundPlaying.isPlaying) {
print("stopped sound");
soundPlaying.stop();
stopAudio();
print("Played sound: " + whichChord + " at volume " + volume);
if (!audioInjector) {
var index = guitarSelector + whichChord;
var chord = chords[guitarSelector + whichChord];
audioInjector = Audio.playSound(chord, { position: position, volume: volume });
} else {
audioInjector.restart();
}
print("Played sound: " + whichChord + " at volume " + options.volume);
if (!soundPlaying) {
soundPlaying = Audio.playSound(chords[guitarSelector + whichChord], {
position: position,
volume: volume
});
} else {
soundPlaying.restart();
}
}
function keyPressEvent(event) {
@ -154,15 +187,19 @@ function keyPressEvent(event) {
keyVolume = 0.4;
if (event.text == "1") {
whichChord = 1;
stopAudio(true);
playChord(MyAvatar.position, keyVolume);
} else if (event.text == "2") {
whichChord = 2;
stopAudio(true);
playChord(MyAvatar.position, keyVolume);
} else if (event.text == "3") {
whichChord = 3;
stopAudio(true);
playChord(MyAvatar.position, keyVolume);
} else if (event.text == "4") {
whichChord = 4;
stopAudio(true);
playChord(MyAvatar.position, keyVolume);
}
}
@ -170,6 +207,7 @@ function keyPressEvent(event) {
function scriptEnding() {
MyAvatar.detachOne(guitarModel);
}
// Connect a call back that happens every frame
Script.update.connect(checkHands);
Script.scriptEnding.connect(scriptEnding);

View file

@ -43,7 +43,8 @@ strokeSpeed[1] = 0.0;
function checkSticks(deltaTime) {
for (var palm = 0; palm < 2; palm++) {
var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1);
var handPose = (palm == 0) ? MyAvatar.leftHandPose : MyAvatar.rightHandPose;
var palmVelocity = handPose.velocity;
var speed = length(palmVelocity);
const TRIGGER_SPEED = 0.30; // Lower this value to let you 'drum' more gently
@ -64,7 +65,7 @@ function checkSticks(deltaTime) {
if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) {
state[palm] = 0;
var options = { position: Controller.getSpatialControlPosition(palm * 2 + 1) };
var options = { position: handPose.translation };
if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; }
options.volume = strokeSpeed[palm];

View file

@ -130,29 +130,38 @@ function Hand(name, palm, tip, forwardButton, button3, trigger) {
this.trigger = trigger;
this.holdingFrisbee = false;
this.entity = false;
this.palmPosition = function() { return Controller.getSpatialControlPosition(this.palm); }
this.palmPosition = function () {
return this.palm == LEFT_PALM ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation;
};
this.grabButtonPressed = function() {
return (
Controller.isButtonPressed(this.forwardButton) ||
Controller.isButtonPressed(this.button3) ||
Controller.getTriggerValue(this.trigger) > 0.5
Controller.getValue(this.forwardButton) ||
Controller.getValue(this.button3) ||
Controller.getValue(this.trigger) > 0.5
)
};
this.holdPosition = function() { return this.palm == LEFT_PALM ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(); };
this.holdPosition = function () {
return this.palm == LEFT_PALM ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation;
};
this.holdRotation = function() {
var q = Controller.getSpatialControlRawRotation(this.palm);
var q = (this.palm == LEFT_PALM) ? Controller.getPoseValue(Controller.Standard.leftHand).rotation
: Controller.getPoseValue(Controller.Standard.rightHand).rotation;
q = Quat.multiply(MyAvatar.orientation, q);
return {x: q.x, y: q.y, z: q.z, w: q.w};
};
this.tipVelocity = function() { return Controller.getSpatialControlVelocity(this.tip); };
this.tipVelocity = function () {
return this.tip == LEFT_TIP ? MyAvatar.leftHandTipPose.velocity : MyAvatar.rightHandTipPose.velocity;
};
}
function MouseControl(button) {
this.button = button;
}
var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, LEFT_BUTTON_FWD, LEFT_BUTTON_3, 0);
var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, RIGHT_BUTTON_FWD, RIGHT_BUTTON_3, 1);
var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, Controller.Standard.LB, Controller.Standard.LeftPrimaryThumb, Controller.Standard.LT);
var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, Controller.Standard.RB, Controller.Standard.RightPrimaryThumb, Controller.Standard.RT);
var leftMouseControl = new MouseControl("LEFT");
var middleMouseControl = new MouseControl("MIDDLE");
@ -302,12 +311,7 @@ function initToolBar() {
}
function hydraCheck() {
var numberOfButtons = Controller.getNumberOfButtons();
var numberOfTriggers = Controller.getNumberOfTriggers();
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2);
return true;//hydrasConnected;
return Controller.Hardware.Hydra !== undefined;
}
function checkController(deltaTime) {

View file

@ -15,24 +15,64 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// FIXME kickback functionality was removed because the joint setting interface in
// MyAvatar has apparently changed, breaking it.
Script.include("../../libraries/utils.js");
Script.include("../../libraries/constants.js");
Script.include("../../libraries/toolBars.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var RED = { red: 255, green: 0, blue: 0 };
var LASER_WIDTH = 2;
var POSE_CONTROLS = [ Controller.Standard.LeftHand, Controller.Standard.RightHand ];
var TRIGGER_CONTROLS = [ Controller.Standard.LT, Controller.Standard.RT ];
var MIN_THROWER_DELAY = 1000;
var MAX_THROWER_DELAY = 1000;
var RELOAD_INTERVAL = 5;
var GUN_MODEL = HIFI_PUBLIC_BUCKET + "cozza13/gun/m1911-handgun+1.fbx?v=4";
var BULLET_VELOCITY = 10.0;
var GUN_OFFSETS = [ {
x: -0.04,
y: 0.26,
z: 0.04
}, {
x: 0.04,
y: 0.26,
z: 0.04
} ];
var GUN_ORIENTATIONS = [ Quat.fromPitchYawRollDegrees(0, 90, 90), Quat.fromPitchYawRollDegrees(0, -90, 270) ];
var BARREL_OFFSETS = [ {
x: -0.12,
y: 0.12,
z: 0.04
}, {
x: 0.12,
y: 0.12,
z: 0.04
} ];
var mapping = Controller.newMapping();
var validPoses = [ false, false ];
var barrelVectors = [ 0, 0 ];
var barrelTips = [ 0, 0 ];
var pointer = [];
pointer.push(Overlays.addOverlay("line3d", {
start: { x: 0, y: 0, z: 0 },
end: { x: 0, y: 0, z: 0 },
color: RED,
start: ZERO_VECTOR,
end: ZERO_VECTOR,
color: COLORS.RED,
alpha: 1,
visible: true,
lineWidth: LASER_WIDTH
}));
pointer.push(Overlays.addOverlay("line3d", {
start: { x: 0, y: 0, z: 0 },
end: { x: 0, y: 0, z: 0 },
color: RED,
start: ZERO_VECTOR,
end: ZERO_VECTOR,
color: COLORS.RED,
alpha: 1,
visible: true,
lineWidth: LASER_WIDTH
@ -42,135 +82,116 @@ function getRandomFloat(min, max) {
return Math.random() * (max - min) + min;
}
var lastX = 0;
var lastY = 0;
var yawFromMouse = 0;
var pitchFromMouse = 0;
var isMouseDown = false;
var MIN_THROWER_DELAY = 1000;
var MAX_THROWER_DELAY = 1000;
var LEFT_BUTTON_3 = 3;
var RELOAD_INTERVAL = 5;
var KICKBACK_ANGLE = 15;
var elbowKickAngle = 0.0;
var rotationBeforeKickback;
var showScore = false;
// Load some sound to use for loading and firing
// Load some sound to use for loading and firing
var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw");
var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw");
var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw");
var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw");
var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw");
var gunModel = "https://s3.amazonaws.com/hifi-public/cozza13/gun/m1911-handgun+1.fbx?v=4";
var audioOptions = {
volume: 0.9
volume: 0.9
}
var shotsFired = 0;
var shotTime = new Date();
var activeControllers = 0;
// initialize our controller triggers
var triggerPulled = new Array();
var numberOfTriggers = Controller.getNumberOfTriggers();
for (t = 0; t < numberOfTriggers; t++) {
triggerPulled[t] = false;
}
var isLaunchButtonPressed = false;
var score = 0;
var shotTime = new Date();
var isLaunchButtonPressed = false;
var score = 0;
var bulletID = false;
var targetID = false;
// Create overlay buttons and reticle
// Create overlay buttons and reticle
var BUTTON_SIZE = 32;
var PADDING = 3;
var NUM_BUTTONS = 3;
var screenSize = Controller.getViewportDimensions();
var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2;
Script.include(["../../libraries/toolBars.js"]);
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function (screenSize) {
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function(screenSize) {
return {
x: startX,
y: (screenSize.y - (BUTTON_SIZE + PADDING)),
};
});
var reticle = Overlays.addOverlay("image", {
x: screenSize.x / 2 - (BUTTON_SIZE / 2),
y: screenSize.y / 2 - (BUTTON_SIZE / 2),
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/crosshairs.svg",
alpha: 1
});
var offButton = toolBar.addOverlay("image", {
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
alpha: 1
});
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
alpha: 1
});
startX += BUTTON_SIZE + PADDING;
var platformButton = toolBar.addOverlay("image", {
x: startX,
y: screenSize.y - (BUTTON_SIZE + PADDING),
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg",
alpha: 1
});
x: startX,
y: screenSize.y - (BUTTON_SIZE + PADDING),
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg",
alpha: 1
});
startX += BUTTON_SIZE + PADDING;
var gridButton = toolBar.addOverlay("image", {
x: startX,
y: screenSize.y - (BUTTON_SIZE + PADDING),
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg",
alpha: 1
});
x: startX,
y: screenSize.y - (BUTTON_SIZE + PADDING),
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg",
alpha: 1
});
if (showScore) {
var text = Overlays.addOverlay("text", {
x: screenSize.x / 2 - 100,
y: screenSize.y / 2 - 50,
width: 150,
height: 50,
color: { red: 0, green: 0, blue: 0},
textColor: { red: 255, green: 0, blue: 0},
topMargin: 4,
leftMargin: 4,
text: "Score: " + score
});
x: screenSize.x / 2 - 100,
y: screenSize.y / 2 - 50,
width: 150,
height: 50,
color: {
red: 0,
green: 0,
blue: 0
},
textColor: {
red: 255,
green: 0,
blue: 0
},
topMargin: 4,
leftMargin: 4,
text: "Score: " + score
});
}
var BULLET_VELOCITY = 10.0;
function entityCollisionWithEntity(entity1, entity2, collision) {
if (entity2 === targetID) {
score++;
if (showScore) {
Overlays.editOverlay(text, { text: "Score: " + score } );
Overlays.editOverlay(text, {
text: "Score: " + score
});
}
// We will delete the bullet and target in 1/2 sec, but for now we can see them bounce!
// We will delete the bullet and target in 1/2 sec, but for now we can
// see them bounce!
Script.setTimeout(deleteBulletAndTarget, 500);
// Turn the target and the bullet white
Entities.editEntity(entity1, { color: { red: 255, green: 255, blue: 255 }});
Entities.editEntity(entity2, { color: { red: 255, green: 255, blue: 255 }});
Entities.editEntity(entity1, {
color: {
red: 255,
green: 255,
blue: 255
}
});
Entities.editEntity(entity2, {
color: {
red: 255,
green: 255,
blue: 255
}
});
// play the sound near the camera so the shooter can hear it
audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
@ -188,41 +209,45 @@ function shootBullet(position, velocity, grenade) {
var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity;
var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE;
var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY;
var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY;
bulletID = Entities.addEntity({
type: "Sphere",
position: position,
dimensions: {
x: bSize,
y: bSize,
z: bSize
},
color: {
red: 0,
green: 0,
blue: 0
},
velocity: bVelocity,
lifetime: BULLET_LIFETIME,
gravity: {
x: 0,
y: bGravity,
z: 0
},
damping: 0.01,
density: 8000,
ignoreCollisions: false,
collisionsWillMove: true
});
bulletID = Entities.addEntity(
{ type: "Sphere",
position: position,
dimensions: { x: bSize, y: bSize, z: bSize },
color: { red: 0, green: 0, blue: 0 },
velocity: bVelocity,
lifetime: BULLET_LIFETIME,
gravity: { x: 0, y: bGravity, z: 0 },
damping: 0.01,
density: 8000,
ignoreCollisions: false,
collisionsWillMove: true
});
Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity);
// Play firing sounds
audioOptions.position = position;
// Play firing sounds
audioOptions.position = position;
Audio.playSound(fireSound, audioOptions);
shotsFired++;
if ((shotsFired % RELOAD_INTERVAL) == 0) {
Audio.playSound(loadSound, audioOptions);
}
// Kickback the arm
if (elbowKickAngle > 0.0) {
MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback);
}
rotationBeforeKickback = MyAvatar.getJointRotation("LeftForeArm");
var armRotation = MyAvatar.getJointRotation("LeftForeArm");
armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, KICKBACK_ANGLE));
MyAvatar.setJointData("LeftForeArm", armRotation);
elbowKickAngle = KICKBACK_ANGLE;
}
function shootTarget() {
var TARGET_SIZE = 0.50;
var TARGET_GRAVITY = 0.0;
@ -232,95 +257,152 @@ function shootTarget() {
var DISTANCE_TO_LAUNCH_FROM = 5.0;
var ANGLE_RANGE_FOR_LAUNCH = 20.0;
var camera = Camera.getPosition();
var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 });
var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), {
x: 0,
y: 1,
z: 0
});
targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection);
var forwardVector = Quat.getFront(targetDirection);
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM));
var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY);
velocity.y += TARGET_UP_VELOCITY;
targetID = Entities.addEntity(
{ type: "Box",
position: newPosition,
dimensions: { x: TARGET_SIZE * (0.5 + Math.random()), y: TARGET_SIZE * (0.5 + Math.random()), z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 },
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
velocity: velocity,
gravity: { x: 0, y: TARGET_GRAVITY, z: 0 },
lifetime: TARGET_LIFETIME,
rotation: Camera.getOrientation(),
damping: 0.1,
density: 100.0,
collisionsWillMove: true });
targetID = Entities.addEntity({
type: "Box",
position: newPosition,
dimensions: {
x: TARGET_SIZE * (0.5 + Math.random()),
y: TARGET_SIZE * (0.5 + Math.random()),
z: TARGET_SIZE * (0.5 + Math.random()) / 4.0
},
color: {
red: Math.random() * 255,
green: Math.random() * 255,
blue: Math.random() * 255
},
velocity: velocity,
gravity: {
x: 0,
y: TARGET_GRAVITY,
z: 0
},
lifetime: TARGET_LIFETIME,
rotation: Camera.getOrientation(),
damping: 0.1,
density: 100.0,
collisionsWillMove: true
});
// Record start time
// Record start time
shotTime = new Date();
// Play target shoot sound
audioOptions.position = newPosition;
audioOptions.position = newPosition;
Audio.playSound(targetLaunchSound, audioOptions);
}
function makeGrid(type, scale, size) {
var separation = scale * 2;
var separation = scale * 2;
var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));
var x, y, z;
var GRID_LIFE = 60.0;
var dimensions;
var GRID_LIFE = 60.0;
var dimensions;
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
for (z = 0; z < size; z++) {
dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 };
Entities.addEntity(
{ type: type,
position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation },
dimensions: dimensions,
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
velocity: { x: 0, y: 0, z: 0 },
gravity: { x: 0, y: 0, z: 0 },
lifetime: GRID_LIFE,
rotation: Camera.getOrientation(),
damping: 0.1,
density: 100.0,
collisionsWillMove: true });
dimensions = {
x: separation / 2.0 * (0.5 + Math.random()),
y: separation / 2.0 * (0.5 + Math.random()),
z: separation / 2.0 * (0.5 + Math.random()) / 4.0
};
Entities.addEntity({
type: type,
position: {
x: pos.x + x * separation,
y: pos.y + y * separation,
z: pos.z + z * separation
},
dimensions: dimensions,
color: {
red: Math.random() * 255,
green: Math.random() * 255,
blue: Math.random() * 255
},
velocity: {
x: 0,
y: 0,
z: 0
},
gravity: {
x: 0,
y: 0,
z: 0
},
lifetime: GRID_LIFE,
rotation: Camera.getOrientation(),
damping: 0.1,
density: 100.0,
collisionsWillMove: true
});
}
}
}
}
function makePlatform(gravity, scale, size) {
var separation = scale * 2;
var separation = scale * 2;
var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));
pos.y -= separation * size;
var x, y, z;
var TARGET_LIFE = 60.0;
var TARGET_LIFE = 60.0;
var INITIAL_GAP = 0.5;
var dimensions;
var dimensions;
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
for (z = 0; z < size; z++) {
dimensions = { x: separation/2.0, y: separation, z: separation/2.0 };
dimensions = {
x: separation / 2.0,
y: separation,
z: separation / 2.0
};
Entities.addEntity(
{ type: "Box",
position: { x: pos.x - (separation * size / 2.0) + x * separation,
y: pos.y + y * (separation + INITIAL_GAP),
z: pos.z - (separation * size / 2.0) + z * separation },
dimensions: dimensions,
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
velocity: { x: 0, y: 0.05, z: 0 },
gravity: { x: 0, y: gravity, z: 0 },
lifetime: TARGET_LIFE,
damping: 0.1,
density: 100.0,
collisionsWillMove: true });
Entities.addEntity({
type: "Box",
position: {
x: pos.x - (separation * size / 2.0) + x * separation,
y: pos.y + y * (separation + INITIAL_GAP),
z: pos.z - (separation * size / 2.0) + z * separation
},
dimensions: dimensions,
color: {
red: Math.random() * 255,
green: Math.random() * 255,
blue: Math.random() * 255
},
velocity: {
x: 0,
y: 0.05,
z: 0
},
gravity: {
x: 0,
y: gravity,
z: 0
},
lifetime: TARGET_LIFE,
damping: 0.1,
density: 100.0,
collisionsWillMove: true
});
}
}
}
@ -328,9 +410,21 @@ function makePlatform(gravity, scale, size) {
// Make a floor for this stuff to fall onto
Entities.addEntity({
type: "Box",
position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z },
dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size },
color: { red: 100, green: 100, blue: 100 },
position: {
x: pos.x,
y: pos.y - separation / 2.0,
z: pos.z
},
dimensions: {
x: 2.0 * separation * size,
y: separation / 2.0,
z: 2.0 * separation * size
},
color: {
red: 100,
green: 100,
blue: 100
},
lifetime: TARGET_LIFE
});
@ -340,153 +434,79 @@ function keyPressEvent(event) {
// if our tools are off, then don't do anything
if (event.text == "t") {
var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY;
Script.setTimeout(shootTarget, time);
Script.setTimeout(shootTarget, time);
} else if ((event.text == ".") || (event.text == "SPACE")) {
shootFromMouse(false);
} else if (event.text == ",") {
shootFromMouse(true);
} else if (event.text == "r") {
playLoadSound();
} else if (event.text == "s") {
// Hit this key to dump a posture from hydra to log
Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm"));
Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm"));
Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand"));
}
}
function playLoadSound() {
audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
audioOptions.position = MyAvatar.leftHandPose.translation;
Audio.playSound(loadSound, audioOptions);
// Raise arm to firing posture
takeFiringPose();
}
function clearPose() {
MyAvatar.clearJointData("LeftForeArm");
MyAvatar.clearJointData("LeftArm");
MyAvatar.clearJointData("LeftHand");
}
function deleteBulletAndTarget() {
Entities.deleteEntity(bulletID);
Entities.deleteEntity(targetID);
bulletID = false;
targetID = false;
bulletID = false;
targetID = false;
}
function takeFiringPose() {
clearPose();
if (Controller.getNumberOfSpatialControls() == 0) {
MyAvatar.setJointData("LeftForeArm", {x: -0.251919, y: -0.0415449, z: 0.499487, w: 0.827843});
MyAvatar.setJointData("LeftArm", { x: 0.470196, y: -0.132559, z: 0.494033, w: 0.719219});
MyAvatar.setJointData("LeftHand", { x: -0.0104815, y: -0.110551, z: -0.352111, w: 0.929333});
}
}
MyAvatar.attach(gunModel, "RightHand", {x:0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, -85, 79), 0.40);
MyAvatar.attach(gunModel, "LeftHand", {x:-0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, 85, -79), 0.40);
// Give a bit of time to load before playing sound
Script.setTimeout(playLoadSound, 2000);
function update(deltaTime) {
if (activeControllers == 0) {
if (Controller.getNumberOfSpatialControls() > 0) {
activeControllers = Controller.getNumberOfSpatialControls();
clearPose();
}
}
// FIXME we should also expose MyAvatar.handPoses[2], MyAvatar.tipPoses[2]
var tipPoses = [ MyAvatar.leftHandTipPose, MyAvatar.rightHandTipPose ];
var KICKBACK_DECAY_RATE = 0.125;
if (elbowKickAngle > 0.0) {
if (elbowKickAngle > 0.5) {
var newAngle = elbowKickAngle * KICKBACK_DECAY_RATE;
elbowKickAngle -= newAngle;
var armRotation = MyAvatar.getJointRotation("LeftForeArm");
armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, -newAngle));
MyAvatar.setJointData("LeftForeArm", armRotation);
} else {
MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback);
if (Controller.getNumberOfSpatialControls() > 0) {
clearPose();
}
elbowKickAngle = 0.0;
}
}
// check for trigger press
var numberOfTriggers = 2;
var controllersPerTrigger = 2;
if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
for (var t = 0; t < 2; t++) {
var shootABullet = false;
var triggerValue = Controller.getTriggerValue(t);
if (triggerPulled[t]) {
// must release to at least 0.1
if (triggerValue < 0.1) {
triggerPulled[t] = false; // unpulled
}
} else {
// must pull to at least
if (triggerValue > 0.5) {
triggerPulled[t] = true; // pulled
shootABullet = true;
}
}
var palmController = t * controllersPerTrigger;
var palmPosition = Controller.getSpatialControlPosition(palmController);
var fingerTipController = palmController + 1;
var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController);
var laserTip = Vec3.sum(Vec3.multiply(100.0, Vec3.subtract(fingerTipPosition, palmPosition)), palmPosition);
// Update Lasers
Overlays.editOverlay(pointer[t], {
start: palmPosition,
end: laserTip,
alpha: 1
for (var side = 0; side < 2; side++) {
// First check if the controller is valid
var controllerPose = Controller.getPoseValue(POSE_CONTROLS[side]);
validPoses[side] = controllerPose.valid;
if (!controllerPose.valid) {
Overlays.editOverlay(pointer[side], {
visible: false
});
if (shootABullet) {
var palmToFingerTipVector =
{ x: (fingerTipPosition.x - palmPosition.x),
y: (fingerTipPosition.y - palmPosition.y),
z: (fingerTipPosition.z - palmPosition.z) };
// just off the front of the finger tip
var position = { x: fingerTipPosition.x + palmToFingerTipVector.x/2,
y: fingerTipPosition.y + palmToFingerTipVector.y/2,
z: fingerTipPosition.z + palmToFingerTipVector.z/2};
var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(palmToFingerTipVector));
shootBullet(position, velocity, false);
}
continue;
}
// Need to adjust the laser
var tipPose = tipPoses[side];
var handRotation = tipPoses[side].rotation;
var barrelOffset = Vec3.multiplyQbyV(handRotation, BARREL_OFFSETS[side]);
barrelTips[side] = Vec3.sum(tipPose.translation, barrelOffset);
barrelVectors[side] = Vec3.multiplyQbyV(handRotation, {
x: 0,
y: 1,
z: 0
});
var laserTip = Vec3.sum(Vec3.multiply(100.0, barrelVectors[side]), barrelTips[side]);
// Update Lasers
Overlays.editOverlay(pointer[side], {
start: barrelTips[side],
end: laserTip,
alpha: 1,
visible: true
});
}
}
function shootFromMouse(grenade) {
var DISTANCE_FROM_CAMERA = 1.0;
var camera = Camera.getPosition();
var forwardVector = Quat.getFront(Camera.getOrientation());
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));
var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY);
shootBullet(newPosition, velocity, grenade);
}
function mouseReleaseEvent(event) {
// position
isMouseDown = false;
function triggerChanged(side, value) {
var pressed = (value != 0);
if (pressed) {
var position = barrelTips[side];
var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(barrelVectors[side]));
shootBullet(position, velocity, false);
}
}
function mousePressEvent(event) {
var clickedText = false;
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
var clickedOverlay = Overlays.getOverlayAtPoint({
x: event.x,
y: event.y
});
if (clickedOverlay == offButton) {
Script.stop();
} else if (clickedOverlay == platformButton) {
@ -494,25 +514,37 @@ function mousePressEvent(event) {
makePlatform(-9.8, 1.0, platformSize);
} else if (clickedOverlay == gridButton) {
makeGrid("Box", 1.0, 3);
}
}
}
function scriptEnding() {
Overlays.deleteOverlay(reticle);
mapping.disable();
toolBar.cleanup();
Overlays.deleteOverlay(pointer[0]);
Overlays.deleteOverlay(pointer[1]);
for (var i = 0; i < pointer.length; ++i) {
Overlays.deleteOverlay(pointer[i]);
}
Overlays.deleteOverlay(text);
MyAvatar.detachOne(gunModel);
MyAvatar.detachOne(gunModel);
MyAvatar.detachOne(GUN_MODEL);
MyAvatar.detachOne(GUN_MODEL);
clearPose();
}
MyAvatar.attach(GUN_MODEL, "LeftHand", GUN_OFFSETS[0], GUN_ORIENTATIONS[0], 0.40);
MyAvatar.attach(GUN_MODEL, "RightHand", GUN_OFFSETS[1], GUN_ORIENTATIONS[1], 0.40);
// Give a bit of time to load before playing sound
Script.setTimeout(playLoadSound, 2000);
mapping.from(Controller.Standard.LT).hysteresis(0.1, 0.5).to(function(value) {
triggerChanged(0, value);
});
mapping.from(Controller.Standard.RT).hysteresis(0.1, 0.5).to(function(value) {
triggerChanged(1, value);
});
mapping.enable();
Script.scriptEnding.connect(scriptEnding);
Script.update.connect(update);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mousePressEvent.connect(mousePressEvent);
Controller.keyPressEvent.connect(keyPressEvent);

View file

@ -1,303 +0,0 @@
//
// hydraMove.js
// examples
//
// Created by Brad Hefta-Gaub on February 10, 2014
// Updated by Philip Rosedale on September 8, 2014
//
// Copyright 2014 High Fidelity, Inc.
//
// This is an example script that demonstrates use of the Controller and MyAvatar classes to implement
// avatar flying through the hydra/controller joysticks
//
// The joysticks (on hydra) will drive the avatar much like a playstation controller.
//
// Pressing the '4' or the 'FWD' button and moving/banking the hand will allow you to move and fly.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var damping = 0.9;
var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z };
var joysticksCaptured = false;
var THRUST_CONTROLLER = 0;
var VIEW_CONTROLLER = 1;
var INITIAL_THRUST_MULTIPLIER = 1.0;
var THRUST_INCREASE_RATE = 1.05;
var MAX_THRUST_MULTIPLIER = 75.0;
var thrustMultiplier = INITIAL_THRUST_MULTIPLIER;
var grabDelta = { x: 0, y: 0, z: 0};
var grabStartPosition = { x: 0, y: 0, z: 0};
var grabDeltaVelocity = { x: 0, y: 0, z: 0};
var grabStartRotation = { x: 0, y: 0, z: 0, w: 1};
var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1};
var grabbingWithRightHand = false;
var wasGrabbingWithRightHand = false;
var grabbingWithLeftHand = false;
var wasGrabbingWithLeftHand = false;
var EPSILON = 0.000001;
var velocity = { x: 0, y: 0, z: 0};
var THRUST_MAG_UP = 100.0;
var THRUST_MAG_DOWN = 100.0;
var THRUST_MAG_FWD = 150.0;
var THRUST_MAG_BACK = 100.0;
var THRUST_MAG_LATERAL = 150.0;
var THRUST_JUMP = 120.0;
var YAW_MAG = 100.0;
var PITCH_MAG = 100.0;
var THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
var JOYSTICK_YAW_MAG = YAW_MAG;
var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5;
var LEFT_PALM = 0;
var LEFT_BUTTON_4 = 4;
var LEFT_BUTTON_FWD = 5;
var RIGHT_PALM = 2;
var RIGHT_BUTTON_4 = 10;
var RIGHT_BUTTON_FWD = 11;
function printVector(text, v, decimals) {
print(text + " " + v.x.toFixed(decimals) + ", " + v.y.toFixed(decimals) + ", " + v.z.toFixed(decimals));
}
var debug = false;
var RED_COLOR = { red: 255, green: 0, blue: 0 };
var GRAY_COLOR = { red: 25, green: 25, blue: 25 };
var defaultPosition = { x: 0, y: 0, z: 0};
var RADIUS = 0.05;
var greenSphere = -1;
var redSphere = -1;
function createDebugOverlay() {
if (greenSphere == -1) {
greenSphere = Overlays.addOverlay("sphere", {
position: defaultPosition,
size: RADIUS,
color: GRAY_COLOR,
alpha: 0.75,
visible: true,
solid: true,
anchor: "MyAvatar"
});
redSphere = Overlays.addOverlay("sphere", {
position: defaultPosition,
size: RADIUS,
color: RED_COLOR,
alpha: 0.5,
visible: true,
solid: true,
anchor: "MyAvatar"
});
}
}
function destroyDebugOverlay() {
if (greenSphere != -1) {
Overlays.deleteOverlay(greenSphere);
Overlays.deleteOverlay(redSphere);
greenSphere = -1;
redSphere = -1;
}
}
function displayDebug() {
if (!(grabbingWithRightHand || grabbingWithLeftHand)) {
if (greenSphere != -1) {
destroyDebugOverlay();
}
} else {
// update debug indicator
if (greenSphere == -1) {
createDebugOverlay();
}
var displayOffset = { x:0, y:0.5, z:-0.5 };
Overlays.editOverlay(greenSphere, { position: Vec3.sum(grabStartPosition, displayOffset) } );
Overlays.editOverlay(redSphere, { position: Vec3.sum(Vec3.sum(grabStartPosition, grabDelta), displayOffset), size: RADIUS + (0.25 * Vec3.length(grabDelta)) } );
}
}
function getJoystickPosition(palm) {
// returns CONTROLLER_ID position in avatar local frame
var invRotation = Quat.inverse(MyAvatar.orientation);
var palmWorld = Controller.getSpatialControlPosition(palm);
var palmRelative = Vec3.subtract(palmWorld, MyAvatar.position);
var palmLocal = Vec3.multiplyQbyV(invRotation, palmRelative);
return palmLocal;
}
// Used by handleGrabBehavior() for managing the grab position changes
function getAndResetGrabDelta() {
var HAND_GRAB_SCALE_DISTANCE = 2.0;
var delta = Vec3.multiply(grabDelta, (MyAvatar.scale * HAND_GRAB_SCALE_DISTANCE));
grabDelta = { x: 0, y: 0, z: 0};
var avatarRotation = MyAvatar.orientation;
var result = Vec3.multiplyQbyV(avatarRotation, Vec3.multiply(delta, -1));
return result;
}
function getGrabRotation() {
var quatDiff = Quat.multiply(grabCurrentRotation, Quat.inverse(grabStartRotation));
return quatDiff;
}
// When move button is pressed, process results
function handleGrabBehavior(deltaTime) {
// check for and handle grab behaviors
grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_4);
grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_4);
stoppedGrabbingWithLeftHand = false;
stoppedGrabbingWithRightHand = false;
if (grabbingWithRightHand && !wasGrabbingWithRightHand) {
// Just starting grab, capture starting rotation
grabStartRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
grabStartPosition = getJoystickPosition(RIGHT_PALM);
if (debug) printVector("start position", grabStartPosition, 3);
}
if (grabbingWithRightHand) {
grabDelta = Vec3.subtract(getJoystickPosition(RIGHT_PALM), grabStartPosition);
grabCurrentRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
}
if (!grabbingWithRightHand && wasGrabbingWithRightHand) {
// Just ending grab, capture velocity
grabDeltaVelocity = Controller.getSpatialControlVelocity(RIGHT_PALM);
stoppedGrabbingWithRightHand = true;
}
if (grabbingWithLeftHand && !wasGrabbingWithLeftHand) {
// Just starting grab, capture starting rotation
grabStartRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
grabStartPosition = getJoystickPosition(LEFT_PALM);
if (debug) printVector("start position", grabStartPosition, 3);
}
if (grabbingWithLeftHand) {
grabDelta = Vec3.subtract(getJoystickPosition(LEFT_PALM), grabStartPosition);
grabCurrentRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
}
if (!grabbingWithLeftHand && wasGrabbingWithLeftHand) {
// Just ending grab, capture velocity
grabDeltaVelocity = Controller.getSpatialControlVelocity(LEFT_PALM);
stoppedGrabbingWithLeftHand = true;
}
grabbing = grabbingWithRightHand || grabbingWithLeftHand;
stoppedGrabbing = stoppedGrabbingWithRightHand || stoppedGrabbingWithLeftHand;
if (grabbing) {
var headOrientation = MyAvatar.headOrientation;
var front = Quat.getFront(headOrientation);
var right = Quat.getRight(headOrientation);
var up = Quat.getUp(headOrientation);
if (debug) {
printVector("grabDelta: ", grabDelta, 3);
}
var thrust = Vec3.multiply(grabDelta, Math.abs(Vec3.length(grabDelta)));
var THRUST_GRAB_SCALING = 100000.0;
var thrustFront = Vec3.multiply(front, MyAvatar.scale * -thrust.z * THRUST_GRAB_SCALING * deltaTime);
MyAvatar.addThrust(thrustFront);
var thrustRight = Vec3.multiply(right, MyAvatar.scale * thrust.x * THRUST_GRAB_SCALING * deltaTime);
MyAvatar.addThrust(thrustRight);
var thrustUp = Vec3.multiply(up, MyAvatar.scale * thrust.y * THRUST_GRAB_SCALING * deltaTime);
MyAvatar.addThrust(thrustUp);
// add some rotation...
var deltaRotation = getGrabRotation();
var PITCH_SCALING = 2.5;
var PITCH_DEAD_ZONE = 2.0;
var YAW_SCALING = 2.5;
var ROLL_SCALING = 2.0;
var euler = Quat.safeEulerAngles(deltaRotation);
// Adjust body yaw by roll from controller
var orientation = Quat.multiply(Quat.angleAxis(((euler.y * YAW_SCALING) +
(euler.z * ROLL_SCALING)) * deltaTime, {x:0, y: 1, z:0}), MyAvatar.orientation);
MyAvatar.orientation = orientation;
// Adjust head pitch from controller
var pitch = 0.0;
if (Math.abs(euler.x) > PITCH_DEAD_ZONE) {
pitch = (euler.x < 0.0) ? (euler.x + PITCH_DEAD_ZONE) : (euler.x - PITCH_DEAD_ZONE);
}
MyAvatar.headPitch = MyAvatar.headPitch + (pitch * PITCH_SCALING * deltaTime);
// TODO: Add some camera roll proportional to the rate of turn (so it feels like an airplane or roller coaster)
}
wasGrabbingWithRightHand = grabbingWithRightHand;
wasGrabbingWithLeftHand = grabbingWithLeftHand;
}
// Update for joysticks and move button
var THRUST_DEAD_ZONE = 0.1;
var ROTATE_DEAD_ZONE = 0.1;
function flyWithHydra(deltaTime) {
var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
if (Math.abs(thrustJoystickPosition.x) > THRUST_DEAD_ZONE || Math.abs(thrustJoystickPosition.y) > THRUST_DEAD_ZONE) {
if (thrustMultiplier < MAX_THRUST_MULTIPLIER) {
thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE);
}
var headOrientation = MyAvatar.headOrientation;
var front = Quat.getFront(headOrientation);
var right = Quat.getRight(headOrientation);
var up = Quat.getUp(headOrientation);
var thrustFront = Vec3.multiply(front, MyAvatar.scale * THRUST_MAG_HAND_JETS *
thrustJoystickPosition.y * thrustMultiplier * deltaTime);
MyAvatar.addThrust(thrustFront);
var thrustRight = Vec3.multiply(right, MyAvatar.scale * THRUST_MAG_HAND_JETS *
thrustJoystickPosition.x * thrustMultiplier * deltaTime);
MyAvatar.addThrust(thrustRight);
} else {
thrustMultiplier = INITIAL_THRUST_MULTIPLIER;
}
// View Controller
var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER);
if (Math.abs(viewJoystickPosition.x) > ROTATE_DEAD_ZONE || Math.abs(viewJoystickPosition.y) > ROTATE_DEAD_ZONE) {
// change the body yaw based on our x controller
var orientation = MyAvatar.orientation;
var deltaOrientation = Quat.fromPitchYawRollDegrees(0, (-1 * viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime), 0);
MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation);
// change the headPitch based on our x controller
var newPitch = MyAvatar.headPitch + (viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime);
MyAvatar.headPitch = newPitch;
}
handleGrabBehavior(deltaTime);
displayDebug();
}
Script.update.connect(flyWithHydra);
Controller.captureJoystick(THRUST_CONTROLLER);
Controller.captureJoystick(VIEW_CONTROLLER);
// Map keyPress and mouse move events to our callbacks
function scriptEnding() {
// re-enabled the standard application for touch events
Controller.releaseJoystick(THRUST_CONTROLLER);
Controller.releaseJoystick(VIEW_CONTROLLER);
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -20,10 +20,11 @@ var BALL_SIZE = 0.08;
var PADDLE_SIZE = 0.20;
var PADDLE_THICKNESS = 0.06;
var PADDLE_COLOR = { red: 184, green: 134, blue: 11 };
var BALL_COLOR = { red: 255, green: 0, blue: 0 };
var BALL_COLOR = { red: 0, green: 255, blue: 0 };
var LINE_COLOR = { red: 255, green: 255, blue: 0 };
var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 };
//probably we need to fix these initial values (offsets and orientation)
var HOLD_POSITION_LEFT_OFFSET = { x: -0.15, y: 0.05, z: -0.05 };
var HOLD_POSITION_RIGHT_OFFSET = { x: -0.15, y: 0.05, z: 0.05 };
var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0);
@ -32,18 +33,7 @@ var SPRING_FORCE = 15.0;
var lastSoundTime = 0;
var gameOn = false;
var leftHanded = true;
var controllerID;
function setControllerID() {
if (leftHanded) {
controllerID = 1;
} else {
controllerID = 3;
}
}
setControllerID();
Menu.addMenu("PaddleBall");
Menu.addMenuItem({ menuName: "PaddleBall", menuItemName: "Left-Handed", isCheckable: true, isChecked: true });
@ -63,7 +53,7 @@ var ball, paddle, paddleModel, line;
function createEntities() {
ball = Entities.addEntity(
{ type: "Sphere",
position: Controller.getSpatialControlPosition(controllerID),
position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE },
color: BALL_COLOR,
gravity: { x: 0, y: GRAVITY, z: 0 },
@ -73,28 +63,28 @@ function createEntities() {
paddle = Entities.addEntity(
{ type: "Box",
position: Controller.getSpatialControlPosition(controllerID),
position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 },
color: PADDLE_COLOR,
gravity: { x: 0, y: 0, z: 0 },
ignoreCollisions: false,
damping: 0.10,
visible: false,
rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)),
collisionsWillMove: false });
rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation,
collisionsWillMove: false });
modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx";
paddleModel = Entities.addEntity(
{ type: "Model",
position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_BOX_OFFSET),
position: Vec3.sum( leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, PADDLE_BOX_OFFSET),
dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 },
color: PADDLE_COLOR,
gravity: { x: 0, y: 0, z: 0 },
ignoreCollisions: true,
modelURL: modelURL,
damping: 0.10,
rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)),
collisionsWillMove: false });
rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation,
collisionsWillMove: false });
line = Overlays.addOverlay("line3d", {
start: { x: 0, y: 0, z: 0 },
@ -118,7 +108,7 @@ function deleteEntities() {
}
function update(deltaTime) {
var palmPosition = Controller.getSpatialControlPosition(controllerID);
var palmPosition = leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation;
var controllerActive = (Vec3.length(palmPosition) > 0);
if (!gameOn && controllerActive) {
@ -133,8 +123,8 @@ function update(deltaTime) {
}
var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0));
var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), paddleOrientation);
var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(),
var paddleWorldOrientation = Quat.multiply(leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, paddleOrientation);
var holdPosition = Vec3.sum(leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET ));
var props = Entities.getEntityProperties(ball);
@ -146,10 +136,10 @@ function update(deltaTime) {
Entities.editEntity(ball, { velocity: ballVelocity });
Overlays.editOverlay(line, { start: props.position, end: holdPosition });
Entities.editEntity(paddle, { position: holdPosition,
velocity: Controller.getSpatialControlVelocity(controllerID),
velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity,
rotation: paddleWorldOrientation });
Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)),
velocity: Controller.getSpatialControlVelocity(controllerID),
velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity,
rotation: paddleWorldOrientation });
}
@ -182,7 +172,6 @@ function menuItemEvent(menuItem) {
leftHanded = Menu.isOptionChecked("Left-Handed");
}
if ((leftHanded != oldHanded) && gameOn) {
setControllerID();
deleteEntities();
createEntities();
}

View file

@ -19,14 +19,7 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
// maybe we should make these constants...
var LEFT_PALM = 0;
var LEFT_TIP = 1;
var LEFT_BUTTON_FWD = 5;
var LEFT_BUTTON_3 = 3;
var RIGHT_PALM = 2;
var RIGHT_TIP = 3;
var RIGHT_BUTTON_FWD = 11;
var RIGHT_BUTTON_3 = 9;
var BALL_RADIUS = 0.08;
var GRAVITY_STRENGTH = 3.0;
@ -69,9 +62,6 @@ function getBallHoldPosition(whichSide) {
}
function checkControllerSide(whichSide) {
var BUTTON_FWD;
var BUTTON_3;
var TRIGGER;
var palmPosition;
var palmRotation;
var ballAlreadyInHand;
@ -79,35 +69,35 @@ function checkControllerSide(whichSide) {
var linearVelocity;
var angularVelocity;
var AVERAGE_FACTOR = 0.33;
var grabButtonPressed;
if (whichSide == LEFT_PALM) {
BUTTON_FWD = LEFT_BUTTON_FWD;
BUTTON_3 = LEFT_BUTTON_3;
TRIGGER = 0;
palmPosition = Controller.getSpatialControlPosition(LEFT_PALM);
palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(LEFT_PALM));
palmPosition = MyAvatar.leftHandPose.translation;
palmRotation = MyAvatar.leftHandPose.rotation;
ballAlreadyInHand = leftBallAlreadyInHand;
handMessage = "LEFT";
averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, Controller.getSpatialControlVelocity(LEFT_TIP)),
averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.leftHandTipPose.velocity),
Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[0]));
linearVelocity = averageLinearVelocity[0];
angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(LEFT_TIP));
angularVelocity = MyAvatar.leftHandTipPose.angularVelocity;
grabButtonPressed = (Controller.getValue(Controller.Standard.LT) > 0.5);
} else {
BUTTON_FWD = RIGHT_BUTTON_FWD;
BUTTON_3 = RIGHT_BUTTON_3;
TRIGGER = 1;
palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM);
palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(RIGHT_PALM));
palmPosition = MyAvatar.rightHandPose.translation;
palmRotation = MyAvatar.rightHandPose.rotation;
ballAlreadyInHand = rightBallAlreadyInHand;
averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, Controller.getSpatialControlVelocity(RIGHT_TIP)),
averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.rightHandTipPose.velocity),
Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[1]));
linearVelocity = averageLinearVelocity[1];
angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(RIGHT_TIP));
angularVelocity = MyAvatar.rightHandTipPose.angularVelocity;
handMessage = "RIGHT";
grabButtonPressed = (Controller.getValue(Controller.Standard.RT) > 0.5);
}
var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3) || (Controller.getTriggerValue(TRIGGER) > 0.5));
// If I don't currently have a ball in my hand, then try to catch closest one
if (!ballAlreadyInHand && grabButtonPressed) {
var closestEntity = Entities.findClosestEntity(palmPosition, targetRadius);
@ -187,10 +177,8 @@ function checkControllerSide(whichSide) {
if (ballAlreadyInHand) {
if (whichSide == LEFT_PALM) {
handEntity = leftHandEntity;
whichTip = LEFT_TIP;
} else {
handEntity = rightHandEntity;
whichTip = RIGHT_TIP;
}
// If holding the ball keep it in the palm
@ -231,22 +219,10 @@ function checkControllerSide(whichSide) {
}
}
}
function checkController(deltaTime) {
var numberOfButtons = Controller.getNumberOfButtons();
var numberOfTriggers = Controller.getNumberOfTriggers();
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
// this is expected for hydras
if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) {
debugPrint("total buttons = " + numberOfButtons + ", Triggers = " + numberOfTriggers + ", controllers/trigger = " + controllersPerTrigger);
return; // bail if no hydra
}
checkControllerSide(LEFT_PALM);
checkControllerSide(RIGHT_PALM);
checkControllerSide(LEFT_PALM);
checkControllerSide(RIGHT_PALM);
}

View file

@ -1510,7 +1510,7 @@ PropertiesTool = function(opts) {
if (entity.properties.rotation !== undefined) {
entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation);
}
if (entity.properties.keyLight.direction !== undefined) {
if (entity.properties.keyLight !== undefined && entity.properties.keyLight.direction !== undefined) {
entity.properties.keyLight.direction = Vec3.multiply(RADIANS_TO_DEGREES, Vec3.toPolar(entity.properties.keyLight.direction));
entity.properties.keyLight.direction.z = 0.0;
}
@ -1541,7 +1541,7 @@ PropertiesTool = function(opts) {
var rotation = data.properties.rotation;
data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z);
}
if (data.properties.keyLight.direction !== undefined) {
if (data.properties.keyLight !== undefined && data.properties.keyLight.direction !== undefined) {
data.properties.keyLight.direction = Vec3.fromPolar(
data.properties.keyLight.direction.x * DEGREES_TO_RADIANS, data.properties.keyLight.direction.y * DEGREES_TO_RADIANS);
}

View file

@ -10,25 +10,19 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// initialize our triggers
var triggerPulled = new Array();
var numberOfTriggers = Controller.getNumberOfTriggers();
for (t = 0; t < numberOfTriggers; t++) {
var NUMBER_OF_TRIGGERS = 2;
for (t = 0; t < NUMBER_OF_TRIGGERS; t++) {
triggerPulled[t] = false;
}
var triggers = new Array();
triggers[0] = Controller.Standard.LT;
triggers[1] = Controller.Standard.RT;
function checkController(deltaTime) {
var numberOfTriggers = Controller.getNumberOfTriggers();
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
var triggerToggled = false;
// this is expected for hydras
if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
for (var t = 0; t < numberOfTriggers; t++) {
var triggerValue = Controller.getTriggerValue(t);
for (var t = 0; t < NUMBER_OF_TRIGGERS; t++) {
var triggerValue = Controller.getValue(triggers[t]);
if (triggerPulled[t]) {
// must release to at least 0.1
if (triggerValue < 0.1) {
@ -41,17 +35,14 @@ function checkController(deltaTime) {
triggerToggled = true;
}
}
if (triggerToggled) {
print("a trigger was toggled");
}
}
}
}
// register the call back so it fires before each data send
Script.update.connect(checkController);
function printKeyEvent(eventName, event) {
print(eventName);
print(" event.key=" + event.key);
@ -64,7 +55,6 @@ function printKeyEvent(eventName, event) {
}
function keyPressEvent(event) {
printKeyEvent("keyPressEvent", event);
if (event.text == "A") {
print("the A key was pressed");
}
@ -72,10 +62,8 @@ function keyPressEvent(event) {
print("the <space> key was pressed");
}
}
function keyReleaseEvent(event) {
printKeyEvent("keyReleaseEvent", event);
if (event.text == "A") {
print("the A key was released");
}
@ -83,11 +71,9 @@ function keyReleaseEvent(event) {
print("the <space> key was pressed");
}
}
// Map keyPress and mouse move events to our callbacks
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
// prevent the A key from going through to the application
Controller.captureKeyEvents({ text: "A" });
Controller.captureKeyEvents({ key: "A".charCodeAt(0) }); // same as above, just another example of how to capture the key
@ -95,8 +81,6 @@ Controller.captureKeyEvents({ text: " " });
Controller.captureKeyEvents({ text: "@", isMeta: true });
Controller.captureKeyEvents({ text: "page up" });
Controller.captureKeyEvents({ text: "page down" });
function printMouseEvent(eventName, event) {
print(eventName);
print(" event.x,y=" + event.x + ", " + event.y);
@ -109,22 +93,18 @@ function printMouseEvent(eventName, event) {
print(" event.isMeta=" + event.isMeta);
print(" event.isAlt=" + event.isAlt);
}
function mouseMoveEvent(event) {
printMouseEvent("mouseMoveEvent", event);
}
function mousePressEvent(event) {
printMouseEvent("mousePressEvent", event);
}
function mouseReleaseEvent(event) {
printMouseEvent("mouseReleaseEvent", event);
}
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
function printTouchEvent(eventName, event) {
print(eventName);
@ -143,7 +123,6 @@ function printTouchEvent(eventName, event) {
print(" event.radius=" + event.radius);
print(" event.isPinching=" + event.isPinching);
print(" event.isPinchOpening=" + event.isPinchOpening);
print(" event.angle=" + event.angle);
for (var i = 0; i < event.points.length; i++) {
print(" event.angles[" + i + "]:" + event.angles[i]);
@ -151,15 +130,12 @@ function printTouchEvent(eventName, event) {
print(" event.isRotating=" + event.isRotating);
print(" event.rotating=" + event.rotating);
}
function touchBeginEvent(event) {
printTouchEvent("touchBeginEvent", event);
}
function touchUpdateEvent(event) {
printTouchEvent("touchUpdateEvent", event);
}
function touchEndEvent(event) {
printTouchEvent("touchEndEvent", event);
}
@ -167,8 +143,6 @@ function touchEndEvent(event) {
Controller.touchBeginEvent.connect(touchBeginEvent);
Controller.touchUpdateEvent.connect(touchUpdateEvent);
Controller.touchEndEvent.connect(touchEndEvent);
function wheelEvent(event) {
print("wheelEvent");
print(" event.x,y=" + event.x + ", " + event.y);
@ -182,9 +156,7 @@ function wheelEvent(event) {
print(" event.isMeta=" + event.isMeta);
print(" event.isAlt=" + event.isAlt);
}
Controller.wheelEvent.connect(wheelEvent);
function scriptEnding() {
// re-enabled the standard application for touch events
Controller.releaseKeyEvents({ text: "A" });
@ -194,5 +166,4 @@ function scriptEnding() {
Controller.releaseKeyEvents({ text: "page up" });
Controller.releaseKeyEvents({ text: "page down" });
}
Script.scriptEnding.connect(scriptEnding);
Script.scriptEnding.connect(scriptEnding);

View file

@ -59,9 +59,7 @@ function controller(side) {
this.triggerHeld = false;
this.triggerThreshold = 0.9;
this.side = side;
this.palm = 2 * side;
this.tip = 2 * side + 1;
this.trigger = side;
this.trigger = side == LEFT ? Controller.Standard.LT : Controller.Standard.RT;
this.originalGravity = {
x: 0,
y: 0,
@ -150,8 +148,8 @@ function controller(side) {
this.updateControllerState = function() {
this.palmPosition = Controller.getSpatialControlPosition(this.palm);
this.tipPosition = Controller.getSpatialControlPosition(this.tip);
this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation;
this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation;
this.triggerValue = Controller.getTriggerValue(this.trigger);
}

View file

@ -238,7 +238,7 @@ var inHand = false;
function isControllerActive() {
// I don't think the hydra API provides any reliable way to know whether a particular controller is active. Ask for both.
controllerActive = (Vec3.length(Controller.getSpatialControlPosition(3)) > 0) || Vec3.length(Controller.getSpatialControlPosition(4)) > 0;
controllerActive = (Vec3.length(MyAvatar.leftHandPose.translation) > 0) || Vec3.length(MyAvatar.rightHandPose.translation) > 0;
return controllerActive;
}
@ -312,10 +312,10 @@ function grabSword(hand) {
}
var handRotation;
if (hand === "right") {
handRotation = MyAvatar.getRightPalmRotation();
handRotation = MyAvatar.rightHandPose.rotation;
} else if (hand === "left") {
handRotation = MyAvatar.getLeftPalmRotation();
handRotation = MyAvatar.leftHandPose.rotation;
}
var swordRotation = Entities.getEntityProperties(swordID).rotation;
var offsetRotation = Quat.multiply(Quat.inverse(handRotation), swordRotation);

View file

@ -0,0 +1,26 @@
//
// lineExample.js
// examples/example
//
// Created by Ryan Huffman on October 27, 2015
// Copyright 2015 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
//
Script.include("../libraries/line.js");
var basePosition = MyAvatar.position;
var color = { red: 128, green: 220, blue: 190 };
var strokeWidth = 0.01;
var line = new InfiniteLine(basePosition, color, 20);
for (var i = 0; i < (16 * Math.PI); i += 0.05) {
var x = 0
var y = 0.25 * Math.sin(i);
var z = i / 10;
var position = Vec3.sum(basePosition, { x: x, y: y, z: z });
line.enqueuePoint(position, strokeWidth);
}

View file

@ -1,41 +0,0 @@
//
// metavoxels.js
// examples
//
// 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
//
Script.setInterval(function() {
var spanner;
if (Math.random() < 0.5) {
spanner = new Sphere();
} else {
spanner = new Cuboid();
spanner.aspectX = 0.1 + Math.random() * 1.9;
spanner.aspectY = 0.1 + Math.random() * 1.9;
}
spanner.scale = 0.1 + Math.random() * 1.9;
spanner.rotation = Quat.fromPitchYawRollDegrees(Math.random() * 360.0, Math.random() * 360.0, Math.random() * 360.0);
spanner.translation = { x: 10.0 + Math.random() * 10.0, y: 10.0 + Math.random() * 10.0, z: 10.0 + Math.random() * 10.0 };
if (Math.random() < 0.5) {
var material = new MaterialObject();
if (Math.random() < 0.5) {
material.diffuse = "http://www.fungibleinsight.com/faces/grass.jpg";
} else {
material.diffuse = "http://www.fungibleinsight.com/faces/soil.jpg";
}
Metavoxels.setVoxelMaterial(spanner, material);
} else if (Math.random() < 0.5) {
Metavoxels.setVoxelColor(spanner, { red: Math.random() * 255.0, green: Math.random() * 255.0,
blue: Math.random() * 255.0 });
} else {
Metavoxels.setVoxelColor(spanner, { red: 0, green: 0, blue: 0, alpha: 0 });
}
}, 1000);

View file

@ -71,10 +71,8 @@ function controller(side, cycleColorButton) {
this.triggerHeld = false;
this.triggerThreshold = 0.9;
this.side = side;
this.palm = 2 * side;
this.tip = 2 * side + 1;
this.trigger = side;
this.cycleColorButton = cycleColorButton;
this.trigger = side == LEFT ? Controller.Stantard.LT : Controller.Standard.RT;
this.cycleColorButton = side == LEFT ? Controller.Stantard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb;
this.points = [];
this.normals = [];
@ -173,11 +171,10 @@ function controller(side, cycleColorButton) {
this.updateControllerState = function() {
this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton);
this.palmPosition = Controller.getSpatialControlPosition(this.palm);
this.tipPosition = Controller.getSpatialControlPosition(this.tip);
this.palmNormal = Controller.getSpatialControlNormal(this.palm);
this.triggerValue = Controller.getTriggerValue(this.trigger);
this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton);
this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation;
this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation;
this.triggerValue = Controller.getValue(this.trigger);
if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) {
@ -215,8 +212,8 @@ function vectorIsZero(v) {
}
var rightController = new controller(RIGHT, RIGHT_BUTTON_4);
var leftController = new controller(LEFT, LEFT_BUTTON_4);
var rightController = new controller(RIGHT);
var leftController = new controller(LEFT);
Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding);

View file

@ -11,6 +11,37 @@
// Assumes you only have the default keyboard connected
function findAction(name) {
var actions = Controller.getAllActions();
for (var i = 0; i < actions.length; i++) {
if (actions[i].actionName == name) {
return i;
}
}
// If the action isn't found, it will default to the first available action
return 0;
}
var hydra = Controller.Hardware.Hydra;
if (hydra !== undefined) {
print("-----------------------------------");
var mapping = Controller.newMapping("Test");
var standard = Controller.Standard;
print("standard:" + standard);
mapping.from(function () { return Math.sin(Date.now() / 250); }).to(function (newValue, oldValue, source) {
print("function source newValue:" + newValue + ", oldValue:" + oldValue + ", source:" + source);
});
mapping.from(hydra.L1).to(standard.A);
mapping.from(hydra.L2).to(standard.B);
mapping.from(hydra.L3).to(function (newValue, oldValue, source) {
print("hydra.L3 newValue:" + newValue + ", oldValue:" + oldValue + ", source:" + source);
});
Controller.enableMapping("Test");
print("-----------------------------------");
} else {
print("couldn't find hydra");
}
Object.keys(Controller.Standard).forEach(function (input) {
print("Controller.Standard." + input + ":" + Controller.Standard[input]);
@ -32,44 +63,48 @@ Controller.resetAllDeviceBindings();
// Query all actions
print("All Actions: \n" + Controller.getAllActions());
var actionId = findAction("YAW_LEFT")
print("Yaw Left action ID: " + actionId)
// Each action stores:
// action: int representation of enum
print("Action 5 int: \n" + Controller.getAllActions()[5].action);
print("Action int: \n" + Controller.getAllActions()[actionId].action);
// actionName: string representation of enum
print("Action 5 name: \n" + Controller.getAllActions()[5].actionName);
print("Action name: \n" + Controller.getAllActions()[actionId].actionName);
// inputChannels: list of all inputchannels that control that action
print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n");
print("Action input channels: \n" + Controller.getAllActions()[actionId].inputChannels + "\n");
// Each input channel stores:
// action: Action that this InputChannel maps to
print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action);
print("Input channel action: \n" + Controller.getAllActions()[actionId].inputChannels[0].action);
// scale: sensitivity of input
print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale);
print("Input channel scale: \n" + Controller.getAllActions()[actionId].inputChannels[0].scale);
// input and modifier: Inputs
print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n");
print("Input channel input and modifier: \n" + Controller.getAllActions()[actionId].inputChannels[0].input + "\n" + Controller.getAllActions()[actionId].inputChannels[0].modifier + "\n");
// Each Input stores:
// device: device of input
print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device);
print("Input device: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.device);
// channel: channel of input
print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel);
print("Input channel: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.channel);
// type: type of input (Unknown, Button, Axis, Joint)
print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type);
print("Input type: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.type);
// id: id of input
print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n");
print("Input id: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.id + "\n");
// You can get the name of a device from its id
print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id));
print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[actionId].inputChannels[0].input.id));
// You can also get all of a devices input channels
print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n");
@ -105,7 +140,7 @@ for (i = 0; i < availableInputs.length; i++) {
// You can modify key bindings by using these avaiable inputs
// This will replace e (up) with 6
var e = Controller.getAllActions()[5].inputChannels[0];
var e = Controller.getAllActions()[actionId].inputChannels[0];
Controller.removeInputChannel(e);
e.input = availableInputs[6].input;
Controller.addInputChannel(e);

View file

@ -830,7 +830,7 @@
elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('intensity','keyLight'));
elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity'));
elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity'));
var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY, elZoneKeyLightDirectionZ);
elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction);
@ -1520,7 +1520,7 @@
<div class="zone-section keyLight-section property">
<div class="label">Ambient URL</div>
<div class="value">
<input type="text" id="property-zone-skybox-url" class="url">
<input type="text" id="property-zone-key-ambient-url" class="url">
</div>
</div>

161
examples/libraries/line.js Normal file
View file

@ -0,0 +1,161 @@
//
// line.js
// examples/libraries
//
// Created by Ryan Huffman on October 27, 2015
// Copyright 2015 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
//
function error(message) {
print("[ERROR] " + message);
}
// PolyLine
var LINE_DIMENSIONS = { x: 2000, y: 2000, z: 2000 };
var MAX_LINE_LENGTH = 40; // This must be 2 or greater;
var DEFAULT_STROKE_WIDTH = 0.1;
var DEFAULT_LIFETIME = 20;
var DEFAULT_COLOR = { red: 255, green: 255, blue: 255 };
var PolyLine = function(position, color, lifetime) {
this.position = position;
this.color = color;
this.lifetime = lifetime === undefined ? DEFAULT_LIFETIME : lifetime;
this.points = [
];
this.strokeWidths = [
];
this.normals = [
]
this.entityID = Entities.addEntity({
type: "PolyLine",
position: position,
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths,
dimensions: LINE_DIMENSIONS,
color: color,
lifetime: lifetime
});
};
PolyLine.prototype.enqueuePoint = function(position, strokeWidth) {
if (this.isFull()) {
error("Hit max PolyLine size");
return;
}
position = Vec3.subtract(position, this.position);
this.points.push(position);
this.normals.push({ x: 1, y: 0, z: 0 });
this.strokeWidths.push(strokeWidth);
Entities.editEntity(this.entityID, {
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths
});
};
PolyLine.prototype.dequeuePoint = function() {
if (this.points.length == 0) {
error("Hit min PolyLine size");
return;
}
this.points = this.points.slice(1);
this.normals = this.normals.slice(1);
this.strokeWidths = this.strokeWidths.slice(1);
Entities.editEntity(this.entityID, {
linePoints: this.points,
normals: this.normals,
strokeWidths: this.strokeWidths
});
};
PolyLine.prototype.getFirstPoint = function() {
return Vec3.sum(this.position, this.points[0]);
};
PolyLine.prototype.getLastPoint = function() {
return Vec3.sum(this.position, this.points[this.points.length - 1]);
};
PolyLine.prototype.getSize = function() {
return this.points.length;
}
PolyLine.prototype.isFull = function() {
return this.points.length >= MAX_LINE_LENGTH;
};
PolyLine.prototype.destroy = function() {
Entities.deleteEntity(this.entityID);
this.points = [];
};
// InfiniteLine
InfiniteLine = function(position, color, lifetime) {
this.position = position;
this.color = color;
this.lifetime = lifetime === undefined ? DEFAULT_LIFETIME : lifetime;
this.lines = [];
this.size = 0;
};
InfiniteLine.prototype.enqueuePoint = function(position, strokeWidth) {
var currentLine;
if (this.lines.length == 0) {
currentLine = new PolyLine(position, this.color, this.lifetime);
this.lines.push(currentLine);
} else {
currentLine = this.lines[this.lines.length - 1];
}
if (currentLine.isFull()) {
var newLine = new PolyLine(currentLine.getLastPoint(), this.color, this.lifetime);
newLine.enqueuePoint(currentLine.getLastPoint(), strokeWidth);
this.lines.push(newLine);
currentLine = newLine;
}
currentLine.enqueuePoint(position, strokeWidth);
++this.size;
};
InfiniteLine.prototype.dequeuePoint = function() {
if (this.lines.length == 0) {
error("Trying to dequeue from InfiniteLine when no points are left");
return;
}
var lastLine = this.lines[0];
lastLine.dequeuePoint();
if (lastLine.getSize() <= 1) {
this.lines = this.lines.slice(1);
}
--this.size;
};
InfiniteLine.prototype.getFirstPoint = function() {
return this.lines.length > 0 ? this.lines[0].getFirstPoint() : null;
};
InfiniteLine.prototype.getLastPoint = function() {
return this.lines.length > 0 ? this.lines[lines.length - 1].getLastPoint() : null;
};
InfiniteLine.prototype.destroy = function() {
for (var i = 0; i < this.lines.length; ++i) {
this.lines[i].destroy();
}
this.size = 0;
};

View file

@ -15,16 +15,18 @@ Script.include("omniTool/models/invisibleWand.js");
OmniToolModules = {};
OmniToolModuleType = null;
LOG_DEBUG = 1;
OmniTool = function(side) {
OmniTool = function(left) {
this.OMNI_KEY = "OmniTool";
this.MAX_FRAMERATE = 60;
this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE
this.SIDE = side;
this.PALM = 2 * side;
this.ACTION = findAction(side ? "ACTION2" : "ACTION1");
this.ALT_ACTION = findAction(side ? "ACTION1" : "ACTION2");
this.left = left;
this.triggered = false;
var actions = Controller.Actions;
var standard = Controller.Standard;
this.palmControl = left ? actions.LeftHand : actions.RightHand;
logDebug("Init OmniTool " + (left ? "left" : "right"));
this.highlighter = new Highlighter();
this.ignoreEntities = {};
this.nearestOmniEntity = {
@ -47,22 +49,25 @@ OmniTool = function(side) {
this.showWand(false);
// Connect to desired events
var _this = this;
Controller.actionEvent.connect(function(action, state) {
_this.onActionEvent(action, state);
});
var that = this;
Script.update.connect(function(deltaTime) {
_this.lastUpdateInterval += deltaTime;
if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) {
_this.onUpdate(_this.lastUpdateInterval);
_this.lastUpdateInterval = 0;
that.lastUpdateInterval += deltaTime;
if (that.lastUpdateInterval >= that.UPDATE_INTERVAL) {
that.onUpdate(that.lastUpdateInterval);
that.lastUpdateInterval = 0;
}
});
Script.scriptEnding.connect(function() {
_this.onCleanup();
that.onCleanup();
});
this.mapping = Controller.newMapping();
this.mapping.from(left ? standard.LeftPrimaryThumb : standard.RightPrimaryThumb).to(function(value){
that.onUpdateTrigger(value);
})
this.mapping.enable();
}
OmniTool.prototype.showWand = function(show) {
@ -81,30 +86,23 @@ OmniTool.prototype.showWand = function(show) {
}
}
OmniTool.prototype.onCleanup = function(action) {
this.mapping.disable();
this.unloadModule();
}
OmniTool.prototype.onActionEvent = function(action, state) {
// FIXME figure out the issues when only one spatial controller is active
// logDebug("Action: " + action + " " + state);
if (this.module && this.module.onActionEvent) {
this.module.onActionEvent(action, state);
}
if (action == this.ACTION) {
if (state) {
OmniTool.prototype.onUpdateTrigger = function (value) {
//logDebug("Trigger update value " + value);
var triggered = value != 0;
if (triggered != this.triggered) {
this.triggered = triggered;
if (this.triggered) {
this.onClick();
} else {
this.onRelease();
}
}
// FIXME Does not work
//// with only one controller active (listed as 2 here because 'tip' + 'palm')
//// then treat the alt action button as the action button
}
OmniTool.prototype.getOmniToolData = function(entityId) {
@ -127,7 +125,7 @@ OmniTool.prototype.setActive = function(active) {
if (active === this.active) {
return;
}
logDebug("OmniTool changing active state: " + active);
logDebug("OmniTool " + this.left + " changing active state: " + active);
this.active = active;
this.model.setVisible(this.active);
if (this.module && this.module.onActiveChanged) {
@ -138,17 +136,17 @@ OmniTool.prototype.setActive = function(active) {
OmniTool.prototype.onUpdate = function(deltaTime) {
// FIXME this returns data if either the left or right controller is not on the base
this.position = Controller.getSpatialControlPosition(this.PALM);
this.pose = Controller.getPoseValue(this.palmControl);
this.position = this.left ? MyAvatar.leftHandTipPosition : MyAvatar.rightHandTipPosition;
// When on the base, hydras report a position of 0
this.setActive(Vec3.length(this.position) > 0.001);
if (!this.active) {
return;
}
if (this.model) {
// Update the wand
var rawRotation = Controller.getSpatialControlRawRotation(this.PALM);
var rawRotation = this.pose.rotation;
this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation);
this.model.setTransform({
rotation: this.rotation,
@ -306,6 +304,7 @@ OmniTool.prototype.scan = function() {
}
OmniTool.prototype.unloadModule = function() {
logDebug("Unloading omniTool module")
if (this.module && this.module.onUnload) {
this.module.onUnload();
}
@ -348,4 +347,4 @@ OmniTool.prototype.activateNewOmniModule = function() {
}
// FIXME find a good way to sync the two omni tools
OMNI_TOOLS = [ new OmniTool(0), new OmniTool(1) ];
OMNI_TOOLS = [ new OmniTool(true), new OmniTool(false) ];

View file

@ -31,13 +31,7 @@ scaleLine = function (start, end, scale) {
}
findAction = function(name) {
var actions = Controller.getAllActions();
for (var i = 0; i < actions.length; i++) {
if (actions[i].actionName == name) {
return i;
}
}
return 0;
return Controller.findAction(name);
}
addLine = function(origin, vector, color) {

View file

@ -18,24 +18,8 @@ Script.include("./libraries/walkConstants.js");
Avatar = function() {
// if Hydras are connected, the only way to enable use is to never set any arm joint rotation
this.hydraCheck = function() {
// function courtesy of Thijs Wenker (frisbee.js)
var numberOfButtons = Controller.getNumberOfButtons();
var numberOfTriggers = Controller.getNumberOfTriggers();
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
const HYDRA_BUTTONS = 12;
const HYDRA_TRIGGERS = 2;
const HYDRA_CONTROLLERS_PER_TRIGGER = 2;
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
if (numberOfButtons == HYDRA_BUTTONS &&
numberOfTriggers == HYDRA_TRIGGERS &&
controllersPerTrigger == HYDRA_CONTROLLERS_PER_TRIGGER) {
print('walk.js info: Razer Hydra detected. Setting arms free (not controlled by script)');
return true;
} else {
print('walk.js info: Razer Hydra not detected. Arms will be controlled by script.');
return false;
}
this.hydraCheck = function () {
return Controller.Hardware.Hydra !== undefined;
}
// settings
this.headFree = true;

View file

@ -38,7 +38,7 @@ var mouseLook = (function () {
keyboardID = 0;
function onKeyPressEvent(event) {
if (event.text == 'M') {
if (event.text == 'm') {
active = !active;
updateMapping();
}

View file

@ -47,6 +47,7 @@
if (this.painting) {
return;
}
this.whichHand = this.hand;
if (this.hand === RIGHT_HAND) {
this.getHandPosition = MyAvatar.getRightPalmPosition;
this.getHandRotation = MyAvatar.getRightPalmRotation;
@ -183,6 +184,9 @@
},
releaseGrab: function() {
if(this.hand !== this.whichHand) {
return;
}
this.stopPainting();
},
@ -241,10 +245,8 @@
Overlays.deleteOverlay(this.laserPointer);
// this.eraseBoard();
}
};
// entity scripts always need to return a newly constructed object of our type
return new Whiteboard();
});

View file

@ -16,186 +16,199 @@
Script.include("../../libraries/utils.js");
var scriptURL = Script.resolvePath("whiteboardEntityScript.js");
var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/whiteboard.fbx";
var colorIndicatorBorderModelURL = "https://s3.amazonaws.com/hifi-public/eric/models/colorIndicatorBorder.fbx";
var eraserModelURL = "https://s3.amazonaws.com/hifi-public/eric/models/eraser.fbx";
var surfaceModelURL = "https://s3.amazonaws.com/hifi-public/eric/models/boardSurface.fbx";
var rotation = Quat.safeEulerAngles(Camera.getOrientation());
rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0);
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation)));
center.y += 0.4;
var whiteboardDimensions, colorIndicatorBoxDimensions, colorIndicatorBox, eraser, blocker;
var colorBoxes = [];
var colors = [
hexToRgb("#2F8E84"),
hexToRgb("#66CCB3"),
hexToRgb("#A43C37"),
hexToRgb("#491849"),
hexToRgb("#6AB03B"),
hexToRgb("#993369"),
hexToRgb("#9B47C2")
hexToRgb("#000000")
];
//WHITEBOARD
var whiteboardDimensions = {
x: 2,
y: 1.5,
z: 0.08
};
var whiteboard = Entities.addEntity({
type: "Model",
modelURL: "https://hifi-public.s3.amazonaws.com/ozan/support/for_eric/whiteboard/whiteboard.fbx",
name: "whiteboard",
shapeType: "box",
modelURL: modelURL,
name: "whiteboard base",
position: center,
rotation: rotation,
script: scriptURL,
dimensions: whiteboardDimensions,
color: {
red: 255,
green: 255,
blue: 255
}
});
// COLOR INDICATOR BOX
var colorIndicatorDimensions = {
x: whiteboardDimensions.x,
y: 0.05,
z: 0.02
var colorIndicatorPosition = {
x: center.x,
y: center.y,
z: center.z
};
scriptURL = Script.resolvePath("colorIndicatorEntityScript.js");
var colorIndicatorPosition = Vec3.sum(center, {
x: 0,
y: whiteboardDimensions.y / 2 + colorIndicatorDimensions.y / 2,
z: 0
});
var colorIndicatorBox = Entities.addEntity({
type: "Box",
name: "Color Indicator",
color: colors[0],
rotation: rotation,
colorIndicatorPosition.y += 1.55;
colorIndicatorPosition = Vec3.sum(colorIndicatorPosition, Vec3.multiply(-0.1, Quat.getFront(rotation)));
var colorIndicatorBorder = Entities.addEntity({
type: "Model",
position: colorIndicatorPosition,
dimensions: colorIndicatorDimensions,
script: scriptURL,
userData: JSON.stringify({
whiteboard: whiteboard
})
modelURL: colorIndicatorBorderModelURL,
rotation: rotation,
shapeType: "box"
});
Entities.editEntity(whiteboard, {
var surfaceCenter = Vec3.sum(center, Vec3.multiply(-0.1, Quat.getFront(rotation)));
surfaceCenter.y += 0.6;
var drawingSurface = Entities.addEntity({
type: "Model",
modelURL: surfaceModelURL,
shapeType: "box",
name: "whiteboard surface",
position: surfaceCenter,
script: scriptURL,
rotation: rotation,
userData: JSON.stringify({
color: {
currentColor: colors[0]
},
colorIndicator: colorIndicatorBox
})
});
//COLOR BOXES
var direction = Quat.getRight(rotation);
var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2));
var colorBoxes = [];
var colorSquareDimensions = {
x: (whiteboardDimensions.x / 2) / (colors.length - 1),
y: 0.1,
z: 0.05
};
colorBoxPosition.y += whiteboardDimensions.y / 2 + colorIndicatorDimensions.y + colorSquareDimensions.y / 2;
var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2);
var scriptURL = Script.resolvePath("colorSelectorEntityScript.js");
for (var i = 0; i < colors.length; i++) {
var colorBox = Entities.addEntity({
type: "Box",
name: "Color Selector",
position: colorBoxPosition,
dimensions: colorSquareDimensions,
rotation: rotation,
color: colors[i],
script: scriptURL,
userData: JSON.stringify({
whiteboard: whiteboard,
colorIndicator: colorIndicatorBox
})
});
colorBoxes.push(colorBox);
colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes);
}
// BLACK BOX
var blackBoxDimensions = {
x: 0.3,
y: 0.3,
z: 0.01
};
colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + blackBoxDimensions.x / 2 - 0.01));
colorBoxPosition.y += 0.3;
var fragShaderURL = Script.resolvePath('blackInk.fs?v1' + Math.random());
var blackBox = Entities.addEntity({
type: 'Box',
name: "Black Color",
position: colorBoxPosition,
dimensions: blackBoxDimensions,
rotation: rotation,
color: {
red: 0,
green: 0,
blue: 0
},
script: scriptURL,
userData: JSON.stringify({
whiteboard: whiteboard,
version: 2,
ProceduralEntity: {
shaderUrl: fragShaderURL
}
})
});
var lightPosition = Vec3.sum(center, Vec3.multiply(-3, Quat.getFront(rotation)));
var light = Entities.addEntity({
type: 'Light',
name: 'whiteboard light',
position: lightPosition,
dimensions: {x: 10, y: 10, z: 10},
intensity: 2,
color: {red: 255, green: 255, blue: 255}
});
var eraseBoxDimensions = {
x: 0.5,
y: 0.1,
z: 0.01
};
var eraseBoxPosition = Vec3.sum(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + eraseBoxDimensions.x / 2 + 0.01));
eraseBoxPosition.y += 0.3;
var eraserPosition = Vec3.sum(center, {x: 0, y: 2.05, z: 0 });
eraserPosition = Vec3.sum(eraserPosition, Vec3.multiply(-0.1, rotation));
scriptURL = Script.resolvePath("eraseBoardEntityScript.js");
var eraseAllText = Entities.addEntity({
type: "Text",
position: eraseBoxPosition,
var eraser = Entities.addEntity({
type: "Model",
modelURL: eraserModelURL,
position: eraserPosition,
name: "Eraser",
script: scriptURL,
rotation: rotation,
dimensions: eraseBoxDimensions,
backgroundColor: {
red: 0,
green: 60,
blue: 0
},
textColor: {
red: 255,
green: 10,
blue: 10
},
text: "ERASE BOARD",
lineHeight: 0.07,
userData: JSON.stringify({
whiteboard: whiteboard
whiteboard: drawingSurface
})
});
Script.setTimeout(function() {
whiteboardDimensions = Entities.getEntityProperties(whiteboard, "naturalDimensions").naturalDimensions;
colorIndicatorBorderDimensions = Entities.getEntityProperties(colorIndicatorBorder, "naturalDimensions").naturalDimensions;
setUp();
}, 2000)
function setUp() {
var blockerPosition = Vec3.sum(center, {x: 0, y: -1, z: 0 });
blockerPosition = Vec3.sum(blockerPosition, Vec3.multiply(-1, Quat.getFront(rotation)));
blocker = Entities.addEntity({
type: "Box",
rotation: rotation,
position: blockerPosition,
dimensions: {x: whiteboardDimensions.x, y: 1, z: 0.1},
shapeType: "box",
visible: false
});
var eraseModelDimensions = Entities.getEntityProperties(eraser, "naturalDimensions").naturalDimensions;
Entities.editEntity(eraser, {dimensions: eraseModelDimensions});
Entities.editEntity(colorIndicatorBorder, {dimensions: colorIndicatorBorderDimensions});
scriptURL = Script.resolvePath("colorIndicatorEntityScript.js");
var colorIndicatorPosition = Vec3.sum(center, {
x: 0,
y: whiteboardDimensions.y / 2 + colorIndicatorBorderDimensions.y / 2,
z: 0
});
colorIndicatorPosition = Vec3.sum(colorIndicatorPosition, Vec3.multiply(-.1, Quat.getFront(rotation)));
var colorIndicatorBoxDimensions = Vec3.multiply(colorIndicatorBorderDimensions, 0.9);
colorIndicatorBox = Entities.addEntity({
type: "Box",
name: "Color Indicator",
color: colors[0],
rotation: rotation,
position: colorIndicatorPosition,
dimensions: colorIndicatorBoxDimensions,
script: scriptURL,
userData: JSON.stringify({
whiteboard: drawingSurface
})
});
Entities.editEntity(drawingSurface, {
userData: JSON.stringify({
color: {
currentColor: colors[0]
},
colorIndicator: colorIndicatorBox
})
});
//COLOR BOXES
var direction = Quat.getRight(rotation);
var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2));
var colorSquareDimensions = {
x: 0.13,
y: 0.13,
z: 0.002
};
var palleteDepthOffset = -0.07;
var palleteHeightOffset = -0.28;
colorBoxPosition = Vec3.sum(colorBoxPosition, Vec3.multiply(palleteDepthOffset, Quat.getFront(rotation)));
colorBoxPosition.y += palleteHeightOffset;
var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 1.76);
var palleteXOffset = Vec3.multiply(direction, 0.43);
colorBoxPosition = Vec3.sum(colorBoxPosition, palleteXOffset);
var scriptURL = Script.resolvePath("colorSelectorEntityScript.js");
for (var i = 0; i < colors.length; i++) {
var colorBox = Entities.addEntity({
type: "Box",
name: "Color Selector",
position: colorBoxPosition,
dimensions: colorSquareDimensions,
rotation: rotation,
color: colors[i],
script: scriptURL,
userData: JSON.stringify({
whiteboard: drawingSurface,
colorIndicator: colorIndicatorBox
})
});
colorBoxes.push(colorBox);
colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes);
}
}
function cleanup() {
Entities.deleteEntity(whiteboard);
Entities.deleteEntity(eraseAllText);
Entities.deleteEntity(blackBox);
Entities.deleteEntity(drawingSurface);
Entities.deleteEntity(colorIndicatorBorder);
Entities.deleteEntity(eraser);
Entities.deleteEntity(colorIndicatorBox);
Entities.deleteEntity(blocker);
Entities.deleteEntity(light);
colorBoxes.forEach(function(colorBox) {
Entities.deleteEntity(colorBox);
});
}
// Uncomment this line to delete whiteboard and all associated entity on script close
Script.scriptEnding.connect(cleanup);
// Script.scriptEnding.connect(cleanup);

View file

@ -10,7 +10,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var hand = "left";
var hand = "right";
var nullActionID = "00000000-0000-0000-0000-000000000000";
var controllerID;
var controllerActive;
@ -32,7 +32,7 @@ function makeNewStick() {
modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
dimensions: {x: .11, y: .11, z: 1.0},
position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close
position: MyAvatar.rightHandPosition, // initial position doesn't matter, as long as it's close
rotation: MyAvatar.orientation,
damping: .1,
collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav",
@ -84,23 +84,15 @@ function mouseMoveEvent(event) {
}
function initControls(){
if (hand == "right") {
controllerID = 3; // right handed
} else {
controllerID = 4; // left handed
}
}
function update(deltaTime){
var palmPosition = Controller.getSpatialControlPosition(controllerID);
var handPose = (hand == "right") ? MyAvatar.rightHandPose : MyAvatar.leftHandPose;
var palmPosition = handPose.translation;
controllerActive = (Vec3.length(palmPosition) > 0);
if(!controllerActive){
return;
}
stickOrientation = Controller.getSpatialControlRawRotation(controllerID);
stickOrientation = handPose.rotation;
var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0);
stickOrientation = Quat.multiply(stickOrientation, adjustment);

View file

@ -0,0 +1,77 @@
ControllerTest = function() {
var standard = Controller.Standard;
var actions = Controller.Actions;
var xbox = Controller.Hardware.GamePad;
this.mappingEnabled = false;
this.mapping = Controller.newMapping();
this.mapping.from(standard.LX).when([standard.LB, standard.RB]).to(actions.Yaw);
this.mapping.from(standard.RX).to(actions.StepYaw);
this.mapping.from(standard.RY).invert().to(actions.Pitch);
this.mapping.from(standard.RY).invert().to(actions.Pitch);
var testMakeAxis = false;
if (testMakeAxis) {
this.mapping.makeAxis(standard.LB, standard.RB).pulse(0.25).scale(40.0).to(actions.StepYaw);
}
var testStepYaw = false;
if (!testMakeAxis && testStepYaw){
this.mapping.from(standard.LB).pulse(0.10).invert().scale(40.0).to(actions.StepYaw);
this.mapping.from(standard.RB).pulse(0.10).scale(15.0).to(actions.StepYaw);
}
var testFunctionSource = false;
if (testFunctionSource) {
this.mapping.from(function(){
return Math.sin(Date.now() / 250);
}).to(actions.Yaw);
}
var testFunctionDest = true;
if (testFunctionDest) {
this.mapping.from(standard.DU).pulse(1.0).to(function(value){
if (value != 0.0) {
print(value);
}
});
}
this.mapping.enable();
this.mappingEnabled = true;
var dumpInputs = false;
if (dumpInputs) {
print("Actions");
for (var prop in Controller.Actions) {
print("\t" + prop);
}
print("Standard");
for (var prop in Controller.Standard) {
print("\t" + prop);
}
print("Hardware");
for (var prop in Controller.Hardware) {
print("\t" + prop);
for (var prop2 in Controller.Hardware[prop]) {
print("\t\t" + prop2);
}
}
print("Done");
}
var that = this;
Script.scriptEnding.connect(function() {
that.onCleanup();
});
}
ControllerTest.prototype.onCleanup = function() {
if (this.mappingEnabled) {
this.mapping.disable();
}
}
new ControllerTest();

View file

@ -45,6 +45,12 @@
green: 255,
blue: 255
};
var TRIGGER_CONTROLS = [
Controller.Standard.LT,
Controller.Standard.RT,
];
PingPongGun.prototype = {
hand: null,
@ -53,11 +59,11 @@
canShoot: false,
canShootTimeout: null,
setRightHand: function() {
this.hand = 'RIGHT';
this.hand = 1;
},
setLeftHand: function() {
this.hand = 'LEFT';
this.hand = 0;
},
startNearGrab: function() {
@ -92,12 +98,7 @@
},
checkTriggerPressure: function(gunHand) {
var handClickString = gunHand + "_HAND_CLICK";
var handClick = Controller.findAction(handClickString);
this.triggerValue = Controller.getActionValue(handClick);
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[gunHand]);
if (this.triggerValue < RELOAD_THRESHOLD) {
// print('RELOAD');
this.canShoot = true;

View file

@ -33,12 +33,17 @@
var MIN_POINT_DISTANCE = 0.01;
var STROKE_WIDTH = 0.02;
var TRIGGER_CONTROLS = [
Controller.Standard.LT,
Controller.Standard.RT,
];
this.setRightHand = function () {
this.hand = 'RIGHT';
this.hand = 1;
}
this.setLeftHand = function () {
this.hand = 'LEFT';
this.hand = 0;
}
this.startNearGrab = function () {
@ -46,11 +51,7 @@
}
this.toggleWithTriggerPressure = function () {
var handClickString = this.whichHand + "_HAND_CLICK";
var handClick = Controller.findAction(handClickString);
this.triggerValue = Controller.getActionValue(handClick);
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.whichHand]);
if (this.triggerValue < DISABLE_SPRAY_THRESHOLD && this.spraying === true) {
this.spraying = false;
this.disableStream();

View file

@ -2,7 +2,12 @@ set(TARGET_NAME interface)
project(${TARGET_NAME})
# set a default root dir for each of our optional externals if it was not passed
set(OPTIONAL_EXTERNALS "Faceshift" "LeapMotion" "RtMidi" "RSSDK" "3DConnexionClient" "iViewHMD")
set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK" "iViewHMD")
if(WIN32)
list(APPEND OPTIONAL_EXTERNALS "3DConnexionClient")
endif()
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
@ -99,15 +104,17 @@ endif()
link_hifi_libraries(shared octree environment gpu gl procedural model render
fbx networking model-networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer ui auto-updater
plugins display-plugins input-plugins)
render-utils entities-renderer ui auto-updater
controllers plugins display-plugins input-plugins )
#fixme find a way to express faceshift as a plugin
target_bullet()
target_glew()
target_opengl()
add_dependency_external_projects(sdl2)
if (WIN32 OR APPLE)
target_faceshift()
endif()
# perform standard include and linking for found externals
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})

View file

@ -1,26 +0,0 @@
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.
You may optionally choose to place this folder in a location outside the repository (so you can re-use with different checkouts and different projects).
If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder faceshift that contains the lib and include folders.
1. Build a Faceshift static library from the fsbinarystream.cpp file.
Windows: Win32 console application; no precompiled header or SDL checks; no ATL or MFC headers; Project Properties, Configuration Type = Static Library (.lib).
2. Copy the library files to the lib folder in your Faceshift folder.
OSX: If you build a release version call it libfaceshift.a. The debug version should be called libfaceshiftd.a.
Windows: The release and debug versions should be called faceshift.lib and faceshiftd.lib, respectively. Copy them into a Win32 folder in your lib folder.
3. Copy the fsbinarystream.h header file from the Faceshift SDK into the include folder in your Faceshift folder.
4. Clear your build directory, run cmake and build, and you should be all set.

View file

@ -1,13 +0,0 @@
Instructions for adding the SDL library (SDL2) to Interface
David Rowe, 11 Jan 2015
You can download the SDL development library from https://www.libsdl.org/. Interface has been tested with version 2.0.3.
1. Copy the include and lib folders into the interface/externals/sdl2 folder.
This readme.txt should be there as well.
You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects).
If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'sdl2' that contains the two folders mentioned above.
2. Clear your build directory, run cmake and build, and you should be all set.

View file

@ -1,10 +0,0 @@
Instructions for adding the Sixense driver to Interface
Andrzej Kapolka, November 18, 2013
1. Copy the Sixense sdk folders (bin, include, lib, and samples) into the interface/external/Sixense folder. This readme.txt should be there as well.
You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects).
If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'sixense' that contains the folders mentioned above.
3. Delete your build directory, run cmake and build, and you should be all set.

View file

@ -0,0 +1,31 @@
{
"name": "Hydra to Standard",
"channels": [
{ "from": "Hydra.LY", "filters": "invert", "to": "Standard.LY" },
{ "from": "Hydra.LX", "to": "Standard.LX" },
{ "from": "Hydra.LT", "to": "Standard.LT" },
{ "from": "Hydra.RY", "filters": "invert", "to": "Standard.RY" },
{ "from": "Hydra.RX", "to": "Standard.RX" },
{ "from": "Hydra.RT", "to": "Standard.RT" },
{ "from": "Hydra.LB", "to": "Standard.LB" },
{ "from": "Hydra.LS", "to": "Standard.LS" },
{ "from": "Hydra.RB", "to": "Standard.RB" },
{ "from": "Hydra.RS", "to": "Standard.RS" },
{ "from": "Hydra.L0", "to": "Standard.Back" },
{ "from": "Hydra.L1", "to": "Standard.DL" },
{ "from": "Hydra.L2", "to": "Standard.DD" },
{ "from": "Hydra.L3", "to": "Standard.DR" },
{ "from": "Hydra.L4", "to": "Standard.DU" },
{ "from": "Hydra.R0", "to": "Standard.Start" },
{ "from": "Hydra.R1", "to": "Standard.X" },
{ "from": "Hydra.R2", "to": "Standard.A" },
{ "from": "Hydra.R3", "to": "Standard.B" },
{ "from": "Hydra.R4", "to": "Standard.Y" },
{ "from": "Hydra.LeftHand", "to": "Standard.LeftHand" },
{ "from": "Hydra.RightHand", "to": "Standard.RightHand" }
]
}

View file

@ -0,0 +1,86 @@
{
"name": "Keyboard/Mouse to Actions",
"channels": [
{ "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.D", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.E", "when": "Keyboard.Shift", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.05 } ] },
{ "from": "Keyboard.C", "when": "Keyboard.Shift", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.05 } ] },
{ "from": "Keyboard.S", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" },
{ "from": "Keyboard.W", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" },
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
"when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseClick" ],
"to": "Actions.StepYaw",
"filters":
[
"constrainToInteger",
{ "type": "pulse", "interval": 0.5 },
{ "type": "scale", "scale": 15 }
]
},
{ "from": { "makeAxis" : [
["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"],
["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"]
]
},
"when": [ "Application.InHMD", "Application.ComfortMode" ],
"to": "Actions.StepYaw",
"filters":
[
{ "type": "pulse", "interval": 0.5 },
{ "type": "scale", "scale": 15 }
]
},
{ "from": { "makeAxis" : [
["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"],
["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"]
]
},
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
"when": "Keyboard.RightMouseClick",
"to": "Actions.Yaw"
},
{ "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" },
{ "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" },
{ "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" },
{ "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" },
{ "from": "Keyboard.Left", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Right", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.Down", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" },
{ "from": "Keyboard.Up", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" },
{ "from": "Keyboard.Up", "to": "Actions.LONGITUDINAL_FORWARD" },
{ "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" },
{ "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" },
{ "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" },
{ "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_UP" },
{ "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_DOWN" },
{ "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" },
{ "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" },
{ "from": "Keyboard.MouseWheelUp", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.MouseWheelDown", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.MouseWheelLeft", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.02 } ]},
{ "from": "Keyboard.MouseWheelRight", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.02 } ]},
{ "from": "Keyboard.Space", "to": "Actions.SHIFT" },
{ "from": "Keyboard.R", "to": "Actions.ACTION1" },
{ "from": "Keyboard.T", "to": "Actions.ACTION2" }
]
}

View file

@ -0,0 +1,24 @@
{
"name": "Full Mapping config including the standard hydra and gamepad and one more thing",
"mappings": [
{ "src": "./mapping-hydra.json" },
{ "src": "./mapping-xbox.json" },
{
"name": "example mapping for standard to js function",
"channels": [ {
"from": "Standard.B",
"to": {
"type":"js",
"function": "function(value){ print(\"Standard.B = \" + value );}"
}
}, {
"from": "Standard.B",
"to": {
"type":"js",
"src": "http://www.theNextBigThing.com/hifiInputSignalHandler.js"
}
}
]
}
]
}

View file

@ -0,0 +1,36 @@
{
"name": "example mapping from Standard to actions",
"channels": [ {
"from": "Standard.LY",
"filters": [ {
"type": "clamp",
"min": 0,
"max": 1
}
],
"to": "Actions.Forward"
}, {
"from": "Standard.LY",
"filters": [ {
"type": "clamp",
"min": -1,
"max": 0
}, {
"type": "invert"
}
],
"to": "Actions.Backward"
}, {
"from": "Standard.LX",
"filters": [ {
"type": "scale",
"scale": 2.0
}
],
"to": "Actions.Yaw"
}, {
"from": "Standard.A",
"to": "Actions.Action0"
}
]
}

View file

@ -0,0 +1,43 @@
{
"name": "Standard to Action",
"channels": [
{ "from": "Standard.LY", "to": "Actions.TranslateZ" },
{ "from": "Standard.LX", "to": "Actions.TranslateX" },
{ "from": "Standard.RX", "to": "Actions.Yaw" },
{ "from": "Standard.RY", "to": "Actions.Pitch" },
{
"from": "Standard.DU",
"to": "Actions.LONGITUDINAL_FORWARD",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DD",
"to": "Actions.LONGITUDINAL_BACKWARD",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DR",
"to": "Actions.LATERAL_RIGHT",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{
"from": "Standard.DL",
"to": "Actions.LATERAL_LEFT",
"filters": [ { "type": "scale", "scale": 0.5 } ]
},
{ "from": "Standard.Y", "to": "Actions.VERTICAL_UP" },
{ "from": "Standard.X", "to": "Actions.VERTICAL_DOWN" },
{
"from": "Standard.RT",
"to": "Actions.BOOM_IN",
"filters": [ { "type": "scale", "scale": 0.1 } ]
},
{
"from": "Standard.LT",
"to": "Actions.BOOM_OUT",
"filters": [ { "type": "scale", "scale": 0.1 } ]
},
{ "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" },
{ "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" }
]
}

View file

@ -0,0 +1,39 @@
{
"name": "Standard to Action",
"channels": [
{ "from": "Standard.LY", "to": "Actions.TranslateZ" },
{ "from": "Standard.LX", "to": "Actions.TranslateX" },
{ "from": "Standard.RX",
"when": [ "Application.InHMD", "Application.ComfortMode" ],
"to": "Actions.StepYaw",
"filters":
[
{ "type": "pulse", "interval": 0.5 },
{ "type": "scale", "scale": 15 }
]
},
{ "from": "Standard.RX", "to": "Actions.Yaw" },
{ "from": "Standard.RY", "filters": "invert", "to": "Actions.TranslateY" },
{ "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" },
{ "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" },
{ "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" },
{ "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" },
{ "from": "Standard.LeftSecondaryThumb", "to": "Actions.CycleCamera" },
{ "from": "Standard.RightSecondaryThumb", "to": "Actions.ContextMenu" },
{ "from": "Standard.LT", "to": "Actions.LeftHandClick" },
{ "from": "Standard.RT", "to": "Actions.RightHandClick" },
{ "from": "Standard.LeftHand", "to": "Actions.LeftHand" },
{ "from": "Standard.RightHand", "to": "Actions.RightHand" }
]
}

View file

@ -0,0 +1,26 @@
{
"name": "Vive to Standard",
"channels": [
{ "from": "Vive.LY", "filters": [ "invert", { "type": "deadZone", "min": 0.7 } ], "to": "Standard.LY" },
{ "from": "Vive.LX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.LX" },
{ "from": "Vive.LT", "to": "Standard.LT" },
{ "from": "Vive.LB", "to": "Standard.LB" },
{ "from": "Vive.LS", "to": "Standard.LS" },
{ "from": "Vive.RY", "filters": "invert", "to": "Standard.RY" },
{ "from": "Vive.RX", "to": "Standard.RX" },
{ "from": "Vive.RT", "to": "Standard.RT" },
{ "from": "Vive.RB", "to": "Standard.RB" },
{ "from": "Vive.RS", "to": "Standard.RS" },
{ "from": "Vive.LeftPrimaryThumb", "to": "Standard.LeftPrimaryThumb" },
{ "from": "Vive.RightPrimaryThumb", "to": "Standard.RightPrimaryThumb" },
{ "from": "Vive.LeftSecondaryThumb", "to": "Standard.LeftSecondaryThumb" },
{ "from": "Vive.RightSecondaryThumb", "to": "Standard.RightSecondaryThumb" },
{ "from": "Vive.LeftHand", "to": "Standard.LeftHand" },
{ "from": "Vive.RightHand", "to": "Standard.RightHand" }
]
}

View file

@ -0,0 +1,29 @@
{
"name": "XBox to Standard",
"channels": [
{ "from": "GamePad.LY", "to": "Standard.LY" },
{ "from": "GamePad.LX", "to": "Standard.LX" },
{ "from": "GamePad.LT", "to": "Standard.LT" },
{ "from": "GamePad.LB", "to": "Standard.LB" },
{ "from": "GamePad.LS", "to": "Standard.LS" },
{ "from": "GamePad.RY", "to": "Standard.RY" },
{ "from": "GamePad.RX", "to": "Standard.RX" },
{ "from": "GamePad.RT", "to": "Standard.RT" },
{ "from": "GamePad.RB", "to": "Standard.RB" },
{ "from": "GamePad.RS", "to": "Standard.RS" },
{ "from": "GamePad.Back", "to": "Standard.Back" },
{ "from": "GamePad.Start", "to": "Standard.Start" },
{ "from": "GamePad.DU", "to": "Standard.DU" },
{ "from": "GamePad.DD", "to": "Standard.DD" },
{ "from": "GamePad.DL", "to": "Standard.DL" },
{ "from": "GamePad.DR", "to": "Standard.DR" },
{ "from": "GamePad.A", "to": "Standard.A" },
{ "from": "GamePad.B", "to": "Standard.B" },
{ "from": "GamePad.X", "to": "Standard.X" },
{ "from": "GamePad.Y", "to": "Standard.Y" }
]
}

View file

@ -197,7 +197,7 @@
"id": "rightHandOpen",
"type": "clip",
"data": {
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx",
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_right.fbx",
"startFrame": 0.0,
"endFrame": 0.0,
"timeScale": 1.0,
@ -209,9 +209,9 @@
"id": "rightHandClose",
"type": "clip",
"data": {
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/squeeze_hands/right_hand_anim.fbx",
"startFrame": 15.0,
"endFrame": 15.0,
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_right.fbx",
"startFrame": 10.0,
"endFrame": 10.0,
"timeScale": 1.0,
"loopFlag": true
},
@ -346,7 +346,7 @@
"id": "leftHandOpen",
"type": "clip",
"data": {
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx",
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_left.fbx",
"startFrame": 0.0,
"endFrame": 0.0,
"timeScale": 1.0,
@ -358,9 +358,9 @@
"id": "leftHandClose",
"type": "clip",
"data": {
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/squeeze_hands/left_hand_anim.fbx",
"startFrame": 15.0,
"endFrame": 15.0,
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_left.fbx",
"startFrame": 10.0,
"endFrame": 10.0,
"timeScale": 1.0,
"loopFlag": true
},

View file

@ -0,0 +1,111 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
Rectangle {
id: root
property int size: 64
width: size
height: size
color: 'black'
property int controlId: 0
property real value: 0.5
property int scrollWidth: 1
property real min: 0.0
property real max: 1.0
property bool log: false
property real range: max - min
property color lineColor: 'yellow'
property bool bar: false
property real lastHeight: -1
property string label: ""
function update() {
value = Controller.getValue(controlId);
if (log) {
var log = Math.log(10) / Math.log(Math.abs(value));
var sign = Math.sign(value);
value = log * sign;
}
canvas.requestPaint();
}
function drawHeight() {
if (value < min) {
return 0;
}
if (value > max) {
return height;
}
return ((value - min) / range) * height;
}
Timer {
interval: 50; running: true; repeat: true
onTriggered: root.update()
}
Canvas {
id: canvas
anchors.fill: parent
antialiasing: false
Text {
anchors.top: parent.top
text: root.label
color: 'white'
}
Text {
anchors.right: parent.right
anchors.top: parent.top
text: root.max
color: 'white'
}
Text {
anchors.right: parent.right
anchors.bottom: parent.bottom
text: root.min
color: 'white'
}
function scroll() {
var ctx = canvas.getContext('2d');
var image = ctx.getImageData(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(image, -root.scrollWidth, 0, canvas.width, canvas.height)
ctx.restore()
}
onPaint: {
scroll();
var ctx = canvas.getContext('2d');
ctx.save();
var currentHeight = root.drawHeight();
if (root.lastHeight == -1) {
root.lastHeight = currentHeight
}
// var x = canvas.width - root.drawWidth;
// var y = canvas.height - drawHeight;
// ctx.fillStyle = root.color
// ctx.fillRect(x, y, root.drawWidth, root.bar ? drawHeight : 1)
// ctx.fill();
// ctx.restore()
ctx.beginPath();
ctx.lineWidth = 1
ctx.strokeStyle = root.lineColor
ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight)
ctx.stroke()
ctx.restore()
root.lastHeight = currentHeight
}
}
}

View file

@ -0,0 +1,161 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "controller"
import "controls" as HifiControls
import "styles"
HifiControls.VrDialog {
id: root
HifiConstants { id: hifi }
title: "Controller Test"
resizable: true
contentImplicitWidth: clientArea.implicitWidth
contentImplicitHeight: clientArea.implicitHeight
backgroundColor: "beige"
property var actions: Controller.Actions
property var standard: Controller.Standard
property var hydra: null
property var testMapping: null
property bool testMappingEnabled: false
property var xbox: null
function buildMapping() {
testMapping = Controller.newMapping();
testMapping.fromQml(standard.RY).invert().toQml(actions.Pitch);
testMapping.fromQml(function(){
return Math.sin(Date.now() / 250);
}).toQml(actions.Yaw);
//testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw);
// Step yaw takes a number of degrees
testMapping.fromQml(standard.LB).pulse(0.10).invert().scale(40.0).toQml(actions.StepYaw);
testMapping.fromQml(standard.RB).pulse(0.10).scale(15.0).toQml(actions.StepYaw);
testMapping.fromQml(standard.RX).scale(15.0).toQml(actions.StepYaw);
}
function toggleMapping() {
testMapping.enable(!testMappingEnabled);
testMappingEnabled = !testMappingEnabled;
}
Component.onCompleted: {
enabled = true
var xboxRegex = /^GamePad/;
var hydraRegex = /^Hydra/;
for (var prop in Controller.Hardware) {
if(xboxRegex.test(prop)) {
root.xbox = Controller.Hardware[prop]
print("found xbox")
continue
}
if (hydraRegex.test(prop)) {
root.hydra = Controller.Hardware[prop]
print("found hydra")
continue
}
}
}
Column {
id: clientArea
spacing: 12
x: root.clientX
y: root.clientY
Row {
spacing: 8
Button {
text: !root.testMapping ? "Build Mapping" : (root.testMappingEnabled ? "Disable Mapping" : "Enable Mapping")
onClicked: {
if (!root.testMapping) {
root.buildMapping()
} else {
root.toggleMapping();
}
}
}
}
Row {
Standard { device: root.standard; label: "Standard"; width: 180 }
}
Row {
spacing: 8
Xbox { device: root.xbox; label: "XBox"; width: 180 }
Hydra { device: root.hydra; width: 180 }
}
Row {
spacing: 4
ScrollingGraph {
controlId: Controller.Actions.Yaw
label: "Yaw"
min: -2.0
max: 2.0
size: 64
}
ScrollingGraph {
controlId: Controller.Actions.YawLeft
label: "Yaw Left"
min: -2.0
max: 2.0
size: 64
}
ScrollingGraph {
controlId: Controller.Actions.YawRight
label: "Yaw Right"
min: -2.0
max: 2.0
size: 64
}
ScrollingGraph {
controlId: Controller.Actions.StepYaw
label: "StepYaw"
min: -20.0
max: 20.0
size: 64
}
}
Row {
ScrollingGraph {
controlId: Controller.Actions.TranslateZ
label: "TranslateZ"
min: -2.0
max: 2.0
size: 64
}
ScrollingGraph {
controlId: Controller.Actions.Forward
label: "Forward"
min: -2.0
max: 2.0
size: 64
}
ScrollingGraph {
controlId: Controller.Actions.Backward
label: "Backward"
min: -2.0
max: 2.0
size: 64
}
}
}
} // dialog

View file

@ -0,0 +1,45 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
Item {
id: root
property int size: 64
width: size
height: size
property int controlId: 0
property real value: 0
property color color: 'black'
function update() {
value = controlId ? Controller.getValue(controlId) : 0;
canvas.requestPaint();
}
Timer {
interval: 50; running: true; repeat: true
onTriggered: root.update();
}
Canvas {
id: canvas
anchors.fill: parent
antialiasing: false
onPaint: {
var ctx = canvas.getContext('2d');
ctx.save();
ctx.beginPath();
ctx.clearRect(0, 0, canvas.width, canvas.height);
var fillHeight = root.value * canvas.height;
ctx.fillStyle = 'red'
ctx.fillRect(0, canvas.height - fillHeight, canvas.width, fillHeight);
ctx.fill();
ctx.restore()
}
}
}

View file

@ -0,0 +1,55 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
Item {
id: root
property int size: 64
width: size
height: size
property bool invertY: false
property int halfSize: size / 2
property var controlIds: [ 0, 0 ]
property vector2d value: Qt.vector2d(0, 0)
function update() {
value = Qt.vector2d(
controlIds[0] ? Controller.getValue(controlIds[0]) : 0,
controlIds[1] ? Controller.getValue(controlIds[1]) : 0
);
if (root.invertY) {
value.y = value.y * -1.0
}
canvas.requestPaint();
}
Timer {
interval: 50; running: controlIds; repeat: true
onTriggered: root.update()
}
Canvas {
id: canvas
anchors.fill: parent
antialiasing: false
onPaint: {
var ctx = canvas.getContext('2d');
ctx.save();
ctx.beginPath();
ctx.clearRect(0, 0, width, height);
ctx.fill();
ctx.translate(root.halfSize, root.halfSize)
ctx.lineWidth = 4
ctx.strokeStyle = Qt.rgba(Math.max(Math.abs(value.x), Math.abs(value.y)), 0, 0, 1)
ctx.moveTo(0, 0).lineTo(root.value.x * root.halfSize, root.value.y * root.halfSize)
ctx.stroke()
ctx.restore()
}
}
}

View file

@ -0,0 +1,34 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "hydra"
Item {
id: root
width: 480
height: width * 3.0 / 4.0
property var device
property real scale: width / 480
property real rightOffset: (width / 2) * scale
Image {
anchors.fill: parent
source: "hydra/hydra.png"
HydraStick {
leftStick: true
scale: root.scale
device: root.device
}
HydraStick {
leftStick: false
scale: root.scale
device: root.device
}
}
}

View file

@ -0,0 +1,35 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "xbox"
Item {
id: root
property real aspect: 300.0 / 215.0
width: 300
height: width / aspect
property var device
property string label: ""
property real scale: width / 300.0
Xbox {
width: root.width; height: root.height
device: root.device
}
// Left primary
ToggleButton {
x: 0; y: parent.height - height;
controlId: root.device.LeftPrimaryThumb
width: 16 * root.scale; height: 16 * root.scale
}
// Left primary
ToggleButton {
x: parent.width - width; y: parent.height - height;
controlId: root.device.RightPrimaryThumb
width: 16 * root.scale; height: 16 * root.scale
}
}

View file

@ -0,0 +1,45 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
Item {
id: root
width: size
height: size
property int size: 64
property int controlId: 0
property real value: 0
property color color: 'black'
function update() {
value = controlId ? Controller.getValue(controlId) : 0;
canvas.requestPaint();
}
Timer {
interval: 50; running: root.controlId; repeat: true
onTriggered: root.update()
}
Canvas {
id: canvas
anchors.fill: parent
antialiasing: false
onPaint: {
var ctx = canvas.getContext('2d');
ctx.save();
ctx.beginPath();
ctx.clearRect(0, 0, width, height);
if (root.value > 0.0) {
ctx.fillStyle = root.color
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
ctx.fill();
ctx.restore()
}
}
}

View file

@ -0,0 +1,104 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import "xbox"
Item {
id: root
property real aspect: 300.0 / 215.0
width: 300
height: width / aspect
property var device
property string label: ""
property real scale: width / 300.0
Image {
Text {
anchors.left: parent.left
anchors.top: parent.top
text: root.label
visible: root.label != ""
}
anchors.fill: parent
source: "xbox/xbox360-controller-md.png"
LeftAnalogStick {
device: root.device
x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2
}
// Left stick press
ToggleButton {
controlId: root.device.LS
width: 16 * root.scale; height: 16 * root.scale
x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2
}
RightAnalogStick {
device: root.device
x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2
}
// Right stick press
ToggleButton {
controlId: root.device.RS
width: 16 * root.scale; height: 16 * root.scale
x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2
}
// Left trigger
AnalogButton {
controlId: root.device.LT
width: 8; height: 64
x: (20 * root.scale); y: (7 * root.scale)
}
// Right trigger
AnalogButton {
controlId: root.device.RT
width: 8; height: 64
x: (272 * root.scale); y: (7 * root.scale)
}
// Left bumper
ToggleButton {
controlId: root.device.LB
width: 32 * root.scale; height: 16 * root.scale
x: (40 * root.scale); y: (7 * root.scale)
}
// Right bumper
ToggleButton {
controlId: root.device.RB
width: 32 * root.scale; height: 16 * root.scale
x: (root.width - width) - (40 * root.scale); y: (7 * root.scale)
}
DPad {
device: root.device
size: 48 * root.scale
x: (80 * root.scale); y: (71 * root.scale)
}
XboxButtons {
device: root.device
size: 65 * root.scale
x: (206 * root.scale); y: (19 * root.scale)
}
ToggleButton {
controlId: root.device.Back
width: 16 * root.scale; height: 12 * root.scale
x: (112 * root.scale); y: (45 * root.scale)
}
ToggleButton {
controlId: root.device.Start
width: 16 * root.scale; height: 12 * root.scale
x: (177 * root.scale); y: (45 * root.scale)
}
}
}

View file

@ -0,0 +1,18 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."
Item {
id: root
width: 72 * scale
height: 48 * scale
property var device
property real scale: 1.0
property bool leftStick: true
}

View file

@ -0,0 +1,91 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."
Item {
id: root
property var device
property real scale: 1.0
property bool leftStick: true
width: parent.width / 2; height: parent.height
x: leftStick ? 0 : parent.width / 2
Text {
x: parent.width / 2 - width / 2; y: parent.height / 2 - height / 2
text: root.leftStick ? "L" : "R"
color: 'green'
}
// Analog Stick
AnalogStick {
size: 64 * root.scale
x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2; z: 100
invertY: true
controlIds: [
root.leftStick ? root.device.LX : root.device.RX,
root.leftStick ? root.device.LY : root.device.RY
]
}
// Stick press
ToggleButton {
controlId: root.leftStick ? root.device.LS : root.device.RS
width: 16 * root.scale; height: 16 * root.scale
x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2;
color: 'yellow'
}
// Trigger
AnalogButton {
controlId: root.leftStick ? root.device.LT : root.device.RT
width: 8 * root.scale ; height: 64 * root.scale
y: 24 * root.scale
x: root.leftStick ? (48 * root.scale) : root.width - (48 * root.scale) - width / 2
}
// Bumper
ToggleButton {
controlId: root.leftStick ? root.device.LB : root.device.RB
height: 16 * root.scale; width: 32 * root.scale
x: 128 * root.scale - width / 2; y: 24 * root.scale
color: 'red'
}
ToggleButton {
controlId: root.leftStick ? root.device.L0 : root.device.R0
height: 16 * root.scale; width: 4 * root.scale
x: 128 * root.scale - width / 2; y: 109 * root.scale
color: 'yellow'
}
ToggleButton {
controlId: root.leftStick ? root.device.L1 : root.device.R1
width: 16 * root.scale; height: 16 * root.scale
x: 103 * root.scale - width / 2; y: 100 * root.scale - height / 2
color: 'yellow'
}
ToggleButton {
controlId: root.leftStick ? root.device.L2 : root.device.R2
width: 16 * root.scale; height: 16 * root.scale
x: 148 * root.scale - width / 2; y: 100 * root.scale - height / 2
color: 'yellow'
}
ToggleButton {
controlId: root.leftStick ? root.device.L3 : root.device.R3
width: 16 * root.scale; height: 16 * root.scale
x: 97 * root.scale - width / 2; y: 76 * root.scale - height / 2
color: 'yellow'
}
ToggleButton {
controlId: root.leftStick ? root.device.L4 : root.device.R4
width: 16 * root.scale; height: 16 * root.scale
x: 155 * root.scale - width / 2; y: 76 * root.scale - height / 2
color: 'yellow'
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -0,0 +1,42 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."
Item {
id: root
property int size: 64
width: size
height: size
property int spacer: size / 3
property var device
property color color: 'black'
ToggleButton {
controlId: device.Up
x: spacer
width: spacer; height: spacer
}
ToggleButton {
controlId: device.Left
y: spacer
width: spacer; height: spacer
}
ToggleButton {
controlId: device.Right
x: spacer * 2; y: spacer
width: spacer; height: spacer
}
ToggleButton {
controlId: device.Down
x: spacer; y: spacer * 2
width: spacer; height: spacer
}
}

View file

@ -0,0 +1,21 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."
Item {
id: root
property int size: 64
width: size
height: size
property var device
AnalogStick {
size: size
controlIds: [ device.LX, device.LY ]
}
}

View file

@ -0,0 +1,21 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."
Item {
id: root
property int size: 64
width: size
height: size
property var device
AnalogStick {
size: size
controlIds: [ device.RX, device.RY ]
}
}

View file

@ -0,0 +1,46 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.0
import ".."
Item {
id: root
property int size: 64
width: size
height: size
property int spacer: size / 3
property var device
property color color: 'black'
ToggleButton {
controlId: device.Y
x: spacer
width: spacer; height: spacer
color: 'yellow'
}
ToggleButton {
controlId: device.X
y: spacer
width: spacer; height: spacer
color: 'blue'
}
ToggleButton {
controlId: device.B
x: spacer * 2; y: spacer
width: spacer; height: spacer
color: 'red'
}
ToggleButton {
controlId: device.A
x: spacer; y: spacer * 2
width: spacer; height: spacer
color: 'green'
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View file

@ -17,27 +17,32 @@
#include <glm/gtx/vector_angle.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <QAbstractNativeEventFilter>
#include <QActionGroup>
#include <QDebug>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QImage>
#include <QFileDialog>
#include <QInputDialog>
#include <QKeyEvent>
#include <QMediaPlayer>
#include <QMenuBar>
#include <QMessageBox>
#include <QMimeData>
#include <QMouseEvent>
#include <QNetworkDiskCache>
#include <QObject>
#include <QScreen>
#include <QTimer>
#include <QUrl>
#include <QWheelEvent>
#include <QWindow>
#include <QtCore/QDebug>
#include <QtCore/QObject>
#include <QtCore/QUrl>
#include <QtCore/QTimer>
#include <QtCore/QAbstractNativeEventFilter>
#include <QtCore/QMimeData>
#include <QtGui/QScreen>
#include <QtGui/QImage>
#include <QtGui/QWheelEvent>
#include <QtGui/QWindow>
#include <QtQml/QQmlContext>
#include <QtGui/QKeyEvent>
#include <QtGui/QMouseEvent>
#include <QtGui/QDesktopServices>
#include <QtWidgets/QActionGroup>
#include <QtWidgets/QDesktopWidget>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QMessageBox>
#include <QtMultimedia/QMediaPlayer>
#include <QtNetwork/QNetworkDiskCache>
#include <AccountManager.h>
#include <AddressManager.h>
@ -60,7 +65,8 @@
#include <InfoView.h>
#include <input-plugins/InputPlugin.h>
#include <input-plugins/Joystick.h> // this should probably be removed
#include <input-plugins/UserInputMapper.h>
#include <controllers/UserInputMapper.h>
#include <controllers/StateController.h>
#include <LogHandler.h>
#include <MainWindow.h>
#include <MessageDialog.h>
@ -120,6 +126,7 @@
#include "scripting/SettingsScriptingInterface.h"
#include "scripting/WebWindowClass.h"
#include "scripting/WindowScriptingInterface.h"
#include "scripting/ControllerScriptingInterface.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h"
#endif
@ -137,6 +144,7 @@
#include "ui/UpdateDialog.h"
#include "Util.h"
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
#if defined(Q_OS_WIN)
@ -316,6 +324,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<EntityScriptingInterface>();
DependencyManager::set<WindowScriptingInterface>();
DependencyManager::set<HMDScriptingInterface>();
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
DependencyManager::set<SpeechRecognizer>();
#endif
@ -327,7 +336,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<InterfaceActionFactory>();
DependencyManager::set<AssetClient>();
DependencyManager::set<UserInputMapper>();
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
return true;
}
@ -339,49 +348,50 @@ int _keyboardFocusHighlightID{ -1 };
PluginContainer* _pluginContainer;
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
QApplication(argc, argv),
_dependencyManagerIsSetup(setupEssentials(argc, argv)),
_window(new MainWindow(desktop())),
_toolWindow(NULL),
_undoStackScriptingInterface(&_undoStack),
_frameCount(0),
_fps(60.0f),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
_entities(true, this, this),
_entityClipboardRenderer(false, this, this),
_entityClipboard(new EntityTree()),
_lastQueriedTime(usecTimestampNow()),
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
_firstRun("firstRun", true),
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
_scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION),
_fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
_scaleMirror(1.0f),
_rotateMirror(0.0f),
_raiseMirror(0.0f),
_lastMouseMoveWasSimulated(false),
_enableProcessOctreeThread(true),
_runningScriptsWidget(NULL),
_runningScriptsWidgetWasVisible(false),
_lastNackTime(usecTimestampNow()),
_lastSendDownstreamAudioStats(usecTimestampNow()),
_aboutToQuit(false),
_notifiedPacketVersionMismatchThisDomain(false),
_maxOctreePPS(maxOctreePacketsPerSecond.get()),
_lastFaceTrackerUpdate(0)
QApplication(argc, argv),
_dependencyManagerIsSetup(setupEssentials(argc, argv)),
_window(new MainWindow(desktop())),
_toolWindow(NULL),
_undoStackScriptingInterface(&_undoStack),
_frameCount(0),
_fps(60.0f),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
_entities(true, this, this),
_entityClipboardRenderer(false, this, this),
_entityClipboard(new EntityTree()),
_lastQueriedTime(usecTimestampNow()),
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
_firstRun("firstRun", true),
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
_scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION),
_fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
_scaleMirror(1.0f),
_rotateMirror(0.0f),
_raiseMirror(0.0f),
_lastMouseMoveWasSimulated(false),
_enableProcessOctreeThread(true),
_runningScriptsWidget(NULL),
_runningScriptsWidgetWasVisible(false),
_lastNackTime(usecTimestampNow()),
_lastSendDownstreamAudioStats(usecTimestampNow()),
_aboutToQuit(false),
_notifiedPacketVersionMismatchThisDomain(false),
_maxOctreePPS(maxOctreePacketsPerSecond.get()),
_lastFaceTrackerUpdate(0)
{
thread()->setObjectName("Main Thread");
setInstance(this);
auto controllerScriptingInterface = DependencyManager::get<controller::ScriptingInterface>().data();
_controllerScriptingInterface = dynamic_cast<ControllerScriptingInterface*>(controllerScriptingInterface);
// to work around the Qt constant wireless scanning, set the env for polling interval very high
const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit();
qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT);
_entityClipboard->createRootElement();
_pluginContainer = new PluginContainerProxy();
Plugin::setContainer(_pluginContainer);
#ifdef Q_OS_WIN
installNativeEventFilter(&MyNativeEventFilter::getInstance());
#endif
@ -440,7 +450,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
audioIO->moveToThread(audioThread);
auto& audioScriptingInterface = AudioScriptingInterface::getInstance();
connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);
connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
@ -479,7 +489,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived);
connect(&domainHandler, &DomainHandler::hostnameChanged,
DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
// update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one
const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
@ -490,7 +500,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// if we get a domain change, immediately attempt update location in metaverse server
connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain,
discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);
connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled);
@ -529,14 +539,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);
connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
#ifdef _WIN32
#ifdef _WIN32
WSADATA WsaData;
int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData);
#endif
int wsaresult = WSAStartup(MAKEWORD(2, 2), &WsaData);
#endif
// tell the NodeList instance who to tell the domain server we care about
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
<< NodeType::EntityServer << NodeType::AssetServer);
<< NodeType::EntityServer << NodeType::AssetServer);
// connect to the packet sent signal of the _entityEditSender
connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);
@ -609,29 +619,44 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// hook up bandwidth estimator
QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
connect(nodeList.data(), &LimitedNodeList::dataSent,
bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData);
bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData);
connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived,
bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData);
bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData);
connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded,
this, &Application::checkSkeleton, Qt::QueuedConnection);
this, &Application::checkSkeleton, Qt::QueuedConnection);
// Setup the userInputMapper with the actions
auto userInputMapper = DependencyManager::get<UserInputMapper>();
connect(userInputMapper.data(), &UserInputMapper::actionEvent, &_controllerScriptingInterface, &AbstractControllerScriptingInterface::actionEvent);
connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
if (state) {
switch (action) {
case UserInputMapper::Action::TOGGLE_MUTE:
if (action == controller::toInt(controller::Action::TOGGLE_MUTE)) {
DependencyManager::get<AudioClient>()->toggleMute();
break;
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
cycleCamera();
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
VrMenu::toggle(); // show context menu even on non-stereo displays
}
}
});
// A new controllerInput device used to reflect current values from the application state
_applicationStateDevice = std::make_shared<controller::StateController>();
_applicationStateDevice->addInputVariant(QString("InHMD"), controller::StateController::ReadLambda([]() -> float {
return (float)qApp->getAvatarUpdater()->isHMDMode();
}));
_applicationStateDevice->addInputVariant(QString("ComfortMode"), controller::StateController::ReadLambda([]() -> float {
return (float)Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode);
}));
userInputMapper->registerDevice(_applicationStateDevice);
// Setup the keyboardMouseDevice and the user input mapper with the default bindings
_keyboardMouseDevice->registerToUserInputMapper(*userInputMapper);
_keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper);
userInputMapper->registerDevice(_keyboardMouseDevice);
userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID());
// check first run...
if (_firstRun.get()) {
@ -704,8 +729,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// Now that menu is initalized we can sync myAvatar with it's state.
getMyAvatar()->updateMotionBehaviorFromMenu();
#if 0
// the 3Dconnexion device wants to be initiliazed after a window is displayed.
ConnexionClient::getInstance().init();
#endif
auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket");
@ -792,6 +819,9 @@ void Application::cleanupBeforeQuit() {
AnimDebugDraw::getInstance().shutdown();
// FIXME: once we move to shared pointer for the INputDevice we shoud remove this naked delete:
_applicationStateDevice.reset();
if (_keyboardFocusHighlightID > 0) {
getOverlays().deleteOverlay(_keyboardFocusHighlightID);
_keyboardFocusHighlightID = -1;
@ -902,7 +932,10 @@ Application::~Application() {
Leapmotion::destroy();
RealSense::destroy();
#if 0
ConnexionClient::getInstance().destroy();
#endif
qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages
}
@ -975,6 +1008,8 @@ void Application::initializeUi() {
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
offscreenUi->load("Root.qml");
offscreenUi->load("RootMenu.qml");
auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
offscreenUi->getRootContext()->setContextProperty("Controller", scriptingInterface.data());
_glWidget->installEventFilter(offscreenUi.data());
VrMenu::load();
VrMenu::executeQueuedLambdas();
@ -1003,7 +1038,10 @@ void Application::initializeUi() {
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
QString name = inputPlugin->getName();
if (name == KeyboardMouseDevice::NAME) {
_keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); // TODO: this seems super hacky
auto kbm = static_cast<KeyboardMouseDevice*>(inputPlugin.data());
// FIXME incredibly evil.... _keyboardMouseDevice is now owned by
// both a QSharedPointer and a std::shared_ptr
_keyboardMouseDevice = std::shared_ptr<KeyboardMouseDevice>(kbm);
}
}
updateInputModes();
@ -1456,7 +1494,7 @@ bool Application::event(QEvent* event) {
}
if (HFActionEvent::types().contains(event->type())) {
_controllerScriptingInterface.handleMetaEvent(static_cast<HFMetaEvent*>(event));
_controllerScriptingInterface->handleMetaEvent(static_cast<HFMetaEvent*>(event));
}
return QApplication::event(event);
@ -1470,7 +1508,7 @@ bool Application::eventFilter(QObject* object, QEvent* event) {
}
// Filter out captured keys before they're used for shortcut actions.
if (_controllerScriptingInterface.isKeyCaptured(static_cast<QKeyEvent*>(event))) {
if (_controllerScriptingInterface->isKeyCaptured(static_cast<QKeyEvent*>(event))) {
event->accept();
return true;
}
@ -1485,10 +1523,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
_altPressed = event->key() == Qt::Key_Alt;
_keysPressed.insert(event->key());
_controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts
_controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isKeyCaptured(event)) {
if (_controllerScriptingInterface->isKeyCaptured(event)) {
return;
}
@ -1518,6 +1556,13 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (isMeta) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->load("Browser.qml");
}
break;
case Qt::Key_X:
if (isMeta && isShifted) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->load("TestControllers.qml");
}
break;
@ -1745,17 +1790,15 @@ void Application::keyPressEvent(QKeyEvent* event) {
void Application::keyReleaseEvent(QKeyEvent* event) {
if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) {
if (getActiveDisplayPlugin()->isStereo()) {
VrMenu::toggle();
}
VrMenu::toggle(); // show context menu even on non-stereo displays
}
_keysPressed.remove(event->key());
_controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts
_controllerScriptingInterface->emitKeyReleaseEvent(event); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isKeyCaptured(event)) {
if (_controllerScriptingInterface->isKeyCaptured(event)) {
return;
}
@ -1796,7 +1839,9 @@ void Application::focusOutEvent(QFocusEvent* event) {
inputPlugin->pluginFocusOutEvent();
}
}
#if 0
ConnexionData::getInstance().focusOutEvent();
#endif
// synthesize events for keys currently pressed, since we may not get their release events
foreach (int key, _keysPressed) {
@ -1844,10 +1889,10 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
_entities.mouseMoveEvent(&mappedEvent, deviceID);
_controllerScriptingInterface.emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
if (_controllerScriptingInterface->isMouseCaptured()) {
return;
}
@ -1872,10 +1917,10 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
_entities.mousePressEvent(&mappedEvent, deviceID);
}
_controllerScriptingInterface.emitMousePressEvent(&mappedEvent); // send events to any registered scripts
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
if (_controllerScriptingInterface->isMouseCaptured()) {
return;
}
@ -1897,11 +1942,11 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) {
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
if (_controllerScriptingInterface->isMouseCaptured()) {
return;
}
_controllerScriptingInterface.emitMouseDoublePressEvent(event);
_controllerScriptingInterface->emitMouseDoublePressEvent(event);
}
void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
@ -1917,10 +1962,10 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
_entities.mouseReleaseEvent(&mappedEvent, deviceID);
}
_controllerScriptingInterface.emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
if (_controllerScriptingInterface->isMouseCaptured()) {
return;
}
@ -1943,12 +1988,12 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
if (event->type() == QEvent::TouchUpdate) {
TouchEvent thisEvent(*event, _lastTouchEvent);
_controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
_controllerScriptingInterface->emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent;
}
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isTouchCaptured()) {
if (_controllerScriptingInterface->isTouchCaptured()) {
return;
}
@ -1960,13 +2005,13 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
void Application::touchBeginEvent(QTouchEvent* event) {
_altPressed = false;
TouchEvent thisEvent(*event); // on touch begin, we don't compare to last event
_controllerScriptingInterface.emitTouchBeginEvent(thisEvent); // send events to any registered scripts
_controllerScriptingInterface->emitTouchBeginEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent; // and we reset our last event to this event before we call our update
touchUpdateEvent(event);
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isTouchCaptured()) {
if (_controllerScriptingInterface->isTouchCaptured()) {
return;
}
@ -1979,11 +2024,11 @@ void Application::touchBeginEvent(QTouchEvent* event) {
void Application::touchEndEvent(QTouchEvent* event) {
_altPressed = false;
TouchEvent thisEvent(*event, _lastTouchEvent);
_controllerScriptingInterface.emitTouchEndEvent(thisEvent); // send events to any registered scripts
_controllerScriptingInterface->emitTouchEndEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent;
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isTouchCaptured()) {
if (_controllerScriptingInterface->isTouchCaptured()) {
return;
}
@ -1996,10 +2041,10 @@ void Application::touchEndEvent(QTouchEvent* event) {
void Application::wheelEvent(QWheelEvent* event) {
_altPressed = false;
_controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts
_controllerScriptingInterface->emitWheelEvent(event); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isWheelCaptured()) {
if (_controllerScriptingInterface->isWheelCaptured()) {
return;
}
@ -2164,7 +2209,7 @@ float Application::getAvatarSimrate() {
}
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
InputDevice::setLowVelocityFilter(lowVelocityFilter);
controller::InputDevice::setLowVelocityFilter(lowVelocityFilter);
}
ivec2 Application::getMouse() const {
@ -2578,6 +2623,30 @@ void Application::updateThreads(float deltaTime) {
}
}
void Application::cycleCamera() {
auto menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
menu->setIsOptionChecked(MenuOption::FullscreenMirror, false);
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
} else if (menu->isOptionChecked(MenuOption::FirstPerson)) {
menu->setIsOptionChecked(MenuOption::FirstPerson, false);
menu->setIsOptionChecked(MenuOption::ThirdPerson, true);
} else if (menu->isOptionChecked(MenuOption::ThirdPerson)) {
menu->setIsOptionChecked(MenuOption::ThirdPerson, false);
menu->setIsOptionChecked(MenuOption::FullscreenMirror, true);
} else if (menu->isOptionChecked(MenuOption::IndependentMode)) {
// do nothing if in independe mode
return;
}
cameraMenuChanged(); // handle the menu change
}
void Application::cameraMenuChanged() {
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
@ -2694,13 +2763,9 @@ void Application::update(float deltaTime) {
userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix());
userInputMapper->update(deltaTime);
// This needs to go after userInputMapper->update() because of the keyboard
bool jointsCaptured = false;
auto inputPlugins = PluginManager::getInstance()->getInputPlugins();
foreach(auto inputPlugin, inputPlugins) {
QString name = inputPlugin->getName();
QAction* action = Menu::getInstance()->getActionForOption(name);
if (action && action->isChecked()) {
for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
if (inputPlugin->isActive()) {
inputPlugin->pluginUpdate(deltaTime, jointsCaptured);
if (inputPlugin->isJointController()) {
jointsCaptured = true;
@ -2708,19 +2773,14 @@ void Application::update(float deltaTime) {
}
}
// Dispatch input events
_controllerScriptingInterface.updateInputControllers();
// Transfer the user inputs to the driveKeys
// FIXME can we drop drive keys and just have the avatar read the action states directly?
myAvatar->clearDriveKeys();
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
if (!_controllerScriptingInterface.areActionsCaptured()) {
myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_FORWARD));
myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_BACKWARD));
myAvatar->setDriveKeys(UP, userInputMapper->getActionState(UserInputMapper::VERTICAL_UP));
myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(UserInputMapper::VERTICAL_DOWN));
myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(UserInputMapper::LATERAL_LEFT));
myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(UserInputMapper::LATERAL_RIGHT));
if (!_controllerScriptingInterface->areActionsCaptured()) {
myAvatar->setDriveKeys(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
myAvatar->setDriveKeys(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
myAvatar->setDriveKeys(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X));
if (deltaTime > FLT_EPSILON) {
// For rotations what we really want are meausures of "angles per second" (in order to prevent
// fps-dependent spin rates) so we need to scale the units of the controller contribution.
@ -2728,25 +2788,24 @@ void Application::update(float deltaTime) {
// controllers to provide a delta_per_second value rather than a raw delta.)
const float EXPECTED_FRAME_RATE = 60.0f;
float timeFactor = EXPECTED_FRAME_RATE * deltaTime;
myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(UserInputMapper::PITCH_UP) / timeFactor);
myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(UserInputMapper::PITCH_DOWN) / timeFactor);
myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(UserInputMapper::YAW_LEFT) / timeFactor);
myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(UserInputMapper::YAW_RIGHT) / timeFactor);
myAvatar->setDriveKeys(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH) / timeFactor);
myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW) / timeFactor);
myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW) / timeFactor);
}
}
myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(UserInputMapper::BOOM_IN));
myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(UserInputMapper::BOOM_OUT));
myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
}
UserInputMapper::PoseValue leftHand = userInputMapper->getPoseState(UserInputMapper::LEFT_HAND);
UserInputMapper::PoseValue rightHand = userInputMapper->getPoseState(UserInputMapper::RIGHT_HAND);
controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();
setPalmData(hand, leftHand, deltaTime, LEFT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK));
setPalmData(hand, rightHand, deltaTime, RIGHT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK));
setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK));
setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK));
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {
emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK),
userInputMapper->getActionState(UserInputMapper::SHIFT), LEFT_HAND_INDEX);
emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK),
userInputMapper->getActionState(UserInputMapper::SHIFT), RIGHT_HAND_INDEX);
emulateMouse(hand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK),
userInputMapper->getActionState(controller::Action::SHIFT), HandData::LeftHand);
emulateMouse(hand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK),
userInputMapper->getActionState(controller::Action::SHIFT), HandData::RightHand);
}
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
@ -4807,86 +4866,80 @@ mat4 Application::getHMDSensorPose() const {
return mat4();
}
void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue) {
PalmData* palm;
bool foundHand = false;
for (size_t j = 0; j < hand->getNumPalms(); j++) {
if (hand->getPalms()[j].getSixenseID() == index) {
palm = &(hand->getPalms()[j]);
foundHand = true;
break;
void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) {
// NOTE: the Hand::modifyPalm() will allow the lambda to modify the palm data while ensuring some other user isn't
// reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more
// of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about
// controller::Pose. More work is needed to clean this up.
hand->modifyPalm(whichHand, [&](PalmData& palm) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
palm.setActive(pose.isValid());
// transform from sensor space, to world space, to avatar model space.
glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation());
glm::mat4 sensorToWorldMat = myAvatar->getSensorToWorldMatrix();
glm::mat4 modelMat = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat;
glm::vec3 position = extractTranslation(objectPose);
glm::quat rotation = glm::quat_cast(objectPose);
// Compute current velocity from position change
glm::vec3 rawVelocity;
if (deltaTime > 0.0f) {
rawVelocity = (position - palm.getRawPosition()) / deltaTime;
} else {
rawVelocity = glm::vec3(0.0f);
}
}
if (!foundHand) {
PalmData newPalm(hand);
hand->getPalms().push_back(newPalm);
palm = &(hand->getPalms()[hand->getNumPalms() - 1]);
palm->setSixenseID(index);
}
palm.setRawVelocity(rawVelocity); // meters/sec
palm->setActive(pose.isValid());
// Angular Velocity of Palm
glm::quat deltaRotation = rotation * glm::inverse(palm.getRawRotation());
glm::vec3 angularVelocity(0.0f);
float rotationAngle = glm::angle(deltaRotation);
if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) {
angularVelocity = glm::normalize(glm::axis(deltaRotation));
angularVelocity *= (rotationAngle / deltaTime);
palm.setRawAngularVelocity(angularVelocity);
} else {
palm.setRawAngularVelocity(glm::vec3(0.0f));
}
// transform from sensor space, to world space, to avatar model space.
glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation());
glm::mat4 sensorToWorldMat = getMyAvatar()->getSensorToWorldMatrix();
glm::mat4 modelMat = createMatFromQuatAndPos(getMyAvatar()->getOrientation(), getMyAvatar()->getPosition());
glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat;
if (controller::InputDevice::getLowVelocityFilter()) {
// Use a velocity sensitive filter to damp small motions and preserve large ones with
// no latency.
float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
position = palm.getRawPosition() * velocityFilter + position * (1.0f - velocityFilter);
rotation = safeMix(palm.getRawRotation(), rotation, 1.0f - velocityFilter);
}
palm.setRawPosition(position);
palm.setRawRotation(rotation);
glm::vec3 position = extractTranslation(objectPose);
glm::quat rotation = glm::quat_cast(objectPose);
// Compute current velocity from position change
glm::vec3 rawVelocity;
if (deltaTime > 0.0f) {
rawVelocity = (position - palm->getRawPosition()) / deltaTime;
} else {
rawVelocity = glm::vec3(0.0f);
}
palm->setRawVelocity(rawVelocity); // meters/sec
// Angular Velocity of Palm
glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation());
glm::vec3 angularVelocity(0.0f);
float rotationAngle = glm::angle(deltaRotation);
if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) {
angularVelocity = glm::normalize(glm::axis(deltaRotation));
angularVelocity *= (rotationAngle / deltaTime);
palm->setRawAngularVelocity(angularVelocity);
} else {
palm->setRawAngularVelocity(glm::vec3(0.0f));
}
if (InputDevice::getLowVelocityFilter()) {
// Use a velocity sensitive filter to damp small motions and preserve large ones with
// no latency.
float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
position = palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter);
rotation = safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter);
}
palm->setRawPosition(position);
palm->setRawRotation(rotation);
// Store the one fingertip in the palm structure so we can track velocity
const float FINGER_LENGTH = 0.3f; // meters
const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f);
const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
glm::vec3 oldTipPosition = palm->getTipRawPosition();
if (deltaTime > 0.0f) {
palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);
} else {
palm->setTipVelocity(glm::vec3(0.0f));
}
palm->setTipPosition(newTipPosition);
palm->setTrigger(triggerValue);
// Store the one fingertip in the palm structure so we can track velocity
const float FINGER_LENGTH = 0.3f; // meters
const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f);
const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
glm::vec3 oldTipPosition = palm.getTipRawPosition();
if (deltaTime > 0.0f) {
palm.setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);
} else {
palm.setTipVelocity(glm::vec3(0.0f));
}
palm.setTipPosition(newTipPosition);
palm.setTrigger(triggerValue); // FIXME - we want to get rid of this idea of PalmData having a trigger
});
}
void Application::emulateMouse(Hand* hand, float click, float shift, int index) {
void Application::emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand) {
auto palms = hand->getCopyOfPalms();
// Locate the palm, if it exists and is active
PalmData* palm;
bool foundHand = false;
for (size_t j = 0; j < hand->getNumPalms(); j++) {
if (hand->getPalms()[j].getSixenseID() == index) {
palm = &(hand->getPalms()[j]);
for (size_t j = 0; j < palms.size(); j++) {
if (palms[j].whichHand() == whichHand) {
palm = &(palms[j]);
foundHand = true;
break;
}
@ -4898,12 +4951,14 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index)
// Process the mouse events
QPoint pos;
unsigned int deviceID = index == 0 ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT;
// FIXME - this mouse emulation stuff needs to be reworked for new controller input plugins
unsigned int deviceID = whichHand == HandData::LeftHand ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT;
int index = (int)whichHand; // FIXME - hack attack
if (isHMDMode()) {
pos = getApplicationCompositor().getPalmClickLocation(palm);
}
else {
} else {
// Get directon relative to avatar orientation
glm::vec3 direction = glm::inverse(getMyAvatar()->getOrientation()) * palm->getFingerDirection();
@ -4912,7 +4967,7 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index)
float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2));
auto canvasSize = getCanvasSize();
// Get the pixel range over which the xAngle and yAngle are scaled
float cursorRange = canvasSize.x * InputDevice::getCursorPixelRangeMult();
float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult();
pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle);
pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle);

View file

@ -14,13 +14,15 @@
#include <functional>
#include <QApplication>
#include <QHash>
#include <QImage>
#include <QPointer>
#include <QSet>
#include <QStringList>
#include <QUndoStack>
#include <QtCore/QHash>
#include <QtCore/QPointer>
#include <QtCore/QSet>
#include <QtCore/QStringList>
#include <QtGui/QImage>
#include <QtWidgets/QApplication>
#include <QtWidgets/QUndoStack>
#include <AbstractScriptingServicesInterface.h>
#include <AbstractViewStateInterface.h>
@ -69,6 +71,10 @@ class FaceTracker;
class MainWindow;
class AssetUpload;
namespace controller {
class StateController;
}
#ifdef Q_OS_WIN
static const UINT UWM_IDENTIFY_INSTANCES =
RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME"));
@ -161,7 +167,7 @@ public:
ToolWindow* getToolWindow() { return _toolWindow ; }
virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; }
virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine);
QImage renderAvatarBillboard(RenderArgs* renderArgs);
@ -287,8 +293,9 @@ public slots:
void aboutApp();
void showEditEntitiesHelp();
void cycleCamera();
void cameraMenuChanged();
void reloadResourceCaches();
void crashApplication();
@ -350,8 +357,8 @@ private:
void update(float deltaTime);
void setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue);
void emulateMouse(Hand* hand, float click, float shift, int index);
void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue);
void emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand);
// Various helper functions called during update()
void updateLOD();
@ -440,7 +447,8 @@ private:
OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers
KeyboardMouseDevice* _keyboardMouseDevice{ nullptr }; // Default input device, the good old keyboard mouse and maybe touchpad
std::shared_ptr<controller::StateController> _applicationStateDevice; // Default ApplicationDevice reflecting the state of different properties of the session
std::shared_ptr<KeyboardMouseDevice> _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
AvatarUpdate* _avatarUpdate {nullptr};
SimpleMovingAverage _avatarSimsPerSecond {10};
int _avatarSimsPerSecondReport {0};
@ -474,8 +482,7 @@ private:
NodeToJurisdictionMap _entityServerJurisdictions;
NodeToOctreeSceneStats _octreeServerSceneStats;
ControllerScriptingInterface _controllerScriptingInterface;
ControllerScriptingInterface* _controllerScriptingInterface{ nullptr };
QPointer<LogDialog> _logDialog;
QPointer<SnapshotShareDialog> _snapshotShareDialog;
@ -521,10 +528,13 @@ private:
ApplicationCompositor _compositor;
OverlayConductor _overlayConductor;
int _oldHandMouseX[2];
int _oldHandMouseY[2];
bool _oldHandLeftClick[2];
bool _oldHandRightClick[2];
// FIXME - Hand Controller to mouse emulation helpers. This is crufty and should be moved
// into the input plugins or something.
int _oldHandMouseX[(int)HandData::NUMBER_OF_HANDS];
int _oldHandMouseY[(int)HandData::NUMBER_OF_HANDS];
bool _oldHandLeftClick[(int)HandData::NUMBER_OF_HANDS];
bool _oldHandRightClick[(int)HandData::NUMBER_OF_HANDS];
DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();

View file

@ -464,11 +464,13 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true,
avatar, SLOT(setEnableMeshVisible(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
#if 0
addCheckableActionToQMenuAndActionHash(avatarDebugMenu,
MenuOption::Connexion,
0, false,
&ConnexionClient::getInstance(),
SLOT(toggleConnexion(bool)));
#endif
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");

View file

@ -13,7 +13,6 @@
#include "ui/DialogsManager.h"
PluginContainerProxy::PluginContainerProxy() {
Plugin::setContainer(this);
}
PluginContainerProxy::~PluginContainerProxy() {

View file

@ -1177,22 +1177,6 @@ glm::vec3 Avatar::getLeftPalmPosition() {
return leftHandPosition;
}
glm::vec3 Avatar::getLeftPalmVelocity() {
const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX);
if (palm != NULL) {
return palm->getVelocity();
}
return glm::vec3(0.0f);
}
glm::vec3 Avatar::getLeftPalmAngularVelocity() {
const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX);
if (palm != NULL) {
return palm->getRawAngularVelocity();
}
return glm::vec3(0.0f);
}
glm::quat Avatar::getLeftPalmRotation() {
glm::quat leftRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
@ -1208,22 +1192,6 @@ glm::vec3 Avatar::getRightPalmPosition() {
return rightHandPosition;
}
glm::vec3 Avatar::getRightPalmVelocity() {
const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX);
if (palm != NULL) {
return palm->getVelocity();
}
return glm::vec3(0.0f);
}
glm::vec3 Avatar::getRightPalmAngularVelocity() {
const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX);
if (palm != NULL) {
return palm->getRawAngularVelocity();
}
return glm::vec3(0.0f);
}
glm::quat Avatar::getRightPalmRotation() {
glm::quat rightRotation;
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);

View file

@ -43,21 +43,6 @@ static const float BILLBOARD_DISTANCE = 5.56f; // meters
extern const float CHAT_MESSAGE_SCALE;
extern const float CHAT_MESSAGE_HEIGHT;
enum DriveKeys {
FWD = 0,
BACK,
LEFT,
RIGHT,
UP,
DOWN,
ROT_LEFT,
ROT_RIGHT,
ROT_UP,
ROT_DOWN,
BOOM_IN,
BOOM_OUT,
MAX_DRIVE_KEYS
};
enum ScreenTintLayer {
SCREEN_TINT_BEFORE_LANDSCAPE = 0,
@ -175,13 +160,11 @@ public:
AvatarMotionState* getMotionState() { return _motionState; }
public slots:
// FIXME - these should be migrated to use Pose data instead
glm::vec3 getLeftPalmPosition();
glm::vec3 getLeftPalmVelocity();
glm::vec3 getLeftPalmAngularVelocity();
glm::quat getLeftPalmRotation();
glm::vec3 getRightPalmPosition();
glm::vec3 getRightPalmVelocity();
glm::vec3 getRightPalmAngularVelocity();
glm::quat getRightPalmRotation();
protected:

View file

@ -119,6 +119,8 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) {
worldTrans.setRotation(glmToBullet(_rotationalTarget));
rigidBody->setWorldTransform(worldTrans);
motionState->dirtyInternalKinematicChanges();
_previousPositionalTarget = _positionalTarget;
_previousRotationalTarget = _rotationalTarget;
_previousSet = true;
@ -224,6 +226,8 @@ QVariantMap AvatarActionHold::getArguments() {
arguments["relativeRotation"] = glmToQMap(_relativeRotation);
arguments["timeScale"] = _linearTimeScale;
arguments["hand"] = _hand;
arguments["kinematic"] = _kinematic;
arguments["kinematicSetVelocity"] = _kinematicSetVelocity;
});
return arguments;
}

View file

@ -248,6 +248,16 @@ QVector<AvatarManager::LocalLight> AvatarManager::getLocalLights() const {
return _localLights;
}
QVector<QUuid> AvatarManager::getAvatarIdentifiers() {
QReadLocker locker(&_hashLock);
return _avatarHash.keys().toVector();
}
AvatarData* AvatarManager::getAvatar(QUuid avatarID) {
QReadLocker locker(&_hashLock);
return _avatarHash[avatarID].get(); // Non-obvious: A bogus avatarID answers your own avatar.
}
void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) {
result.clear();
result.swap(_motionStatesToDelete);
@ -356,5 +366,10 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID)
return std::static_pointer_cast<Avatar>(_myAvatar);
}
QReadLocker locker(&_hashLock);
return _avatarHash[sessionID];
auto iter = _avatarHash.find(sessionID);
if (iter != _avatarHash.end()) {
return iter.value();
} else {
return AvatarSharedPointer();
}
}

View file

@ -52,6 +52,10 @@ public:
Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;
// Currently, your own avatar will be included as the null avatar id.
Q_INVOKABLE QVector<QUuid> getAvatarIdentifiers();
Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID);
void getObjectsToDelete(VectorOfMotionStates& motionStates);
void getObjectsToAdd(VectorOfMotionStates& motionStates);

View file

@ -31,13 +31,7 @@ Hand::Hand(Avatar* owningAvatar) :
}
void Hand::simulate(float deltaTime, bool isMine) {
if (isMine) {
// Iterate hand controllers, take actions as needed
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
palm.setLastControllerButtons(palm.getControllerButtons());
}
}
// nothing to do here
}
void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
@ -53,10 +47,11 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
const glm::vec3 grayColor(0.5f);
const float SPHERE_RADIUS = 0.03f * avatarScale;
auto palms = getCopyOfPalms();
gpu::Batch& batch = *renderArgs->_batch;
if (isMine) {
for (size_t i = 0; i < getNumPalms(); i++) {
PalmData& palm = getPalms()[i];
for (const auto& palm : palms) {
if (!palm.isActive()) {
continue;
}
@ -82,8 +77,7 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS;
// Draw the coordinate frames of the hand targets
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
for (const auto& palm : palms) {
if (palm.isActive()) {
glm::vec3 root = palm.getPosition();

View file

@ -53,6 +53,7 @@
using namespace std;
static quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second
const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
const float YAW_SPEED = 150.0f; // degrees/sec
const float PITCH_SPEED = 100.0f; // degrees/sec
@ -246,8 +247,31 @@ void MyAvatar::simulate(float deltaTime) {
{
PerformanceTimer perfTimer("transform");
bool stepAction = false;
// When there are no step values, we zero out the last step pulse.
// This allows a user to do faster snapping by tapping a control
for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) {
if (_driveKeys[i] != 0.0f) {
stepAction = true;
}
}
quint64 now = usecTimestampNow();
quint64 pulseDeltaTime = now - _lastStepPulse;
if (!stepAction) {
_lastStepPulse = 0;
}
if (stepAction && pulseDeltaTime > COMFORT_MODE_PULSE_TIMING) {
_pulseUpdate = true;
}
updateOrientation(deltaTime);
updatePosition(deltaTime);
if (_pulseUpdate) {
_lastStepPulse = now;
_pulseUpdate = false;
}
}
{
@ -531,6 +555,50 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
}
glm::vec3 MyAvatar::getLeftHandPosition() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f);
}
glm::vec3 MyAvatar::getRightHandPosition() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f);
}
glm::vec3 MyAvatar::getLeftHandTipPosition() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f);
}
glm::vec3 MyAvatar::getRightHandTipPosition() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f);
}
controller::Pose MyAvatar::getLeftHandPose() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
}
controller::Pose MyAvatar::getRightHandPose() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
}
controller::Pose MyAvatar::getLeftHandTipPose() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
}
controller::Pose MyAvatar::getRightHandTipPose() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
}
// virtual
void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
// don't render if we've been asked to disable local rendering
@ -1533,69 +1601,44 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
void MyAvatar::updateOrientation(float deltaTime) {
// Smoothly rotate body with arrow keys
float targetSpeed = 0.0f;
// FIXME - this comfort mode code is a total hack, remove it when we have new input mapping
bool isComfortMode = Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode);
bool isHMDMode = qApp->getAvatarUpdater()->isHMDMode();
if (!isHMDMode || !isComfortMode) {
targetSpeed = (_driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]) * YAW_SPEED;
if (targetSpeed != 0.0f) {
const float ROTATION_RAMP_TIMESCALE = 0.1f;
float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
if (blend > 1.0f) {
blend = 1.0f;
}
_bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed;
} else if (_bodyYawDelta != 0.0f) {
// attenuate body rotation speed
const float ROTATION_DECAY_TIMESCALE = 0.05f;
float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE;
if (attenuation < 0.0f) {
attenuation = 0.0f;
}
_bodyYawDelta *= attenuation;
float MINIMUM_ROTATION_RATE = 2.0f;
if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) {
_bodyYawDelta = 0.0f;
}
float targetSpeed = _driveKeys[YAW] * YAW_SPEED;
if (targetSpeed != 0.0f) {
const float ROTATION_RAMP_TIMESCALE = 0.1f;
float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
if (blend > 1.0f) {
blend = 1.0f;
}
_bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed;
} else if (_bodyYawDelta != 0.0f) {
// attenuate body rotation speed
const float ROTATION_DECAY_TIMESCALE = 0.05f;
float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE;
if (attenuation < 0.0f) {
attenuation = 0.0f;
}
_bodyYawDelta *= attenuation;
// update body orientation by movement inputs
setOrientation(getOrientation() *
glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f))));
} else {
// Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll
// get an instantaneous 15 degree turn. If you keep holding the key down you'll get another
// snap turn every half second.
_bodyYawDelta = 0.0f;
static quint64 lastPulse = 0;
quint64 now = usecTimestampNow();
quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second
float driveLeft = _driveKeys[ROT_LEFT];
float driveRight= _driveKeys[ROT_RIGHT];
if ((driveLeft != 0.0f || driveRight != 0.0f) && (now - lastPulse > COMFORT_MODE_PULSE_TIMING)) {
lastPulse = now;
const float SNAP_TURN_DELTA = 15.0f; // degrees
float direction = (driveLeft - driveRight) < 0.0f ? -1.0f : 1.0f;
float turnAmount = direction * SNAP_TURN_DELTA;
// update body orientation by movement inputs
setOrientation(getOrientation() *
glm::quat(glm::radians(glm::vec3(0.0f, turnAmount, 0.0f))));
float MINIMUM_ROTATION_RATE = 2.0f;
if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) {
_bodyYawDelta = 0.0f;
}
}
getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime);
float totalBodyYaw = _bodyYawDelta * deltaTime;
// Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll
// get an instantaneous 15 degree turn. If you keep holding the key down you'll get another
// snap turn every half second.
quint64 now = usecTimestampNow();
if (_driveKeys[STEP_YAW] != 0.0f && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) {
totalBodyYaw += _driveKeys[STEP_YAW];
}
// update body orientation by movement inputs
setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));
getHead()->setBasePitch(getHead()->getBasePitch() + _driveKeys[PITCH] * PITCH_SPEED * deltaTime);
if (qApp->getAvatarUpdater()->isHMDMode()) {
glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation();
@ -1655,14 +1698,18 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);
glm::vec3 newLocalVelocity = localVelocity;
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
(fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) +
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
float stepControllerInput = fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]);
quint64 now = usecTimestampNow();
// FIXME how do I implement step translation as well?
if (stepControllerInput && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) {
}
float keyboardInput = fabsf(_driveKeys[TRANSLATE_Z]) + fabsf(_driveKeys[TRANSLATE_X]) + fabsf(_driveKeys[TRANSLATE_Y]);
if (keyboardInput) {
// Compute keyboard input
glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT;
glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT;
glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP;
glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT;
glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT;
glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP;
glm::vec3 direction = front + right + up;
float directionLength = glm::length(direction);
@ -1713,7 +1760,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
}
}
float boomChange = _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN];
float boomChange = _driveKeys[ZOOM];
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
@ -1915,28 +1962,6 @@ void MyAvatar::updateMotionBehaviorFromMenu() {
_characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController));
}
//Renders sixense laser pointers for UI selection with controllers
void MyAvatar::renderLaserPointers(gpu::Batch& batch) {
const float PALM_TIP_ROD_RADIUS = 0.002f;
//If the Oculus is enabled, we will draw a blue cursor ray
for (size_t i = 0; i < getHand()->getNumPalms(); ++i) {
PalmData& palm = getHand()->getPalms()[i];
if (palm.isActive()) {
glm::vec3 tip = getLaserPointerTipPosition(&palm);
glm::vec3 root = palm.getPosition();
//Scale the root vector with the avatar scale
scaleVectorRelativeToPosition(root);
Transform transform = Transform();
transform.setTranslation(glm::vec3());
batch.setModelTransform(transform);
Avatar::renderJointConnectingCone(batch, root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS, glm::vec4(0, 1, 1, 1));
}
}
}
//Gets the tip position for the laser pointer
glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {
glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition());
@ -1962,7 +1987,7 @@ void MyAvatar::clearDriveKeys() {
}
void MyAvatar::relayDriveKeysToCharacterController() {
if (_driveKeys[UP] > 0.0f) {
if (_driveKeys[TRANSLATE_Y] > 0.0f) {
_characterController.jump();
}
}

View file

@ -17,6 +17,8 @@
#include <SettingHandle.h>
#include <Rig.h>
#include <controllers/Pose.h>
#include "Avatar.h"
#include "AtRestDetector.h"
#include "MyCharacterController.h"
@ -24,6 +26,20 @@
class ModelItemID;
enum DriveKeys {
TRANSLATE_X = 0,
TRANSLATE_Y,
TRANSLATE_Z,
YAW,
STEP_TRANSLATE_X,
STEP_TRANSLATE_Y,
STEP_TRANSLATE_Z,
STEP_YAW,
PITCH,
ZOOM,
MAX_DRIVE_KEYS
};
enum eyeContactTarget {
LEFT_EYE,
RIGHT_EYE,
@ -37,7 +53,6 @@ enum AudioListenerMode {
};
Q_DECLARE_METATYPE(AudioListenerMode);
class MyAvatar : public Avatar {
Q_OBJECT
Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
@ -53,6 +68,17 @@ class MyAvatar : public Avatar {
Q_PROPERTY(AudioListenerMode CUSTOM READ getAudioListenerModeCustom)
//TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition)
Q_PROPERTY(glm::vec3 rightHandPosition READ getRightHandPosition)
Q_PROPERTY(glm::vec3 leftHandTipPosition READ getLeftHandTipPosition)
Q_PROPERTY(glm::vec3 rightHandTipPosition READ getRightHandTipPosition)
Q_PROPERTY(controller::Pose leftHandPose READ getLeftHandPose)
Q_PROPERTY(controller::Pose rightHandPose READ getRightHandPose)
Q_PROPERTY(controller::Pose leftHandTipPose READ getLeftHandTipPose)
Q_PROPERTY(controller::Pose rightHandTipPose READ getRightHandTipPose)
public:
MyAvatar(RigPointer rig);
~MyAvatar();
@ -111,6 +137,18 @@ public:
Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role);
Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url);
void clearJointAnimationPriorities();
// Adds handler(animStateDictionaryIn) => animStateDictionaryOut, which will be invoked just before each animGraph state update.
// The handler will be called with an animStateDictionaryIn that has all those properties specified by the (possibly empty)
// propertiesList argument. However for debugging, if the properties argument is null, all internal animGraph state is provided.
// The animStateDictionaryOut can be a different object than animStateDictionaryIn. Any properties set in animStateDictionaryOut
// will override those of the internal animation machinery.
// The animStateDictionaryIn may be shared among multiple handlers, and thus may contain additional properties specified when
// adding one of the other handlers. While any handler may change a value in animStateDictionaryIn (or supply different values in animStateDictionaryOut)
// a handler must not remove properties from animStateDictionaryIn, nor change property values that it does not intend to change.
// It is not specified in what order multiple handlers are called.
Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _rig->addAnimationStateHandler(handler, propertiesList); }
// Removes a handler previously added by addAnimationStateHandler.
Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _rig->removeAnimationStateHandler(handler); }
// get/set avatar data
void saveData();
@ -141,6 +179,16 @@ public:
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
Q_INVOKABLE glm::vec3 getLeftHandPosition() const;
Q_INVOKABLE glm::vec3 getRightHandPosition() const;
Q_INVOKABLE glm::vec3 getLeftHandTipPosition() const;
Q_INVOKABLE glm::vec3 getRightHandTipPosition() const;
Q_INVOKABLE controller::Pose getLeftHandPose() const;
Q_INVOKABLE controller::Pose getRightHandPose() const;
Q_INVOKABLE controller::Pose getLeftHandTipPose() const;
Q_INVOKABLE controller::Pose getRightHandTipPose() const;
AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
void updateLookAtTargetAvatar();
void clearLookAtTargetAvatar();
@ -265,7 +313,6 @@ private:
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
bool allowDuplicates = false, bool useSaved = true) override;
void renderLaserPointers(gpu::Batch& batch);
const RecorderPointer getRecorder() const { return _recorder; }
const PlayerPointer getPlayer() const { return _player; }
@ -281,6 +328,8 @@ private:
void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity);
PalmData getActivePalmData(int palmIndex) const;
// derive avatar body position and orientation from the current HMD Sensor location.
// results are in sensor space
glm::mat4 deriveBodyFromHMDSensor() const;
@ -370,6 +419,8 @@ private:
AtRestDetector _hmdAtRestDetector;
glm::vec3 _lastPosition;
bool _lastIsMoving { false };
quint64 _lastStepPulse { 0 };
bool _pulseUpdate { false };
};
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);

View file

@ -97,23 +97,9 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
emit skeletonLoaded();
}
static const PalmData* getPalmWithIndex(Hand* hand, int index) {
const PalmData* palm = nullptr;
for (size_t j = 0; j < hand->getNumPalms(); j++) {
if (hand->getPalms()[j].getSixenseID() == index) {
palm = &(hand->getPalms()[j]);
break;
}
}
return palm;
}
const float PALM_PRIORITY = DEFAULT_PRIORITY;
// Called within Model::simulate call, below.
void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
if (_owningAvatar->isMyAvatar()) {
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
}
Head* head = _owningAvatar->getHead();
if (_owningAvatar->isMyAvatar()) {
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
@ -160,28 +146,30 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
_rig->updateFromHeadParameters(headParams, deltaTime);
Rig::HandParameters handParams;
const PalmData* leftPalm = getPalmWithIndex(myAvatar->getHand(), LEFT_HAND_INDEX);
if (leftPalm && leftPalm->isActive()) {
auto leftPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand);
if (leftPalm.isValid() && leftPalm.isActive()) {
handParams.isLeftEnabled = true;
handParams.leftPosition = leftPalm->getRawPosition();
handParams.leftOrientation = leftPalm->getRawRotation();
handParams.leftTrigger = leftPalm->getTrigger();
handParams.leftPosition = leftPalm.getRawPosition();
handParams.leftOrientation = leftPalm.getRawRotation();
handParams.leftTrigger = leftPalm.getTrigger();
} else {
handParams.isLeftEnabled = false;
}
const PalmData* rightPalm = getPalmWithIndex(myAvatar->getHand(), RIGHT_HAND_INDEX);
if (rightPalm && rightPalm->isActive()) {
auto rightPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::RightHand);
if (rightPalm.isValid() && rightPalm.isActive()) {
handParams.isRightEnabled = true;
handParams.rightPosition = rightPalm->getRawPosition();
handParams.rightOrientation = rightPalm->getRawRotation();
handParams.rightTrigger = rightPalm->getTrigger();
handParams.rightPosition = rightPalm.getRawPosition();
handParams.rightOrientation = rightPalm.getRawRotation();
handParams.rightTrigger = rightPalm.getTrigger();
} else {
handParams.isRightEnabled = false;
}
_rig->updateFromHandParameters(handParams, deltaTime);
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
// evaluate AnimGraph animation and update jointStates.
Model::updateRig(deltaTime, parentTransform);
@ -196,7 +184,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
_rig->updateFromEyeParameters(eyeParams);
// rebuild the jointState transform for the eyes only
// rebuild the jointState transform for the eyes only. Must be after updateRig.
_rig->updateJointState(eyeParams.leftEyeJointIndex, parentTransform);
_rig->updateJointState(eyeParams.rightEyeJointIndex, parentTransform);
@ -266,15 +254,15 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
// find the left and rightmost active palms
int leftPalmIndex, rightPalmIndex;
Hand* hand = _owningAvatar->getHand();
hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
// Don't Relax toward hand positions when in animGraph mode.
if (!_rig->getEnableAnimGraph()) {
Hand* hand = _owningAvatar->getHand();
auto leftPalm = hand->getCopyOfPalmData(HandData::LeftHand);
auto rightPalm = hand->getCopyOfPalmData(HandData::RightHand);
const float HAND_RESTORATION_RATE = 0.25f;
if (leftPalmIndex == -1 && rightPalmIndex == -1) {
if (!leftPalm.isActive() && !rightPalm.isActive()) {
// palms are not yet set, use mouse
if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
@ -284,20 +272,14 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
applyHandPosition(geometry.rightHandJointIndex, handPosition);
}
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
} else if (leftPalmIndex == rightPalmIndex) {
// right hand only
applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]);
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
} else {
if (leftPalmIndex != -1) {
applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]);
if (leftPalm.isActive()) {
applyPalmData(geometry.leftHandJointIndex, leftPalm);
} else {
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
}
if (rightPalmIndex != -1) {
applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]);
if (rightPalm.isActive()) {
applyPalmData(geometry.rightHandJointIndex, rightPalm);
} else {
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
}
@ -348,7 +330,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
PALM_PRIORITY);
}
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) {
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
return;
}

View file

@ -118,7 +118,7 @@ protected:
/// \param position position of joint in model-frame
void applyHandPosition(int jointIndex, const glm::vec3& position);
void applyPalmData(int jointIndex, PalmData& palm);
void applyPalmData(int jointIndex, const PalmData& palm);
private:
void renderJointConstraints(gpu::Batch& batch, int jointIndex);

View file

@ -9,9 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "3DConnexionClient.h"
#if 0
#include <UserActivityLogger.h>
#include <PathUtils.h>
#include "Menu.h"
#include "UserActivityLogger.h"
const float MAX_AXIS = 75.0f; // max forward = 2x speed
@ -25,22 +30,19 @@ ConnexionData& ConnexionData::getInstance() {
return sharedInstance;
}
ConnexionData::ConnexionData() {
}
ConnexionData::ConnexionData() : InputDevice("ConnexionClient") {}
void ConnexionData::handleAxisEvent() {
_axisStateMap[makeInput(ROTATION_AXIS_Y_POS).getChannel()] = (cc_rotation.y > 0.0f) ? cc_rotation.y / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(ROTATION_AXIS_Y_NEG).getChannel()] = (cc_rotation.y < 0.0f) ? -cc_rotation.y / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(POSITION_AXIS_X_POS).getChannel()] = (cc_position.x > 0.0f) ? cc_position.x / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(POSITION_AXIS_X_NEG).getChannel()] = (cc_position.x < 0.0f) ? -cc_position.x / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(POSITION_AXIS_Y_POS).getChannel()] = (cc_position.y > 0.0f) ? cc_position.y / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(POSITION_AXIS_Y_NEG).getChannel()] = (cc_position.y < 0.0f) ? -cc_position.y / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(POSITION_AXIS_Z_POS).getChannel()] = (cc_position.z > 0.0f) ? cc_position.z / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(POSITION_AXIS_Z_NEG).getChannel()] = (cc_position.z < 0.0f) ? -cc_position.z / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(ROTATION_AXIS_X_POS).getChannel()] = (cc_rotation.x > 0.0f) ? cc_rotation.x / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(ROTATION_AXIS_X_NEG).getChannel()] = (cc_rotation.x < 0.0f) ? -cc_rotation.x / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(ROTATION_AXIS_Z_POS).getChannel()] = (cc_rotation.z > 0.0f) ? cc_rotation.z / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(ROTATION_AXIS_Z_NEG).getChannel()] = (cc_rotation.z < 0.0f) ? -cc_rotation.z / MAX_AXIS : 0.0f;
auto rotation = cc_rotation / MAX_AXIS;
_axisStateMap[ROTATE_X] = rotation.x;
_axisStateMap[ROTATE_Y] = rotation.y;
_axisStateMap[ROTATE_Z] = rotation.z;
auto position = cc_rotation / MAX_AXIS;
_axisStateMap[TRANSLATE_X] = position.x;
_axisStateMap[TRANSLATE_Y] = position.y;
_axisStateMap[TRANSLATE_Z] = position.z;
}
void ConnexionData::setButton(int lastButtonState) {
@ -48,76 +50,65 @@ void ConnexionData::setButton(int lastButtonState) {
_buttonPressedMap.insert(lastButtonState);
}
void ConnexionData::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getFreeDeviceID();
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("ConnexionClient"));
proxy->getButton = [this](const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this](const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
proxy->getAvailabeInputs = [this]() -> QVector<UserInputMapper::InputPair> {
QVector<UserInputMapper::InputPair> availableInputs;
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1), "Left button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2), "Right button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3), "Both buttons"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_NEG), "Move backward"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_POS), "Move forward"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_POS), "Move right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_NEG), "Move Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_POS), "Move up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_NEG), "Move down"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_NEG), "Rotate backward"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_POS), "Rotate forward"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_POS), "Rotate right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_NEG), "Rotate left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_POS), "Rotate up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_NEG), "Rotate down"));
void ConnexionData::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) {
proxy->_name = _name = "ConnexionClient";
proxy->getButton = [this](const controller::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
proxy->getAxis = [this](const controller::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
proxy->getAvailabeInputs = [this]() -> QVector<controller::Input::NamedPair> {
using namespace controller;
static QVector<controller::Input::NamedPair> availableInputs {
Input::NamedPair(makeInput(BUTTON_1), "LeftButton"),
Input::NamedPair(makeInput(BUTTON_2), "RightButton"),
Input::NamedPair(makeInput(BUTTON_3), "BothButtons"),
Input::NamedPair(makeInput(TRANSLATE_X), "TranslateX"),
Input::NamedPair(makeInput(TRANSLATE_Y), "TranslateY"),
Input::NamedPair(makeInput(TRANSLATE_Z), "TranslateZ"),
Input::NamedPair(makeInput(ROTATE_X), "RotateX"),
Input::NamedPair(makeInput(ROTATE_Y), "RotateY"),
Input::NamedPair(makeInput(ROTATE_Z), "RotateZ"),
};
return availableInputs;
};
proxy->resetDeviceBindings = [this, &mapper]() -> bool {
mapper.removeAllInputChannelsForDevice(_deviceID);
this->assignDefaultInputMapping(mapper);
return true;
};
mapper.registerDevice(_deviceID, proxy);
}
void ConnexionData::assignDefaultInputMapping(UserInputMapper& mapper) {
const float JOYSTICK_MOVE_SPEED = 1.0f;
//const float DPAD_MOVE_SPEED = 0.5f;
const float JOYSTICK_YAW_SPEED = 0.5f;
const float JOYSTICK_PITCH_SPEED = 0.25f;
const float BOOM_SPEED = 0.1f;
// Y axes are flipped (up is negative)
// postion: Movement, strafing
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(POSITION_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(POSITION_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(POSITION_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(POSITION_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(POSITION_AXIS_Z_NEG), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(POSITION_AXIS_Z_POS), JOYSTICK_MOVE_SPEED);
// Rotation: Camera orientation with button 1
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(ROTATION_AXIS_Z_POS), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(ROTATION_AXIS_Z_NEG), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(ROTATION_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(ROTATION_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
// Button controls
// Zoom
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_1), BOOM_SPEED);
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_2), BOOM_SPEED);
// Zoom
// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(ROTATION_AXIS_Z_NEG), BOOM_SPEED);
// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(ROTATION_AXIS_Z_POS), BOOM_SPEED);
QString ConnexionData::getDefaultMappingConfig() {
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json";
return MAPPING_JSON;
}
//void ConnexionData::assignDefaultInputMapping(UserInputMapper& mapper) {
// const float JOYSTICK_MOVE_SPEED = 1.0f;
// //const float DPAD_MOVE_SPEED = 0.5f;
// const float JOYSTICK_YAW_SPEED = 0.5f;
// const float JOYSTICK_PITCH_SPEED = 0.25f;
// const float BOOM_SPEED = 0.1f;
//
// // Y axes are flipped (up is negative)
// // postion: Movement, strafing
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(POSITION_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(POSITION_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(POSITION_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(POSITION_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(POSITION_AXIS_Z_NEG), JOYSTICK_MOVE_SPEED);
// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(POSITION_AXIS_Z_POS), JOYSTICK_MOVE_SPEED);
//
// // Rotation: Camera orientation with button 1
// mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(ROTATION_AXIS_Z_POS), JOYSTICK_YAW_SPEED);
// mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(ROTATION_AXIS_Z_NEG), JOYSTICK_YAW_SPEED);
// mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(ROTATION_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
// mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(ROTATION_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
//
// // Button controls
// // Zoom
// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_1), BOOM_SPEED);
// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_2), BOOM_SPEED);
//
// // Zoom
// // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(ROTATION_AXIS_Z_NEG), BOOM_SPEED);
// // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(ROTATION_AXIS_Z_POS), BOOM_SPEED);
//
//}
float ConnexionData::getButton(int channel) const {
if (!_buttonPressedMap.empty()) {
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
@ -138,15 +129,15 @@ float ConnexionData::getAxis(int channel) const {
}
}
UserInputMapper::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) {
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
controller::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) {
return controller::Input(_deviceID, button, controller::ChannelType::BUTTON);
}
UserInputMapper::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) {
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
controller::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) {
return controller::Input(_deviceID, axis, controller::ChannelType::AXIS);
}
void ConnexionData::update() {
void ConnexionData::update(float deltaTime, bool jointsCaptured) {
// the update is done in the ConnexionClient class.
// for windows in the nativeEventFilter the inputmapper is connected or registed or removed when an 3Dconnnexion device is attached or detached
// for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached
@ -187,7 +178,6 @@ void ConnexionClient::destroy() {
QAbstractEventDispatcher::instance()->removeNativeEventFilter(this);
ConnexionData& connexiondata = ConnexionData::getInstance();
int deviceid = connexiondata.getDeviceID();
connexiondata.setDeviceID(0);
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->removeDevice(deviceid);
}
@ -295,13 +285,10 @@ bool ConnexionClient::RawInputEventFilter(void* msg, long* result) {
ConnexionData& connexiondata = ConnexionData::getInstance();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) {
connexiondata.registerToUserInputMapper(*userInputMapper);
connexiondata.assignDefaultInputMapping(*userInputMapper);
userInputMapper->registerDevice(&connexiondata);
UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
} else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) {
int deviceid = connexiondata.getDeviceID();
connexiondata.setDeviceID(0);
userInputMapper->removeDevice(deviceid);
userInputMapper->removeDevice(connexiondata.getDeviceID());
}
if (!Is3dmouseAttached()) {
@ -894,8 +881,7 @@ void ConnexionClient::init() {
if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) {
auto userInputMapper = DependencyManager::get<UserInputMapper>();
connexiondata.registerToUserInputMapper(*userInputMapper);
connexiondata.assignDefaultInputMapping(*userInputMapper);
userInputMapper->registerDevice(&connexiondata);
UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
}
//let one axis be dominant
@ -926,8 +912,7 @@ void DeviceAddedHandler(unsigned int connection) {
if (connexiondata.getDeviceID() == 0) {
qCWarning(interfaceapp) << "3Dconnexion device added ";
auto userInputMapper = DependencyManager::get<UserInputMapper>();
connexiondata.registerToUserInputMapper(*userInputMapper);
connexiondata.assignDefaultInputMapping(*userInputMapper);
userInputMapper->registerDevice(&connexiondata);
UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
}
}
@ -984,3 +969,4 @@ void MessageHandler(unsigned int connection, unsigned int messageType, void *mes
#endif // __APPLE__
#endif // HAVE_3DCONNEXIONCLIENT
#endif

View file

@ -11,9 +11,10 @@
#ifndef hifi_3DConnexionClient_h
#define hifi_3DConnexionClient_h
#if 0
#include <QObject>
#include <QLibrary>
#include <input-plugins/UserInputMapper.h>
#include <controllers/UserInputMapper.h>
#include "InterfaceLogging.h"
@ -175,26 +176,19 @@ public slots:
// connnects to the userinputmapper
class ConnexionData : public QObject {
class ConnexionData : public QObject, public controller::InputDevice {
Q_OBJECT
public:
static ConnexionData& getInstance();
ConnexionData();
enum PositionChannel {
POSITION_AXIS_X_POS = 1,
POSITION_AXIS_X_NEG = 2,
POSITION_AXIS_Y_POS = 3,
POSITION_AXIS_Y_NEG = 4,
POSITION_AXIS_Z_POS = 5,
POSITION_AXIS_Z_NEG = 6,
ROTATION_AXIS_X_POS = 7,
ROTATION_AXIS_X_NEG = 8,
ROTATION_AXIS_Y_POS = 9,
ROTATION_AXIS_Y_NEG = 10,
ROTATION_AXIS_Z_POS = 11,
ROTATION_AXIS_Z_NEG = 12
TRANSLATE_X,
TRANSLATE_Y,
TRANSLATE_Z,
ROTATE_X,
ROTATE_Y,
ROTATE_Z,
};
enum ButtonChannel {
@ -209,19 +203,12 @@ public:
float getButton(int channel) const;
float getAxis(int channel) const;
UserInputMapper::Input makeInput(ConnexionData::PositionChannel axis);
UserInputMapper::Input makeInput(ConnexionData::ButtonChannel button);
void registerToUserInputMapper(UserInputMapper& mapper);
void assignDefaultInputMapping(UserInputMapper& mapper);
void update();
void focusOutEvent();
int getDeviceID() { return _deviceID; }
void setDeviceID(int deviceID) { _deviceID = deviceID; }
QString _name;
controller::Input makeInput(ConnexionData::PositionChannel axis);
controller::Input makeInput(ConnexionData::ButtonChannel button);
virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override;
virtual QString getDefaultMappingConfig() override;
virtual void update(float deltaTime, bool jointsCaptured) override;
virtual void focusOutEvent() override;
glm::vec3 cc_position;
glm::vec3 cc_rotation;
@ -229,12 +216,8 @@ public:
void setButton(int lastButtonState);
void handleAxisEvent();
protected:
int _deviceID = 0;
ButtonPressedMap _buttonPressedMap;
AxisStateMap _axisStateMap;
};
#endif
#endif // defined(hifi_3DConnexionClient_h)

View file

@ -9,120 +9,20 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ControllerScriptingInterface.h"
#include <avatar/AvatarManager.h>
#include <avatar/MyAvatar.h>
#include <HandData.h>
#include <HFBackEvent.h>
#include <plugins/PluginManager.h>
#include "Application.h"
#include "devices/MotionTracker.h"
#include "ControllerScriptingInterface.h"
// TODO: this needs to be removed, as well as any related controller-specific information
#include <input-plugins/SixenseManager.h>
ControllerScriptingInterface::ControllerScriptingInterface() :
_mouseCaptured(false),
_touchCaptured(false),
_wheelCaptured(false),
_actionsCaptured(false)
{
}
static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>();
static int inputChannelMetaTypeId = qRegisterMetaType<UserInputMapper::InputChannel>();
static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>();
static int inputPairMetaTypeId = qRegisterMetaType<UserInputMapper::InputPair>();
QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input);
void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input);
QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel);
void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel);
QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action);
void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action);
QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair);
void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair);
QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) {
QScriptValue obj = engine->newObject();
obj.setProperty("device", input.getDevice());
obj.setProperty("channel", input.getChannel());
obj.setProperty("type", (unsigned short) input.getType());
obj.setProperty("id", input.getID());
return obj;
}
void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) {
input.setDevice(object.property("device").toUInt16());
input.setChannel(object.property("channel").toUInt16());
input.setType(object.property("type").toUInt16());
input.setID(object.property("id").toInt32());
}
QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel) {
QScriptValue obj = engine->newObject();
obj.setProperty("input", inputToScriptValue(engine, inputChannel.getInput()));
obj.setProperty("modifier", inputToScriptValue(engine, inputChannel.getModifier()));
obj.setProperty("action", inputChannel.getAction());
obj.setProperty("scale", inputChannel.getScale());
return obj;
}
void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel) {
UserInputMapper::Input input;
UserInputMapper::Input modifier;
inputFromScriptValue(object.property("input"), input);
inputChannel.setInput(input);
inputFromScriptValue(object.property("modifier"), modifier);
inputChannel.setModifier(modifier);
inputChannel.setAction(UserInputMapper::Action(object.property("action").toVariant().toInt()));
inputChannel.setScale(object.property("scale").toVariant().toFloat());
}
QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) {
QScriptValue obj = engine->newObject();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
QVector<UserInputMapper::InputChannel> inputChannels = userInputMapper->getInputChannelsForAction(action);
QScriptValue _inputChannels = engine->newArray(inputChannels.size());
for (int i = 0; i < inputChannels.size(); i++) {
_inputChannels.setProperty(i, inputChannelToScriptValue(engine, inputChannels[i]));
}
obj.setProperty("action", (int) action);
obj.setProperty("actionName", userInputMapper->getActionName(action));
obj.setProperty("inputChannels", _inputChannels);
return obj;
}
void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) {
action = UserInputMapper::Action(object.property("action").toVariant().toInt());
}
QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) {
QScriptValue obj = engine->newObject();
obj.setProperty("input", inputToScriptValue(engine, inputPair.first));
obj.setProperty("inputName", inputPair.second);
return obj;
}
void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) {
inputFromScriptValue(object.property("input"), inputPair.first);
inputPair.second = QString(object.property("inputName").toVariant().toString());
}
void ControllerScriptingInterface::registerControllerTypes(ScriptEngine* engine) {
qScriptRegisterSequenceMetaType<QVector<UserInputMapper::Action> >(engine);
qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputChannel> >(engine);
qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputPair> >(engine);
qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue);
qScriptRegisterMetaType(engine, inputChannelToScriptValue, inputChannelFromScriptValue);
qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue);
qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue);
wireUpControllers(engine);
}
void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
if (event->type() == HFActionEvent::startType()) {
emit actionStartEvent(static_cast<HFActionEvent&>(*event));
@ -135,205 +35,6 @@ void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
}
}
const PalmData* ControllerScriptingInterface::getPrimaryPalm() const {
int leftPalmIndex, rightPalmIndex;
const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
handData->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
if (rightPalmIndex != -1) {
return &handData->getPalms()[rightPalmIndex];
}
return NULL;
}
int ControllerScriptingInterface::getNumberOfActivePalms() const {
const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
int numberOfPalms = handData->getNumPalms();
int numberOfActivePalms = 0;
for (int i = 0; i < numberOfPalms; i++) {
if (getPalm(i)->isActive()) {
numberOfActivePalms++;
}
}
return numberOfActivePalms;
}
const PalmData* ControllerScriptingInterface::getPalm(int palmIndex) const {
const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
return &handData->getPalms()[palmIndex];
}
const PalmData* ControllerScriptingInterface::getActivePalm(int palmIndex) const {
const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
int numberOfPalms = handData->getNumPalms();
int numberOfActivePalms = 0;
for (int i = 0; i < numberOfPalms; i++) {
if (getPalm(i)->isActive()) {
if (numberOfActivePalms == palmIndex) {
return &handData->getPalms()[i];
}
numberOfActivePalms++;
}
}
return NULL;
}
bool ControllerScriptingInterface::isPrimaryButtonPressed() const {
const PalmData* primaryPalm = getPrimaryPalm();
if (primaryPalm) {
if (primaryPalm->getControllerButtons() & BUTTON_FWD) {
return true;
}
}
return false;
}
glm::vec2 ControllerScriptingInterface::getPrimaryJoystickPosition() const {
const PalmData* primaryPalm = getPrimaryPalm();
if (primaryPalm) {
return glm::vec2(primaryPalm->getJoystickX(), primaryPalm->getJoystickY());
}
return glm::vec2(0);
}
int ControllerScriptingInterface::getNumberOfButtons() const {
return getNumberOfActivePalms() * NUMBER_OF_BUTTONS_PER_PALM;
}
bool ControllerScriptingInterface::isButtonPressed(int buttonIndex) const {
int palmIndex = buttonIndex / NUMBER_OF_BUTTONS_PER_PALM;
int buttonOnPalm = buttonIndex % NUMBER_OF_BUTTONS_PER_PALM;
const PalmData* palmData = getActivePalm(palmIndex);
if (palmData) {
switch (buttonOnPalm) {
case 0:
return palmData->getControllerButtons() & BUTTON_0;
case 1:
return palmData->getControllerButtons() & BUTTON_1;
case 2:
return palmData->getControllerButtons() & BUTTON_2;
case 3:
return palmData->getControllerButtons() & BUTTON_3;
case 4:
return palmData->getControllerButtons() & BUTTON_4;
case 5:
return palmData->getControllerButtons() & BUTTON_FWD;
}
}
return false;
}
int ControllerScriptingInterface::getNumberOfTriggers() const {
return getNumberOfActivePalms() * NUMBER_OF_TRIGGERS_PER_PALM;
}
float ControllerScriptingInterface::getTriggerValue(int triggerIndex) const {
// we know there's one trigger per palm, so the triggerIndex is the palm Index
int palmIndex = triggerIndex;
const PalmData* palmData = getActivePalm(palmIndex);
if (palmData) {
return palmData->getTrigger();
}
return 0.0f;
}
int ControllerScriptingInterface::getNumberOfJoysticks() const {
return getNumberOfActivePalms() * NUMBER_OF_JOYSTICKS_PER_PALM;
}
glm::vec2 ControllerScriptingInterface::getJoystickPosition(int joystickIndex) const {
// we know there's one joystick per palm, so the joystickIndex is the palm Index
int palmIndex = joystickIndex;
const PalmData* palmData = getActivePalm(palmIndex);
if (palmData) {
return glm::vec2(palmData->getJoystickX(), palmData->getJoystickY());
}
return glm::vec2(0);
}
int ControllerScriptingInterface::getNumberOfSpatialControls() const {
return getNumberOfActivePalms() * NUMBER_OF_SPATIALCONTROLS_PER_PALM;
}
glm::vec3 ControllerScriptingInterface::getSpatialControlPosition(int controlIndex) const {
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
const PalmData* palmData = getActivePalm(palmIndex);
if (palmData) {
switch (controlOfPalm) {
case PALM_SPATIALCONTROL:
return palmData->getPosition();
case TIP_SPATIALCONTROL:
return palmData->getTipPosition();
}
}
return glm::vec3(0); // bad index
}
glm::vec3 ControllerScriptingInterface::getSpatialControlVelocity(int controlIndex) const {
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
const PalmData* palmData = getActivePalm(palmIndex);
if (palmData) {
switch (controlOfPalm) {
case PALM_SPATIALCONTROL:
return palmData->getVelocity();
case TIP_SPATIALCONTROL:
return palmData->getTipVelocity();
}
}
return glm::vec3(0); // bad index
}
glm::quat ControllerScriptingInterface::getSpatialControlRawRotation(int controlIndex) const {
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
const PalmData* palmData = getActivePalm(palmIndex);
if (palmData) {
switch (controlOfPalm) {
case PALM_SPATIALCONTROL:
return palmData->getRawRotation();
case TIP_SPATIALCONTROL:
return palmData->getRawRotation(); // currently the tip doesn't have a unique rotation, use the palm rotation
}
}
return glm::quat(); // bad index
}
glm::vec3 ControllerScriptingInterface::getSpatialControlRawAngularVelocity(int controlIndex) const {
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
const PalmData* palmData = getActivePalm(palmIndex);
if (palmData) {
switch (controlOfPalm) {
case PALM_SPATIALCONTROL:
return palmData->getRawAngularVelocity();
case TIP_SPATIALCONTROL:
return palmData->getRawAngularVelocity(); // Tip = palm angular velocity
}
}
return glm::vec3(0); // bad index
}
glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex) const {
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
const PalmData* palmData = getActivePalm(palmIndex);
if (palmData) {
switch (controlOfPalm) {
case PALM_SPATIALCONTROL:
return palmData->getNormal();
case TIP_SPATIALCONTROL:
return palmData->getFingerDirection();
}
}
return glm::vec3(0); // bad index
}
bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const {
return isKeyCaptured(KeyEvent(*event));
}
@ -383,157 +84,45 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const {
return qApp->getUiSize();
}
QString ControllerScriptingInterface::sanatizeName(const QString& name) {
QString cleanName { name };
cleanName.remove(QRegularExpression{"[\\(\\)\\.\\s]"});
return cleanName;
}
void ControllerScriptingInterface::wireUpControllers(ScriptEngine* engine) {
// Controller.Standard.*
auto standardDevice = DependencyManager::get<UserInputMapper>()->getStandardDevice();
if (standardDevice) {
auto deviceName = sanatizeName(standardDevice->getName());
auto deviceInputs = standardDevice->getAvailabeInputs();
for (const auto& inputMapping : deviceInputs) {
auto input = inputMapping.first;
auto inputName = sanatizeName(inputMapping.second);
QString deviceInputName{ "Controller." + deviceName + "." + inputName };
engine->registerValue(deviceInputName, input.getID());
}
}
// Controller.Hardware.*
auto devices = DependencyManager::get<UserInputMapper>()->getDevices();
for(const auto& deviceMapping : devices) {
auto device = deviceMapping.second.get();
auto deviceName = sanatizeName(device->getName());
auto deviceInputs = device->getAvailabeInputs();
for (const auto& inputMapping : deviceInputs) {
auto input = inputMapping.first;
auto inputName = sanatizeName(inputMapping.second);
QString deviceInputName { "Controller.Hardware." + deviceName + "." + inputName };
engine->registerValue(deviceInputName, input.getID());
}
}
// Controller.Actions.*
auto actionNames = DependencyManager::get<UserInputMapper>()->getActionNames();
int actionNumber = 0;
for (const auto& actionName : actionNames) {
QString safeActionName { "Controller.Actions." + sanatizeName(actionName) };
engine->registerValue(safeActionName, actionNumber);
actionNumber++;
}
}
AbstractInputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
controller::InputController::Pointer ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
// This is where we retreive the Device Tracker category and then the sub tracker within it
//TODO C++11 auto icIt = _inputControllers.find(0);
InputControllerMap::iterator icIt = _inputControllers.find(0);
auto icIt = _inputControllers.find(0);
if (icIt != _inputControllers.end()) {
return (*icIt).second;
} else {
}
// Look for device
DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
if (deviceID < 0) {
deviceID = 0;
}
// TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion)
// in the near future we need to change that to a real mapping between the devices and the deviceName
// ALso we need to expand the spec so we can fall back on the "default" controller per categories
if (deviceID >= 0) {
// TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices
MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID));
if (motionTracker) {
MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString());
if (trackerID >= 0) {
AbstractInputController* inputController = new InputController(deviceID, trackerID, this);
// Look for device
DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
if (deviceID < 0) {
deviceID = 0;
}
// TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion)
// in the near future we need to change that to a real mapping between the devices and the deviceName
// ALso we need to expand the spec so we can fall back on the "default" controller per categories
_inputControllers.insert(InputControllerMap::value_type(inputController->getKey(), inputController));
return inputController;
}
if (deviceID >= 0) {
// TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices
MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID));
if (motionTracker) {
MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString());
if (trackerID >= 0) {
controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this);
controller::InputController::Key key = inputController->getKey();
_inputControllers.insert(InputControllerMap::value_type(key, inputController));
return inputController;
}
}
return 0;
}
return controller::InputController::Pointer();
}
void ControllerScriptingInterface::releaseInputController(AbstractInputController* input) {
void ControllerScriptingInterface::releaseInputController(controller::InputController::Pointer input) {
_inputControllers.erase(input->getKey());
}
void ControllerScriptingInterface::updateInputControllers() {
//TODO C++11 for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
for (InputControllerMap::iterator it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
(*it).second->update();
}
}
QVector<UserInputMapper::Action> ControllerScriptingInterface::getAllActions() {
return DependencyManager::get<UserInputMapper>()->getAllActions();
}
QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getInputChannelsForAction(UserInputMapper::Action action) {
return DependencyManager::get<UserInputMapper>()->getInputChannelsForAction(action);
}
QString ControllerScriptingInterface::getDeviceName(unsigned int device) {
return DependencyManager::get<UserInputMapper>()->getDeviceName((unsigned short)device);
}
QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getAllInputsForDevice(unsigned int device) {
return DependencyManager::get<UserInputMapper>()->getAllInputsForDevice(device);
}
bool ControllerScriptingInterface::addInputChannel(UserInputMapper::InputChannel inputChannel) {
return DependencyManager::get<UserInputMapper>()->addInputChannel(inputChannel._action, inputChannel._input, inputChannel._modifier, inputChannel._scale);
}
bool ControllerScriptingInterface::removeInputChannel(UserInputMapper::InputChannel inputChannel) {
return DependencyManager::get<UserInputMapper>()->removeInputChannel(inputChannel);
}
QVector<UserInputMapper::InputPair> ControllerScriptingInterface::getAvailableInputs(unsigned int device) {
return DependencyManager::get<UserInputMapper>()->getAvailableInputs((unsigned short)device);
}
void ControllerScriptingInterface::resetAllDeviceBindings() {
DependencyManager::get<UserInputMapper>()->resetAllDeviceBindings();
}
void ControllerScriptingInterface::resetDevice(unsigned int device) {
DependencyManager::get<UserInputMapper>()->resetDevice(device);
}
int ControllerScriptingInterface::findDevice(QString name) {
return DependencyManager::get<UserInputMapper>()->findDevice(name);
}
QVector<QString> ControllerScriptingInterface::getDeviceNames() {
return DependencyManager::get<UserInputMapper>()->getDeviceNames();
}
float ControllerScriptingInterface::getActionValue(int action) {
return DependencyManager::get<UserInputMapper>()->getActionState(UserInputMapper::Action(action));
}
int ControllerScriptingInterface::findAction(QString actionName) {
return DependencyManager::get<UserInputMapper>()->findAction(actionName);
}
QVector<QString> ControllerScriptingInterface::getActionNames() const {
return DependencyManager::get<UserInputMapper>()->getActionNames();
}
InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
AbstractInputController(),
_deviceTrackerId(deviceTrackerId),
_subTrackerId(subTrackerId),
_isActive(false)
@ -556,7 +145,7 @@ void InputController::update() {
joint->getLocFrame().getRotation(_eventCache.locRotation);
_isActive = true;
emit spatialEvent(_eventCache);
//emit spatialEvent(_eventCache);
}
}
}
@ -568,3 +157,19 @@ const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16;
InputController::Key InputController::getKey() const {
return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId);
}
void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
void ControllerScriptingInterface::emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
void ControllerScriptingInterface::emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); }
void ControllerScriptingInterface::emitMousePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mousePressEvent(MouseEvent(*event, deviceID)); }
void ControllerScriptingInterface::emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); }
void ControllerScriptingInterface::emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); }
void ControllerScriptingInterface::emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); }
void ControllerScriptingInterface::emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); }
void ControllerScriptingInterface::emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); }
void ControllerScriptingInterface::emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); }

Some files were not shown because too many files have changed in this diff Show more